@@ -132,22 +132,64 @@ export class CheckpointService {
132132 stashSha : string
133133 force ?: boolean
134134 } ) {
135- if ( force ) {
136- await this . git . checkout ( [ "-f" , this . mainBranch ] )
137- } else {
138- await this . git . checkout ( this . mainBranch )
135+ let currentBranch = await this . git . revparse ( [ "--abbrev-ref" , "HEAD" ] )
136+
137+ if ( currentBranch !== this . mainBranch ) {
138+ if ( force ) {
139+ try {
140+ await this . git . checkout ( [ "-f" , this . mainBranch ] )
141+ } catch ( err ) {
142+ this . log (
143+ `[restoreMain] failed to force checkout ${ this . mainBranch } : ${ err instanceof Error ? err . message : String ( err ) } ` ,
144+ )
145+ }
146+ } else {
147+ try {
148+ await this . git . checkout ( this . mainBranch )
149+ } catch ( err ) {
150+ this . log (
151+ `[restoreMain] failed to checkout ${ this . mainBranch } : ${ err instanceof Error ? err . message : String ( err ) } ` ,
152+ )
153+
154+ // Escalate to a forced checkout if we can't checkout the
155+ // main branch under normal circumstances.
156+ currentBranch = await this . git . revparse ( [ "--abbrev-ref" , "HEAD" ] )
157+
158+ if ( currentBranch !== this . mainBranch ) {
159+ await this . git . checkout ( [ "-f" , this . mainBranch ] ) . catch ( ( ) => { } )
160+ }
161+ }
162+ }
163+ }
164+
165+ currentBranch = await this . git . revparse ( [ "--abbrev-ref" , "HEAD" ] )
166+
167+ if ( currentBranch !== this . mainBranch ) {
168+ throw new Error ( `Unable to restore ${ this . mainBranch } ` )
139169 }
140170
141171 if ( stashSha ) {
142172 this . log ( `[restoreMain] applying stash ${ stashSha } ` )
143- await this . git . raw ( [ "stash" , "apply" , "--index" , stashSha ] )
173+
174+ try {
175+ await this . git . raw ( [ "stash" , "apply" , "--index" , stashSha ] )
176+ } catch ( err ) {
177+ this . log ( `[restoreMain] Failed to apply stash: ${ err instanceof Error ? err . message : String ( err ) } ` )
178+ }
144179 }
145180
146- this . log ( `[restoreMain] restoring from ${ branch } ` )
147- await this . git . raw ( [ "restore" , "--source" , branch , "--worktree" , "--" , "." ] )
181+ this . log ( `[restoreMain] restoring from ${ branch } branch` )
182+
183+ try {
184+ await this . git . raw ( [ "restore" , "--source" , branch , "--worktree" , "--" , "." ] )
185+ } catch ( err ) {
186+ this . log ( `[restoreMain] Failed to restore branch: ${ err instanceof Error ? err . message : String ( err ) } ` )
187+ }
148188 }
149189
150190 public async saveCheckpoint ( message : string ) {
191+ const startTime = Date . now ( )
192+
151193 await this . ensureBranch ( this . mainBranch )
152194
153195 const stashSha = ( await this . git . raw ( [ "stash" , "create" ] ) ) . trim ( )
@@ -172,15 +214,13 @@ export class CheckpointService {
172214 */
173215 try {
174216 await this . git . add ( [ "-A" ] )
175- const status = await this . git . status ( )
176- this . log ( `[saveCheckpoint] status: ${ JSON . stringify ( status ) } ` )
177217 } catch ( err ) {
178- await this . git . checkout ( [ "-f" , this . mainBranch ] )
179- await this . git . branch ( [ "-D" , stashBranch ] ) . catch ( ( ) => { } )
180-
181- throw new Error (
182- `[saveCheckpoint] Failed in stage stash phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
218+ this . log (
219+ `[saveCheckpoint] failed in stage stash phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
183220 )
221+ await this . restoreMain ( { branch : stashBranch , stashSha, force : true } )
222+ await this . git . branch ( [ "-D" , stashBranch ] ) . catch ( ( ) => { } )
223+ throw err
184224 }
185225
186226 /**
@@ -192,17 +232,25 @@ export class CheckpointService {
192232 * - UNDO: Create branch
193233 * - UNDO: Change branch
194234 */
235+ let stashCommit
236+
195237 try {
196- // TODO: Add a test to see if empty commits break this.
197- const tempCommit = await this . git . commit ( message , undefined , { "--no-verify" : null } )
198- this . log ( `[saveCheckpoint] tempCommit: ${ message } -> ${ JSON . stringify ( tempCommit ) } ` )
238+ stashCommit = await this . git . commit ( message , undefined , { "--no-verify" : null } )
239+ this . log ( `[saveCheckpoint] stashCommit: ${ message } -> ${ JSON . stringify ( stashCommit ) } ` )
199240 } catch ( err ) {
200- await this . git . checkout ( [ "-f" , this . mainBranch ] )
241+ this . log (
242+ `[saveCheckpoint] failed in stash commit phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
243+ )
244+ await this . restoreMain ( { branch : stashBranch , stashSha, force : true } )
201245 await this . git . branch ( [ "-D" , stashBranch ] ) . catch ( ( ) => { } )
246+ throw err
247+ }
202248
203- throw new Error (
204- `[saveCheckpoint] Failed in stash commit phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
205- )
249+ if ( ! stashCommit ) {
250+ this . log ( "[saveCheckpoint] no stash commit" )
251+ await this . restoreMain ( { branch : stashBranch , stashSha } )
252+ await this . git . branch ( [ "-D" , stashBranch ] )
253+ return undefined
206254 }
207255
208256 /**
@@ -219,12 +267,10 @@ export class CheckpointService {
219267 try {
220268 diff = await this . git . diff ( [ latestSha , stashBranch ] )
221269 } catch ( err ) {
270+ this . log ( `[saveCheckpoint] failed in diff phase: ${ err instanceof Error ? err . message : String ( err ) } ` )
222271 await this . restoreMain ( { branch : stashBranch , stashSha, force : true } )
223272 await this . git . branch ( [ "-D" , stashBranch ] ) . catch ( ( ) => { } )
224-
225- throw new Error (
226- `[saveCheckpoint] Failed in diff phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
227- )
273+ throw err
228274 }
229275
230276 if ( ! diff ) {
@@ -249,12 +295,10 @@ export class CheckpointService {
249295 await this . git . reset ( [ "--hard" , this . mainBranch ] )
250296 this . log ( `[saveCheckpoint] reset ${ this . hiddenBranch } ` )
251297 } catch ( err ) {
298+ this . log ( `[saveCheckpoint] failed in reset phase: ${ err instanceof Error ? err . message : String ( err ) } ` )
252299 await this . restoreMain ( { branch : stashBranch , stashSha, force : true } )
253300 await this . git . branch ( [ "-D" , stashBranch ] ) . catch ( ( ) => { } )
254-
255- throw new Error (
256- `[saveCheckpoint] Failed in reset phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
257- )
301+ throw err
258302 }
259303
260304 /**
@@ -289,25 +333,33 @@ export class CheckpointService {
289333 this . currentCheckpoint = commit
290334 this . log ( `[saveCheckpoint] cherry-pick commit = ${ commit } ` )
291335 } catch ( err ) {
336+ this . log (
337+ `[saveCheckpoint] failed in cherry pick phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
338+ )
292339 await this . git . reset ( [ "--hard" , latestSha ] ) . catch ( ( ) => { } )
293340 await this . restoreMain ( { branch : stashBranch , stashSha, force : true } )
294341 await this . git . branch ( [ "-D" , stashBranch ] ) . catch ( ( ) => { } )
295-
296- throw new Error (
297- `[saveCheckpoint] Failed in cherry pick phase: ${ err instanceof Error ? err . message : String ( err ) } ` ,
298- )
342+ throw err
299343 }
300344
301345 await this . restoreMain ( { branch : stashBranch , stashSha } )
302346 await this . git . branch ( [ "-D" , stashBranch ] )
303347
348+ // We've gotten reports that checkpoints can be slow in some cases, so
349+ // we'll log the duration of the checkpoint save.
350+ const duration = Date . now ( ) - startTime
351+ this . log ( `[saveCheckpoint] saved checkpoint ${ commit } in ${ duration } ms` )
352+
304353 return { commit }
305354 }
306355
307356 public async restoreCheckpoint ( commitHash : string ) {
357+ const startTime = Date . now ( )
308358 await this . ensureBranch ( this . mainBranch )
309359 await this . git . clean ( [ CleanOptions . FORCE , CleanOptions . RECURSIVE ] )
310360 await this . git . raw ( [ "restore" , "--source" , commitHash , "--worktree" , "--" , "." ] )
361+ const duration = Date . now ( ) - startTime
362+ this . log ( `[restoreCheckpoint] restored checkpoint ${ commitHash } in ${ duration } ms` )
311363 this . currentCheckpoint = commitHash
312364 }
313365
0 commit comments