@@ -72,7 +72,7 @@ export class CheckpointService {
7272 const status = await this . git . status ( )
7373
7474 if ( status . files . length > 0 ) {
75- await this . git . stash ( [ "-u" ] ) // Stash tracked and untracked files.
75+ await this . git . stash ( [ "-u" ] ) // Includes tracked and untracked files.
7676 return true
7777 }
7878
@@ -83,7 +83,7 @@ export class CheckpointService {
8383 const stashList = await this . git . stashList ( )
8484
8585 if ( stashList . all . length > 0 ) {
86- await this . git . stash ( [ "apply" ] ) // Apply the most recent stash.
86+ await this . git . stash ( [ "apply" ] ) // Applies the most recent stash only .
8787 return true
8888 }
8989
@@ -94,15 +94,15 @@ export class CheckpointService {
9494 const stashList = await this . git . stashList ( )
9595
9696 if ( stashList . all . length > 0 ) {
97- await this . git . stash ( [ "pop" ] ) // Pop the most recent stash.
97+ await this . git . stash ( [ "pop" ] ) // Pops the most recent stash only .
9898 return true
9999 }
100100
101101 return false
102102 }
103103
104104 private async ensureBranch ( expectedBranch : string ) {
105- const branch = await this . git . revparse ( [ "--abbrev-ref" , "HEAD" ] ) // git rev-parse --abbrev-ref HEAD
105+ const branch = await this . git . revparse ( [ "--abbrev-ref" , "HEAD" ] )
106106
107107 if ( branch . trim ( ) !== expectedBranch ) {
108108 throw new Error ( `Git branch mismatch: expected '${ expectedBranch } ' but found '${ branch } '` )
@@ -121,23 +121,23 @@ export class CheckpointService {
121121 for ( const file of files . filter ( ( f ) => ! f . binary ) ) {
122122 const relPath = file . file
123123 const absPath = path . join ( this . baseDir , relPath )
124+
125+ // If modified both before and after will generate content.
126+ // If added only after will generate content.
127+ // If deleted only before will generate content.
124128 let beforeContent = ""
125129 let afterContent = ""
126130
127- // Try to get content from both commits - handles all cases:
128- // - Modified: both will succeed
129- // - Added: only 'to' will succeed
130- // - Deleted: only 'from' will succeed
131131 try {
132132 beforeContent = await this . git . show ( [ `${ from } :${ relPath } ` ] )
133133 } catch ( err ) {
134- // File didn 't exist in older commit => remains empty .
134+ // File doesn 't exist in older commit.
135135 }
136136
137137 try {
138138 afterContent = await this . git . show ( [ `${ to } :${ relPath } ` ] )
139139 } catch ( err ) {
140- // File didn 't exist in newer commit => remains empty .
140+ // File doesn 't exist in newer commit.
141141 }
142142
143143 result . push ( {
@@ -153,76 +153,74 @@ export class CheckpointService {
153153 await this . ensureBranch ( this . mainBranch )
154154
155155 // Attempt to stash pending changes (including untracked files).
156- const pendingChanges = await this . pushStash ( ) // git stash -u
156+ const pendingChanges = await this . pushStash ( )
157157
158158 // Get the latest commit on the hidden branch before we reset it.
159- const latestHash = await this . git . revparse ( [ this . hiddenBranch ] ) // git rev-parse <hiddenBranch>
159+ const latestHash = await this . git . revparse ( [ this . hiddenBranch ] )
160160
161- // Check if there is any diff relative to the last checkpoint .
161+ // Check if there is any diff relative to the latest commit .
162162 if ( ! pendingChanges ) {
163- const diff = await this . git . diff ( [ latestHash ] ) // git diff <latestHash>
163+ const diff = await this . git . diff ( [ latestHash ] )
164164
165165 if ( ! diff ) {
166- this . log ( `[saveCheckpoint] No changes relative to previous checkpoint; nothing to commit. ` )
166+ this . log ( `[saveCheckpoint] No changes detected, giving up ` )
167167 return undefined
168168 }
169169 }
170170
171- await this . git . checkout ( this . hiddenBranch ) // git checkout <hiddenBranch>
171+ await this . git . checkout ( this . hiddenBranch )
172172
173173 const reset = async ( ) => {
174- await this . git . reset ( [ "HEAD" , "." ] ) // git reset HEAD .
175- await this . git . clean ( [ CleanOptions . FORCE , CleanOptions . RECURSIVE ] ) // git clean -f -d
176- await this . git . reset ( [ "--hard" , latestHash ] ) // git reset --hard <latestHash>
177- await this . git . checkout ( this . mainBranch ) // git checkout <mainBranch>
178- await this . popStash ( ) // git stash pop
174+ await this . git . reset ( [ "HEAD" , "." ] )
175+ await this . git . clean ( [ CleanOptions . FORCE , CleanOptions . RECURSIVE ] )
176+ await this . git . reset ( [ "--hard" , latestHash ] )
177+ await this . git . checkout ( this . mainBranch )
178+ await this . popStash ( )
179179 }
180180
181181 try {
182182 // Reset hidden branch to match main and apply the pending changes.
183- await this . git . reset ( [ "--hard" , this . mainBranch ] ) // git reset --hard <mainBranch>
183+ await this . git . reset ( [ "--hard" , this . mainBranch ] )
184184
185- // Only try to apply stash if we had pending changes.
186185 if ( pendingChanges ) {
187- await this . applyStash ( ) // git stash apply 0
186+ await this . applyStash ( )
188187 }
189188
190189 // Using "-A" ensures that deletions are staged as well.
191- await this . git . add ( [ "-A" ] ) // git add -A
192- const diff = await this . git . diff ( [ latestHash ] ) // git diff <latestHash>
190+ await this . git . add ( [ "-A" ] )
191+ const diff = await this . git . diff ( [ latestHash ] )
193192
194193 if ( ! diff ) {
195- // If the diff is empty and there are no untracked files then we
196- // don't need to commit.
197- this . log ( `[saveCheckpoint] Diff is empty, no untracked files` )
194+ this . log ( `[saveCheckpoint] No changes detected, resetting and giving up` )
198195 await reset ( )
199196 return undefined
200197 }
201198
202199 // Otherwise, commit the changes.
203- const status = await this . git . status ( ) // git status
200+ const status = await this . git . status ( )
204201 this . log ( `[saveCheckpoint] Changes detected, committing ${ JSON . stringify ( status ) } ` )
205202
203+ // Allow empty commits in order to correctly handle deletion of
204+ // untracked files (see unit tests for an example of this).
205+ // Additionally, skip pre-commit hooks so that they don't slow
206+ // things down or tamper with the contents of the commit.
206207 const commit = await this . git . commit ( message , undefined , {
207208 "--allow-empty" : null ,
208- "--no-verify" : null , // Skip pre-commit hooks.
209+ "--no-verify" : null ,
209210 } )
210211
211212 await this . git . checkout ( this . mainBranch )
212213
213- // Only pop stash if we had stashed something earlier.
214214 if ( pendingChanges ) {
215- await this . popStash ( ) // git stash pop
215+ await this . popStash ( )
216216 }
217217
218218 return commit
219219 } catch ( err ) {
220220 this . log ( `[saveCheckpoint] Failed to save checkpoint: ${ err instanceof Error ? err . message : String ( err ) } ` )
221221
222- // If we're not on the main branch, we need to trigger a reset
223- // (equivalent to the empty diff case above).
224- // This ensures that we return to the main branch and restore the
225- // pending changes.
222+ // If we're not on the main branch then we need to trigger a reset
223+ // to return to the main branch and restore it's previous state.
226224 const currentBranch = await this . git . revparse ( [ "--abbrev-ref" , "HEAD" ] )
227225
228226 if ( currentBranch . trim ( ) !== this . mainBranch ) {
0 commit comments