@@ -118,12 +118,27 @@ public async Task GitWorkspaceService_should_list_create_and_prune_worktrees()
118118 var pruned = await service . PruneWorktreesAsync ( "/repo" , CancellationToken . None ) ;
119119
120120 initial . Worktrees . Should ( ) . HaveCount ( 2 ) ;
121- created . Worktree . Path . Should ( ) . Be ( "/repo-new" ) ;
121+ created . Worktree . Path . Should ( ) . Be ( Path . GetFullPath ( Path . Combine ( "/repo" , "../repo -new") ) ) ;
122122 created . Worktree . Branch . Should ( ) . Be ( "feature/new" ) ;
123123 pruned . PrunedCount . Should ( ) . Be ( 1 ) ;
124124 pruned . Worktrees . Should ( ) . NotContain ( entry => entry . IsPrunable ) ;
125125 }
126126
127+ /// <summary>
128+ /// Ensures linked worktrees resolve the main worktree path from git-common-dir output.
129+ /// </summary>
130+ [ Fact ]
131+ public async Task GitWorkspaceService_should_detect_linked_worktree_main_path ( )
132+ {
133+ IGitWorkspaceService service = new GitWorkspaceService ( new LinkedWorktreeProcessRunner ( ) ) ;
134+
135+ var snapshot = await service . GetSnapshotAsync ( "/repo-linked" , CancellationToken . None ) ;
136+
137+ snapshot . IsLinkedWorktree . Should ( ) . BeTrue ( ) ;
138+ snapshot . MainWorktreePath . Should ( ) . Be ( "/repo" ) ;
139+ snapshot . WorktreeCount . Should ( ) . Be ( 2 ) ;
140+ }
141+
127142 private static string CreateTemporaryWorkspace ( )
128143 {
129144 var workspacePath = Path . Combine ( Path . GetTempPath ( ) , "sharpclaw-memory-skill-git-tests" , Guid . NewGuid ( ) . ToString ( "N" ) ) ;
@@ -203,4 +218,34 @@ private string RenderWorktreeList()
203218
204219 private sealed record WorktreeState ( string Branch , string Head , bool IsPrunable ) ;
205220 }
221+
222+ private sealed class LinkedWorktreeProcessRunner : IProcessRunner
223+ {
224+ public Task < ProcessRunResult > RunAsync ( ProcessRunRequest request , CancellationToken cancellationToken )
225+ {
226+ var output = request . Arguments switch
227+ {
228+ [ "rev-parse" , "--show-toplevel" ] => "/repo-linked\n " ,
229+ [ "rev-parse" , "--path-format=absolute" , "--git-common-dir" ] => "/repo/.git/worktrees/repo-linked\n " ,
230+ [ "branch" , "--show-current" ] => "feature/worktrees\n " ,
231+ [ "rev-parse" , "HEAD" ] => "def456\n " ,
232+ [ "status" , "--porcelain=v1" , "--branch" ] => "## feature/worktrees\n " ,
233+ [ "diff" , "--no-ext-diff" , "--stat" ] => string . Empty ,
234+ [ "rev-list" , "--left-right" , "--count" , "@{upstream}...HEAD" ] => "0\t 0\n " ,
235+ [ "worktree" , "list" , "--porcelain" ] => """
236+ worktree /repo
237+ HEAD abc123
238+ branch refs/heads/main
239+
240+ worktree /repo-linked
241+ HEAD def456
242+ branch refs/heads/feature/worktrees
243+
244+ """ ,
245+ _ => throw new InvalidOperationException ( $ "Unexpected git command: { string . Join ( ' ' , request . Arguments ) } ")
246+ } ;
247+
248+ return Task . FromResult ( new ProcessRunResult ( 0 , output , string . Empty , DateTimeOffset . UtcNow , DateTimeOffset . UtcNow ) ) ;
249+ }
250+ }
206251}
0 commit comments