@@ -170,51 +170,83 @@ function getCompletedTurnsFor(sg: ShadowGit, sessionId: string): number[] {
170170 return ids ;
171171}
172172
173+ interface RestorePlan {
174+ throughTurnId : number ;
175+ baseTurnId : number ;
176+ affectedTurns : number [ ] ;
177+ baseline : string ;
178+ }
179+
180+ function getTurnRestorePlan ( sg : ShadowGit , sessionId : string , turnId : number ) : RestorePlan | null {
181+ const baseline = sg . findCommitByMessage ( commitMsg ( sessionId , turnId , 'baseline' ) ) ;
182+ const final = sg . findCommitByMessage ( commitMsg ( sessionId , turnId , 'final' ) ) ;
183+ if ( ! baseline || ! final ) return null ;
184+ return {
185+ throughTurnId : turnId ,
186+ baseTurnId : turnId ,
187+ affectedTurns : [ ] ,
188+ baseline,
189+ } ;
190+ }
191+
192+ function getRollbackToTurnPlan (
193+ sg : ShadowGit ,
194+ sessionId : string ,
195+ throughTurnId : number
196+ ) : RestorePlan | null {
197+ const completedTurns = getCompletedTurnsFor ( sg , sessionId ) ;
198+ const affectedTurns = completedTurns . filter ( ( id ) => id >= throughTurnId ) ;
199+ if ( affectedTurns . length === 0 ) return null ;
200+
201+ const baseline = sg . findCommitByMessage ( commitMsg ( sessionId , throughTurnId , 'baseline' ) ) ;
202+ if ( ! baseline ) return null ;
203+
204+ return {
205+ throughTurnId,
206+ baseTurnId : throughTurnId ,
207+ affectedTurns,
208+ baseline,
209+ } ;
210+ }
211+
173212function revertFilesImpl (
174213 projectPath : string ,
175214 sessionId : string ,
176- turnId : number ,
215+ plan : RestorePlan ,
177216 selectedFiles : string [ ] ,
178217 action : CodeRestoreEntry [ 'action' ] ,
179218 sg : ShadowGit
180219) : CodeRollbackResult {
181220 if ( selectedFiles . length === 0 ) {
182221 return {
183222 reverted : false ,
184- throughTurnId : turnId ,
185- baseTurnId : turnId ,
186- affectedTurns : [ ] ,
223+ throughTurnId : plan . throughTurnId ,
224+ baseTurnId : plan . baseTurnId ,
225+ affectedTurns : plan . affectedTurns ,
187226 selectedFiles : [ ] ,
188227 restoreEntry : null ,
189228 } ;
190229 }
191230
192- const baseline = sg . findCommitByMessage ( commitMsg ( sessionId , turnId , 'baseline' ) ) ;
193- if ( ! baseline )
194- return {
195- reverted : false ,
196- throughTurnId : turnId ,
197- baseTurnId : turnId ,
198- affectedTurns : [ ] ,
199- selectedFiles : [ ] ,
200- restoreEntry : null ,
201- } ;
202-
203231 sg . lock ( ) ;
204232 try {
205233 let safetyCommit : string ;
206234 const existingEntry = readRestoreEntry ( sg . gitDir , sessionId ) ;
207235
208- if ( existingEntry && existingEntry . throughTurnId === turnId && existingEntry . safetyCommit ) {
236+ if (
237+ existingEntry &&
238+ existingEntry . throughTurnId === plan . throughTurnId &&
239+ existingEntry . safetyCommit
240+ ) {
209241 safetyCommit = existingEntry . safetyCommit ;
210242 } else {
211- safetyCommit = sg . commit ( commitMsg ( sessionId , turnId , 'revert-safety' ) ) ;
243+ safetyCommit = sg . commit ( commitMsg ( sessionId , plan . throughTurnId , 'revert-safety' ) ) ;
212244 }
213245
214- sg . checkoutFiles ( baseline , selectedFiles ) ;
246+ sg . checkoutFiles ( plan . baseline , selectedFiles ) ;
215247
216248 const combinedFiles =
217- existingEntry && existingEntry . throughTurnId === turnId
249+ existingEntry && existingEntry . throughTurnId === plan . throughTurnId
218250 ? [
219251 ...new Map (
220252 [ ...existingEntry . selectedFiles , ...selectedFiles ] . map ( ( f ) => [
@@ -227,14 +259,14 @@ function revertFilesImpl(
227259
228260 const entry : CodeRestoreEntry = {
229261 id : createHash ( 'sha256' )
230- . update ( `${ sessionId } -${ turnId } -${ Date . now ( ) } ` )
262+ . update ( `${ sessionId } -${ plan . throughTurnId } -${ Date . now ( ) } ` )
231263 . digest ( 'hex' )
232264 . slice ( 0 , 12 ) ,
233265 sessionId,
234266 action,
235- throughTurnId : turnId ,
236- baseTurnId : turnId ,
237- affectedTurns : [ ] ,
267+ throughTurnId : plan . throughTurnId ,
268+ baseTurnId : plan . baseTurnId ,
269+ affectedTurns : plan . affectedTurns ,
238270 selectedFiles : combinedFiles ,
239271 safetyCommit,
240272 timestamp : new Date ( ) . toISOString ( ) ,
@@ -243,9 +275,9 @@ function revertFilesImpl(
243275
244276 return {
245277 reverted : true ,
246- throughTurnId : turnId ,
247- baseTurnId : turnId ,
248- affectedTurns : [ ] ,
278+ throughTurnId : plan . throughTurnId ,
279+ baseTurnId : plan . baseTurnId ,
280+ affectedTurns : plan . affectedTurns ,
249281 selectedFiles : combinedFiles ,
250282 restoreEntry : entry ,
251283 } ;
@@ -440,7 +472,17 @@ export class CheckpointService extends Effect.Service<CheckpointService>()('Chec
440472 file : string
441473 ) : CodeRollbackResult => {
442474 const sg = ensure ( projectPath ) ;
443- return revertFilesImpl ( projectPath , sessionId , turnId , [ file ] , 'checkpoint-file' , sg ) ;
475+ const plan = getTurnRestorePlan ( sg , sessionId , turnId ) ;
476+ if ( ! plan )
477+ return {
478+ reverted : false ,
479+ throughTurnId : turnId ,
480+ baseTurnId : turnId ,
481+ affectedTurns : [ ] ,
482+ selectedFiles : [ ] ,
483+ restoreEntry : null ,
484+ } ;
485+ return revertFilesImpl ( projectPath , sessionId , plan , [ file ] , 'checkpoint-file' , sg ) ;
444486 } ,
445487
446488 revertCheckpointFiles : (
@@ -450,7 +492,17 @@ export class CheckpointService extends Effect.Service<CheckpointService>()('Chec
450492 files : string [ ]
451493 ) : CodeRollbackResult => {
452494 const sg = ensure ( projectPath ) ;
453- return revertFilesImpl ( projectPath , sessionId , turnId , files , 'checkpoint-files' , sg ) ;
495+ const plan = getTurnRestorePlan ( sg , sessionId , turnId ) ;
496+ if ( ! plan )
497+ return {
498+ reverted : false ,
499+ throughTurnId : turnId ,
500+ baseTurnId : turnId ,
501+ affectedTurns : [ ] ,
502+ selectedFiles : [ ] ,
503+ restoreEntry : null ,
504+ } ;
505+ return revertFilesImpl ( projectPath , sessionId , plan , files , 'checkpoint-files' , sg ) ;
454506 } ,
455507
456508 // ---- B3: revertCheckpointAgentFiles / revertCheckpointAllFiles ----
@@ -481,10 +533,20 @@ export class CheckpointService extends Effect.Service<CheckpointService>()('Chec
481533 selectedFiles : [ ] ,
482534 restoreEntry : null ,
483535 } ;
536+ const plan = getTurnRestorePlan ( sg , sessionId , turnId ) ;
537+ if ( ! plan )
538+ return {
539+ reverted : false ,
540+ throughTurnId : turnId ,
541+ baseTurnId : null ,
542+ affectedTurns : [ ] ,
543+ selectedFiles : [ ] ,
544+ restoreEntry : null ,
545+ } ;
484546 return revertFilesImpl (
485547 projectPath ,
486548 sessionId ,
487- turnId ,
549+ plan ,
488550 changes . agentModified ,
489551 'checkpoint-agent' ,
490552 sg
@@ -518,7 +580,24 @@ export class CheckpointService extends Effect.Service<CheckpointService>()('Chec
518580 selectedFiles : [ ] ,
519581 restoreEntry : null ,
520582 } ;
521- return revertFilesImpl ( projectPath , sessionId , turnId , all , 'checkpoint-all' , sg ) ;
583+ const plan = getTurnRestorePlan ( sg , sessionId , turnId ) ;
584+ if ( ! plan )
585+ return {
586+ reverted : false ,
587+ throughTurnId : turnId ,
588+ baseTurnId : null ,
589+ affectedTurns : [ ] ,
590+ selectedFiles : [ ] ,
591+ restoreEntry : null ,
592+ } ;
593+ return revertFilesImpl (
594+ projectPath ,
595+ sessionId ,
596+ plan ,
597+ all ,
598+ 'checkpoint-all' ,
599+ sg
600+ ) ;
522601 } ,
523602
524603 // ---- B4: previewRollbackDiff ----
@@ -529,20 +608,18 @@ export class CheckpointService extends Effect.Service<CheckpointService>()('Chec
529608 throughTurnId : number
530609 ) : RollbackPreviewDiff => {
531610 const sg = ensure ( projectPath ) ;
532- const completedTurns = getCompletedTurnsFor ( sg , sessionId ) ;
533- const affectedTurns = completedTurns . filter ( ( id ) => id > throughTurnId ) ;
534- if ( affectedTurns . length === 0 ) {
611+ const plan = getRollbackToTurnPlan ( sg , sessionId , throughTurnId ) ;
612+ if ( ! plan ) {
535613 return { throughTurnId, baseTurnId : null , affectedTurns : [ ] , diff : '' } ;
536614 }
537615
538- const baseTurnId = affectedTurns [ 0 ] ! - 1 ;
539- const baseline = sg . findCommitByMessage ( commitMsg ( sessionId , baseTurnId , 'baseline' ) ) ;
540- if ( ! baseline ) {
541- return { throughTurnId, baseTurnId, affectedTurns, diff : '' } ;
542- }
543-
544- const result = sg . git ( 'diff' , baseline ) ;
545- return { throughTurnId, baseTurnId, affectedTurns, diff : result . stdout } ;
616+ const result = sg . git ( 'diff' , plan . baseline ) ;
617+ return {
618+ throughTurnId,
619+ baseTurnId : plan . baseTurnId ,
620+ affectedTurns : plan . affectedTurns ,
621+ diff : result . stdout ,
622+ } ;
546623 } ,
547624
548625 // ---- B5: rollbackCodeToTurn ----
@@ -553,33 +630,19 @@ export class CheckpointService extends Effect.Service<CheckpointService>()('Chec
553630 throughTurnId : number
554631 ) : CodeRollbackResult => {
555632 const sg = ensure ( projectPath ) ;
556- const completedTurns = getCompletedTurnsFor ( sg , sessionId ) ;
557- const affectedTurns = completedTurns . filter ( ( id ) => id > throughTurnId ) ;
558- if ( affectedTurns . length === 0 ) {
559- return {
560- reverted : true ,
561- throughTurnId,
562- baseTurnId : null ,
563- affectedTurns : [ ] ,
564- selectedFiles : [ ] ,
565- restoreEntry : null ,
566- } ;
567- }
568-
569- const baseTurnId = affectedTurns [ 0 ] ! - 1 ;
570- const baseline = sg . findCommitByMessage ( commitMsg ( sessionId , baseTurnId , 'baseline' ) ) ;
571- if ( ! baseline ) {
633+ const plan = getRollbackToTurnPlan ( sg , sessionId , throughTurnId ) ;
634+ if ( ! plan ) {
572635 return {
573636 reverted : false ,
574637 throughTurnId,
575- baseTurnId,
638+ baseTurnId : null ,
576639 affectedTurns : [ ] ,
577640 selectedFiles : [ ] ,
578641 restoreEntry : null ,
579642 } ;
580643 }
581644
582- const diffResult = sg . git ( 'diff' , '--name-only' , baseline ) ;
645+ const diffResult = sg . git ( 'diff' , '--name-only' , plan . baseline ) ;
583646 const selectedFiles = diffResult . stdout
584647 . trim ( )
585648 . split ( '\n' )
@@ -590,45 +653,14 @@ export class CheckpointService extends Effect.Service<CheckpointService>()('Chec
590653 return {
591654 reverted : true ,
592655 throughTurnId,
593- baseTurnId,
594- affectedTurns,
656+ baseTurnId : plan . baseTurnId ,
657+ affectedTurns : plan . affectedTurns ,
595658 selectedFiles : [ ] ,
596659 restoreEntry : null ,
597660 } ;
598661 }
599662
600- sg . lock ( ) ;
601- try {
602- const safetyCommit = sg . commit ( commitMsg ( sessionId , throughTurnId , 'rollback-safety' ) ) ;
603- sg . checkoutFiles ( baseline , selectedFiles ) ;
604-
605- const entry : CodeRestoreEntry = {
606- id : createHash ( 'sha256' )
607- . update ( `${ sessionId } -${ Date . now ( ) } ` )
608- . digest ( 'hex' )
609- . slice ( 0 , 12 ) ,
610- sessionId,
611- action : 'rollback-to-turn' ,
612- throughTurnId,
613- baseTurnId,
614- affectedTurns,
615- selectedFiles,
616- safetyCommit,
617- timestamp : new Date ( ) . toISOString ( ) ,
618- } ;
619- writeRestoreEntry ( sg . gitDir , sessionId , entry ) ;
620-
621- return {
622- reverted : true ,
623- throughTurnId,
624- baseTurnId,
625- affectedTurns,
626- selectedFiles,
627- restoreEntry : entry ,
628- } ;
629- } finally {
630- sg . unlock ( ) ;
631- }
663+ return revertFilesImpl ( projectPath , sessionId , plan , selectedFiles , 'rollback-to-turn' , sg ) ;
632664 } ,
633665
634666 // ---- B6: undoLastCodeRollback ----
0 commit comments