1- import type { GitBranch } from "@t3tools/contracts" ;
2- import { useInfiniteQuery , useQueryClient } from "@tanstack/react-query" ;
1+ import type { GitBranch , NativeApi } from "@t3tools/contracts" ;
2+ import { type QueryClient , useInfiniteQuery , useQueryClient } from "@tanstack/react-query" ;
33import { useVirtualizer } from "@tanstack/react-virtual" ;
44import { ChevronDownIcon } from "lucide-react" ;
55import {
@@ -92,6 +92,83 @@ function formatDirtyWorktreeDescription(files: string[]): string {
9292 return `${ basenames . slice ( 0 , 2 ) . join ( ", " ) } and ${ basenames . length - 2 } other file${ basenames . length - 2 === 1 ? "" : "s" } have uncommitted changes. Commit or stash before switching.` ;
9393}
9494
95+ function handleCheckoutError (
96+ error : unknown ,
97+ ctx : {
98+ api : NativeApi ;
99+ cwd : string ;
100+ branch : string ;
101+ queryClient : QueryClient ;
102+ onSuccess : ( ) => void ;
103+ fallbackTitle : string ;
104+ } ,
105+ ) : void {
106+ const dirtyWorktree = parseDirtyWorktreeError ( error ) ;
107+ if ( dirtyWorktree ) {
108+ toastManager . add ( {
109+ type : "warning" ,
110+ title : "Uncommitted changes block checkout." ,
111+ description : formatDirtyWorktreeDescription ( dirtyWorktree . files ) ,
112+ actionProps : {
113+ children : "Stash & Switch" ,
114+ onClick : async ( ) => {
115+ try {
116+ await ctx . api . git . stashAndCheckout ( { cwd : ctx . cwd , branch : ctx . branch } ) ;
117+ await invalidateGitQueries ( ctx . queryClient ) ;
118+ ctx . onSuccess ( ) ;
119+ } catch ( stashError ) {
120+ if ( isStashConflictError ( stashError ) ) {
121+ toastManager . add ( {
122+ type : "warning" ,
123+ title : "Stash could not be applied." ,
124+ description : toBranchActionErrorMessage ( stashError ) ,
125+ actionProps : {
126+ children : "Discard stash" ,
127+ onClick : async ( ) => {
128+ const confirmed = await readNativeApi ( ) ?. dialogs . confirm (
129+ "Drop the most recent stash entry? This cannot be undone." ,
130+ ) ;
131+ if ( ! confirmed ) return ;
132+ try {
133+ await ctx . api . git . stashDrop ( { cwd : ctx . cwd } ) ;
134+ } catch ( dropError ) {
135+ toastManager . add ( {
136+ type : "error" ,
137+ title : "Failed to drop stash." ,
138+ description : toBranchActionErrorMessage ( dropError ) ,
139+ } ) ;
140+ }
141+ } ,
142+ } ,
143+ } ) ;
144+ } else {
145+ toastManager . add ( {
146+ type : "error" ,
147+ title : "Failed to stash and switch." ,
148+ description : toBranchActionErrorMessage ( stashError ) ,
149+ } ) ;
150+ }
151+ }
152+ } ,
153+ } ,
154+ } ) ;
155+ return ;
156+ }
157+ if ( isUnresolvedIndexError ( error ) ) {
158+ toastManager . add ( {
159+ type : "error" ,
160+ title : "Unresolved conflicts in the repository." ,
161+ description : toBranchActionErrorMessage ( error ) ,
162+ } ) ;
163+ return ;
164+ }
165+ toastManager . add ( {
166+ type : "error" ,
167+ title : ctx . fallbackTitle ,
168+ description : toBranchActionErrorMessage ( error ) ,
169+ } ) ;
170+ }
171+
95172function getBranchTriggerLabel ( input : {
96173 activeWorktreePath : string | null ;
97174 effectiveEnvMode : EnvMode ;
@@ -275,71 +352,16 @@ export function BranchToolbarBranchSelector({
275352 onSetThreadBranch ( nextBranchName , selectionTarget . nextWorktreePath ) ;
276353 } catch ( error ) {
277354 setOptimisticBranch ( previousBranch ) ;
278- const dirtyWorktree = parseDirtyWorktreeError ( error ) ;
279- if ( dirtyWorktree ) {
280- toastManager . add ( {
281- type : "warning" ,
282- title : "Uncommitted changes block checkout." ,
283- description : formatDirtyWorktreeDescription ( dirtyWorktree . files ) ,
284- actionProps : {
285- children : "Stash & Switch" ,
286- onClick : async ( ) => {
287- try {
288- await api . git . stashAndCheckout ( {
289- cwd : selectionTarget . checkoutCwd ,
290- branch : branch . name ,
291- } ) ;
292- await invalidateGitQueries ( queryClient ) ;
293- } catch ( stashError ) {
294- if ( isStashConflictError ( stashError ) ) {
295- toastManager . add ( {
296- type : "warning" ,
297- title : "Stash could not be applied." ,
298- description : toBranchActionErrorMessage ( stashError ) ,
299- actionProps : {
300- children : "Discard stash" ,
301- onClick : async ( ) => {
302- const confirmed = await readNativeApi ( ) ?. dialogs . confirm (
303- "Drop the most recent stash entry? This cannot be undone." ,
304- ) ;
305- if ( ! confirmed ) return ;
306- try {
307- await api . git . stashDrop ( { cwd : selectionTarget . checkoutCwd } ) ;
308- } catch ( dropError ) {
309- toastManager . add ( {
310- type : "error" ,
311- title : "Failed to drop stash." ,
312- description : toBranchActionErrorMessage ( dropError ) ,
313- } ) ;
314- }
315- } ,
316- } ,
317- } ) ;
318- } else {
319- toastManager . add ( {
320- type : "error" ,
321- title : "Failed to stash and switch." ,
322- description : toBranchActionErrorMessage ( stashError ) ,
323- } ) ;
324- }
325- }
326- } ,
327- } ,
328- } ) ;
329- return ;
330- }
331- if ( isUnresolvedIndexError ( error ) ) {
332- toastManager . add ( {
333- type : "error" ,
334- title : "Unresolved conflicts in the repository." ,
335- description : toBranchActionErrorMessage ( error ) ,
336- } ) ;
337- return ;
338- }
339- toastManager . add ( {
340- type : "error" ,
341- title : "Failed to checkout branch." ,
342- description : toBranchActionErrorMessage ( error ) ,
355+ handleCheckoutError ( error , {
356+ api,
357+ cwd : selectionTarget . checkoutCwd ,
358+ branch : branch . name ,
359+ queryClient,
360+ onSuccess : ( ) => {
361+ setOptimisticBranch ( selectedBranchName ) ;
362+ onSetThreadBranch ( selectedBranchName , selectionTarget . nextWorktreePath ) ;
363+ } ,
364+ fallbackTitle : "Failed to checkout branch." ,
343365 } ) ;
344366 }
345367 } ) ;
@@ -366,68 +388,16 @@ export function BranchToolbarBranchSelector({
366388 onSetThreadBranch ( createBranchResult . branch , activeWorktreePath ) ;
367389 } catch ( error ) {
368390 setOptimisticBranch ( previousBranch ) ;
369- const dirtyWorktree = parseDirtyWorktreeError ( error ) ;
370- if ( dirtyWorktree ) {
371- toastManager . add ( {
372- type : "warning" ,
373- title : "Uncommitted changes block checkout." ,
374- description : formatDirtyWorktreeDescription ( dirtyWorktree . files ) ,
375- actionProps : {
376- children : "Stash & Switch" ,
377- onClick : async ( ) => {
378- try {
379- await api . git . stashAndCheckout ( { cwd : branchCwd , branch : name } ) ;
380- await invalidateGitQueries ( queryClient ) ;
381- } catch ( stashError ) {
382- if ( isStashConflictError ( stashError ) ) {
383- toastManager . add ( {
384- type : "warning" ,
385- title : "Stash could not be applied." ,
386- description : toBranchActionErrorMessage ( stashError ) ,
387- actionProps : {
388- children : "Discard stash" ,
389- onClick : async ( ) => {
390- const confirmed = await readNativeApi ( ) ?. dialogs . confirm (
391- "Drop the most recent stash entry? This cannot be undone." ,
392- ) ;
393- if ( ! confirmed ) return ;
394- try {
395- await api . git . stashDrop ( { cwd : branchCwd } ) ;
396- } catch ( dropError ) {
397- toastManager . add ( {
398- type : "error" ,
399- title : "Failed to drop stash." ,
400- description : toBranchActionErrorMessage ( dropError ) ,
401- } ) ;
402- }
403- } ,
404- } ,
405- } ) ;
406- } else {
407- toastManager . add ( {
408- type : "error" ,
409- title : "Failed to stash and switch." ,
410- description : toBranchActionErrorMessage ( stashError ) ,
411- } ) ;
412- }
413- }
414- } ,
415- } ,
416- } ) ;
417- return ;
418- }
419- if ( isUnresolvedIndexError ( error ) ) {
420- toastManager . add ( {
421- type : "error" ,
422- title : "Unresolved conflicts in the repository." ,
423- description : toBranchActionErrorMessage ( error ) ,
424- } ) ;
425- return ;
426- }
427- toastManager . add ( {
428- type : "error" ,
429- title : "Failed to create and checkout branch." ,
430- description : toBranchActionErrorMessage ( error ) ,
391+ handleCheckoutError ( error , {
392+ api,
393+ cwd : branchCwd ,
394+ branch : name ,
395+ queryClient,
396+ onSuccess : ( ) => {
397+ setOptimisticBranch ( name ) ;
398+ onSetThreadBranch ( name , activeWorktreePath ) ;
399+ } ,
400+ fallbackTitle : "Failed to create and checkout branch." ,
431401 } ) ;
432402 }
433403 } ) ;
0 commit comments