diff --git a/internal/git/gitdir.go b/internal/git/gitdir.go index e29e49b1..e052208c 100644 --- a/internal/git/gitdir.go +++ b/internal/git/gitdir.go @@ -149,8 +149,13 @@ func GetMainRepoRoot() (string, error) { if err != nil { return "", err } - // The main repo root is the parent of the .git directory (commonDir) - return filepath.Dir(ctx.commonDir), nil + if ctx.isWorktree { + // For worktrees, the main repo root is the parent of the shared .git directory. + return filepath.Dir(ctx.commonDir), nil + } + + // For regular repos (including submodules), repoRoot is the correct root. + return ctx.repoRoot, nil } // GetRepoRoot returns the root directory of the current git repository. @@ -196,4 +201,4 @@ func NormalizePath(path string) string { func ResetCaches() { gitCtxOnce = sync.Once{} gitCtx = gitContext{} -} \ No newline at end of file +} diff --git a/internal/git/worktree_test.go b/internal/git/worktree_test.go index 1cc37d72..39c6ba2f 100644 --- a/internal/git/worktree_test.go +++ b/internal/git/worktree_test.go @@ -894,6 +894,54 @@ func TestGetMainRepoRoot(t *testing.T) { } }) + t.Run("returns correct root for submodule repo", func(t *testing.T) { + ResetCaches() // Reset caches from previous subtests + superRepoPath, superCleanup := setupTestRepo(t) + defer superCleanup() + + submoduleRepoPath, submoduleCleanup := setupTestRepo(t) + defer submoduleCleanup() + + addCmd := exec.Command("git", "-c", "protocol.file.allow=always", "submodule", "add", submoduleRepoPath, "core") + addCmd.Dir = superRepoPath + if output, err := addCmd.CombinedOutput(); err != nil { + t.Fatalf("Failed to add submodule: %v\nOutput: %s", err, string(output)) + } + + commitCmd := exec.Command("git", "commit", "-m", "Add submodule") + commitCmd.Dir = superRepoPath + if output, err := commitCmd.CombinedOutput(); err != nil { + t.Fatalf("Failed to commit submodule: %v\nOutput: %s", err, string(output)) + } + + submodulePath := filepath.Join(superRepoPath, "core") + + // Save current dir and change to submodule + originalDir, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get current dir: %v", err) + } + defer func() { _ = os.Chdir(originalDir) }() + + if err := os.Chdir(submodulePath); err != nil { + t.Fatalf("Failed to chdir to submodule: %v", err) + } + ResetCaches() // Reset after chdir + + root, err := GetMainRepoRoot() + if err != nil { + t.Fatalf("GetMainRepoRoot failed: %v", err) + } + + // Resolve symlinks for comparison (e.g., /tmp -> /private/tmp on macOS) + expectedRoot, _ := filepath.EvalSymlinks(submodulePath) + actualRoot, _ := filepath.EvalSymlinks(root) + + if actualRoot != expectedRoot { + t.Errorf("GetMainRepoRoot() = %s, want %s (submodule repo)", actualRoot, expectedRoot) + } + }) + t.Run("returns main repo root from worktree", func(t *testing.T) { ResetCaches() // Reset caches from previous subtests repoPath, cleanup := setupTestRepo(t)