@@ -36,6 +36,9 @@ pub struct CheckpointManager {
3636 /// Path to the shadow (bare) git repository
3737 pub shadow_repo_path : PathBuf ,
3838
39+ /// Path to current working directory
40+ pub work_tree_path : PathBuf ,
41+
3942 /// All checkpoints in chronological order
4043 pub checkpoints : Vec < Checkpoint > ,
4144
@@ -103,14 +106,17 @@ impl CheckpointManager {
103106 let path = path. as_ref ( ) ;
104107 os. fs . create_dir_all ( path) . await ?;
105108
109+ let work_tree_path =
110+ std:: env:: current_dir ( ) . map_err ( |e| eyre ! ( "Failed to get current working directory: {}" , e) ) ?;
111+
106112 // Initialize bare repository
107- run_git ( path, false , & [ "init" , "--bare" , & path. to_string_lossy ( ) ] ) ?;
113+ run_git ( path, None , & [ "init" , "--bare" , & path. to_string_lossy ( ) ] ) ?;
108114
109115 // Configure git
110116 configure_git ( & path. to_string_lossy ( ) ) ?;
111117
112118 // Create initial checkpoint
113- stage_commit_tag ( & path. to_string_lossy ( ) , "Initial state" , "0" ) ?;
119+ stage_commit_tag ( & path. to_string_lossy ( ) , & work_tree_path , "Initial state" , "0" ) ?;
114120
115121 let initial_checkpoint = Checkpoint {
116122 tag : "0" . to_string ( ) ,
@@ -126,6 +132,7 @@ impl CheckpointManager {
126132
127133 Ok ( Self {
128134 shadow_repo_path : path. to_path_buf ( ) ,
135+ work_tree_path,
129136 checkpoints : vec ! [ initial_checkpoint] ,
130137 tag_index,
131138 current_turn : 0 ,
@@ -146,7 +153,12 @@ impl CheckpointManager {
146153 tool_name : Option < String > ,
147154 ) -> Result < ( ) > {
148155 // Stage, commit and tag
149- stage_commit_tag ( & self . shadow_repo_path . to_string_lossy ( ) , description, tag) ?;
156+ stage_commit_tag (
157+ & self . shadow_repo_path . to_string_lossy ( ) ,
158+ & self . work_tree_path ,
159+ description,
160+ tag,
161+ ) ?;
150162
151163 // Record checkpoint metadata
152164 let checkpoint = Checkpoint {
@@ -175,7 +187,9 @@ impl CheckpointManager {
175187
176188 if hard {
177189 // Hard: reset the whole work-tree to the tag
178- let output = run_git ( & self . shadow_repo_path , true , & [ "reset" , "--hard" , tag] ) ?;
190+ let output = run_git ( & self . shadow_repo_path , Some ( & self . work_tree_path ) , & [
191+ "reset" , "--hard" , tag,
192+ ] ) ?;
179193 if !output. status . success ( ) {
180194 bail ! ( "Failed to restore: {}" , String :: from_utf8_lossy( & output. stderr) ) ;
181195 }
@@ -187,7 +201,9 @@ impl CheckpointManager {
187201 return Ok ( ( ) ) ;
188202 }
189203 // Use checkout against work-tree
190- let output = run_git ( & self . shadow_repo_path , true , & [ "checkout" , tag, "--" , "." ] ) ?;
204+ let output = run_git ( & self . shadow_repo_path , Some ( & self . work_tree_path ) , & [
205+ "checkout" , tag, "--" , "." ,
206+ ] ) ?;
191207 if !output. status . success ( ) {
192208 bail ! ( "Failed to restore: {}" , String :: from_utf8_lossy( & output. stderr) ) ;
193209 }
@@ -205,7 +221,7 @@ impl CheckpointManager {
205221 let out = run_git (
206222 & self . shadow_repo_path ,
207223 // work_tree
208- false ,
224+ None ,
209225 & [ "ls-tree" , "-r" , "--name-only" , tag] ,
210226 ) ?;
211227 Ok ( !out. stdout . is_empty ( ) )
@@ -223,7 +239,7 @@ impl CheckpointManager {
223239
224240 /// Compute file statistics between two checkpoints
225241 pub fn compute_stats_between ( & self , from : & str , to : & str ) -> Result < FileStats > {
226- let output = run_git ( & self . shadow_repo_path , false , & [ "diff" , "--name-status" , from, to] ) ?;
242+ let output = run_git ( & self . shadow_repo_path , None , & [ "diff" , "--name-status" , from, to] ) ?;
227243
228244 let mut stats = FileStats :: default ( ) ;
229245 for line in String :: from_utf8_lossy ( & output. stdout ) . lines ( ) {
@@ -246,7 +262,7 @@ impl CheckpointManager {
246262 let mut result = String :: new ( ) ;
247263
248264 // Get file changes
249- let output = run_git ( & self . shadow_repo_path , false , & [ "diff" , "--name-status" , from, to] ) ?;
265+ let output = run_git ( & self . shadow_repo_path , None , & [ "diff" , "--name-status" , from, to] ) ?;
250266
251267 for line in String :: from_utf8_lossy ( & output. stdout ) . lines ( ) {
252268 if let Some ( ( status, file) ) = line. split_once ( '\t' ) {
@@ -261,7 +277,7 @@ impl CheckpointManager {
261277 }
262278
263279 // Add statistics
264- let stat_output = run_git ( & self . shadow_repo_path , false , & [
280+ let stat_output = run_git ( & self . shadow_repo_path , None , & [
265281 "diff" ,
266282 from,
267283 to,
@@ -279,7 +295,10 @@ impl CheckpointManager {
279295
280296 /// Check for uncommitted changes
281297 pub fn has_changes ( & self ) -> Result < bool > {
282- let output = run_git ( & self . shadow_repo_path , true , & [ "status" , "--porcelain" ] ) ?;
298+ let output = run_git ( & self . shadow_repo_path , Some ( & self . work_tree_path ) , & [
299+ "status" ,
300+ "--porcelain" ,
301+ ] ) ?;
283302 Ok ( !output. stdout . is_empty ( ) )
284303 }
285304
@@ -351,18 +370,18 @@ fn is_in_git_repo() -> bool {
351370}
352371
353372fn configure_git ( shadow_path : & str ) -> Result < ( ) > {
354- run_git ( Path :: new ( shadow_path) , false , & [ "config" , "user.name" , "Q" ] ) ?;
355- run_git ( Path :: new ( shadow_path) , false , & [ "config" , "user.email" , "qcli@local" ] ) ?;
356- run_git ( Path :: new ( shadow_path) , false , & [ "config" , "core.preloadindex" , "true" ] ) ?;
373+ run_git ( Path :: new ( shadow_path) , None , & [ "config" , "user.name" , "Q" ] ) ?;
374+ run_git ( Path :: new ( shadow_path) , None , & [ "config" , "user.email" , "qcli@local" ] ) ?;
375+ run_git ( Path :: new ( shadow_path) , None , & [ "config" , "core.preloadindex" , "true" ] ) ?;
357376 Ok ( ( ) )
358377}
359378
360- fn stage_commit_tag ( shadow_path : & str , message : & str , tag : & str ) -> Result < ( ) > {
379+ fn stage_commit_tag ( shadow_path : & str , work_tree : & Path , message : & str , tag : & str ) -> Result < ( ) > {
361380 // Stage all changes
362- run_git ( Path :: new ( shadow_path) , true , & [ "add" , "-A" ] ) ?;
381+ run_git ( Path :: new ( shadow_path) , Some ( work_tree ) , & [ "add" , "-A" ] ) ?;
363382
364383 // Commit
365- let output = run_git ( Path :: new ( shadow_path) , true , & [
384+ let output = run_git ( Path :: new ( shadow_path) , Some ( work_tree ) , & [
366385 "commit" ,
367386 "--allow-empty" ,
368387 "--no-verify" ,
@@ -375,20 +394,20 @@ fn stage_commit_tag(shadow_path: &str, message: &str, tag: &str) -> Result<()> {
375394 }
376395
377396 // Tag
378- let output = run_git ( Path :: new ( shadow_path) , false , & [ "tag" , tag] ) ?;
397+ let output = run_git ( Path :: new ( shadow_path) , None , & [ "tag" , tag] ) ?;
379398 if !output. status . success ( ) {
380399 bail ! ( "Git tag failed: {}" , String :: from_utf8_lossy( & output. stderr) ) ;
381400 }
382401
383402 Ok ( ( ) )
384403}
385404
386- fn run_git ( dir : & Path , with_work_tree : bool , args : & [ & str ] ) -> Result < Output > {
405+ fn run_git ( dir : & Path , work_tree : Option < & Path > , args : & [ & str ] ) -> Result < Output > {
387406 let mut cmd = Command :: new ( "git" ) ;
388407 cmd. arg ( format ! ( "--git-dir={}" , dir. display( ) ) ) ;
389408
390- if with_work_tree {
391- cmd. arg ( "--work-tree=." ) ;
409+ if let Some ( work_tree_path ) = work_tree {
410+ cmd. arg ( format ! ( "--work-tree={}" , work_tree_path . display ( ) ) ) ;
392411 }
393412
394413 cmd. args ( args) ;
0 commit comments