@@ -26,6 +26,7 @@ export class LocalCheckpointer {
2626 const checkpointer = new LocalCheckpointer ( options )
2727 await checkpointer . ensureGitInstalled ( )
2828 await checkpointer . ensureGitRepo ( )
29+ await checkpointer . initGitConfig ( )
2930 await checkpointer . initHiddenBranch ( )
3031 return checkpointer
3132 }
@@ -46,18 +47,6 @@ export class LocalCheckpointer {
4647 this . git = simpleGit ( options )
4748 }
4849
49- /**
50- * Initialize git configuration. Should be called after constructor.
51- */
52- private async initGitConfig ( ) : Promise < void > {
53- try {
54- await this . git . addConfig ( "user.name" , "Roo Code" )
55- await this . git . addConfig ( "user.email" , "[email protected] " ) 56- } catch ( err ) {
57- throw new Error ( `Failed to configure Git: ${ err instanceof Error ? err . message : String ( err ) } ` )
58- }
59- }
60-
6150 /**
6251 * Ensure that Git is installed.
6352 */
@@ -85,6 +74,35 @@ export class LocalCheckpointer {
8574 }
8675 }
8776
77+ /**
78+ * Initialize git configuration. Should be called after constructor.
79+ */
80+ private async initGitConfig ( ) {
81+ try {
82+ await this . git . addConfig ( "user.name" , "Roo Code" )
83+ await this . git . addConfig ( "user.email" , "[email protected] " ) 84+ } catch ( err ) {
85+ throw new Error ( `Failed to configure Git: ${ err instanceof Error ? err . message : String ( err ) } ` )
86+ }
87+ }
88+
89+ /**
90+ * Create the hidden branch if it doesn't exist. Otherwise, do nothing.
91+ * If the branch is missing, we base it off the main branch.
92+ */
93+ private async initHiddenBranch ( ) : Promise < void > {
94+ // Check if the branch already exists.
95+ const branchSummary = await this . git . branch ( )
96+
97+ if ( ! branchSummary . all . includes ( this . hiddenBranch ) ) {
98+ // Create the new branch from main.
99+ await this . git . checkoutBranch ( this . hiddenBranch , this . mainBranch )
100+
101+ // Switch back to main.
102+ await this . git . checkout ( this . mainBranch )
103+ }
104+ }
105+
88106 private async pushStash ( ) {
89107 try {
90108 const status = await this . git . status ( )
@@ -145,23 +163,6 @@ export class LocalCheckpointer {
145163 }
146164 }
147165
148- /**
149- * Create the hidden branch if it doesn't exist. Otherwise, do nothing.
150- * If the branch is missing, we base it off the main branch.
151- */
152- private async initHiddenBranch ( ) : Promise < void > {
153- // Check if the branch already exists.
154- const branchSummary = await this . git . branch ( )
155-
156- if ( ! branchSummary . all . includes ( this . hiddenBranch ) ) {
157- // Create the new branch from main.
158- await this . git . checkoutBranch ( this . hiddenBranch , this . mainBranch )
159-
160- // Switch back to main.
161- await this . git . checkout ( this . mainBranch )
162- }
163- }
164-
165166 /**
166167 * List commits on the hidden branch as checkpoints.
167168 * We can parse the commit log to build an array of `Checkpoint`.
@@ -177,7 +178,7 @@ export class LocalCheckpointer {
177178 }
178179
179180 /**
180- * Commit changes in the working directory (on the hidden branch) as a new checkpoint.\
181+ * Commit changes in the working directory (on the hidden branch) as a new checkpoint.
181182 * Preserves the current state of the main branch.
182183 */
183184 public async saveCheckpoint ( message : string ) {
@@ -194,10 +195,25 @@ export class LocalCheckpointer {
194195 }
195196
196197 try {
198+ // Get the latest commit on the hidden branch before we reset it.
199+ const latestHash = await this . git . revparse ( [ this . hiddenBranch ] )
200+
201+ // Reset hidden branch to match main and apply the pending changes.
197202 await this . git . checkout ( this . hiddenBranch )
198- await this . git . reset ( [ "--hard" , this . mainBranch ] ) // Reset hidden branch to match main
199- await this . applyStash ( ) // Apply the stashed changes
200- await this . git . add ( [ "." ] ) // Stage everything
203+ await this . git . reset ( [ "--hard" , this . mainBranch ] )
204+ await this . applyStash ( )
205+
206+ // If there are no changes, we don't need to commit.
207+ const diff = await this . git . diff ( [ latestHash ] )
208+
209+ if ( ! diff ) {
210+ await this . git . checkout ( this . mainBranch )
211+ await this . popStash ( )
212+ return undefined
213+ }
214+
215+ // Otherwise, commit the changes.
216+ await this . git . add ( [ "." ] )
201217 const commit = await this . git . commit ( message )
202218 await this . git . checkout ( this . mainBranch )
203219 await this . popStash ( )
@@ -222,10 +238,13 @@ export class LocalCheckpointer {
222238 throw new Error ( `Must be on ${ this . mainBranch } branch to restore checkpoints. Currently on: ${ branch } ` )
223239 }
224240
225- // Discard any pending changes. Note that these should already be preserved
226- // as a checkpoint, but we should verify that.
227- await this . pushStash ( )
228- await this . dropStash ( )
241+ // Persist pending changes in a checkpoint and then discard them.
242+ const commit = await this . saveCheckpoint ( `restoreCheckpoint ${ commitHash } ` )
243+
244+ if ( commit ) {
245+ await this . pushStash ( )
246+ await this . dropStash ( )
247+ }
229248
230249 await this . git . raw ( [ "restore" , "--source" , commitHash , "--worktree" , "--" , "." ] )
231250 }
0 commit comments