Skip to content

Commit 57e7167

Browse files
authored
fix: resolve main repo root correctly from inside worktrees (#126)
fix: resolve main repo root correctly from inside worktrees (#123) discover_repo_root() used git rev-parse --show-toplevel which returns the worktree's own root when called from inside a worktree. Switch to --git-common-dir to always resolve the true main repo root, fixing list, go 1, and all other commands when run from a worktree.
1 parent 154b83c commit 57e7167

File tree

4 files changed

+65
-4
lines changed

4 files changed

+65
-4
lines changed

lib/core.sh

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,27 @@
1010
declare _ctx_repo_root _ctx_base_dir _ctx_prefix
1111
declare _ctx_is_main _ctx_worktree_path _ctx_branch
1212

13-
# Discover the root of the current git repository
14-
# Returns: absolute path to repo root
13+
# Discover the root of the main git repository
14+
# Works correctly from both the main repo and from inside worktrees.
15+
# Returns: absolute path to main repo root
1516
# Exit code: 0 on success, 1 if not in a git repo
1617
discover_repo_root() {
17-
local root
18-
root=$(git rev-parse --show-toplevel 2>/dev/null)
18+
local root git_common_dir
19+
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null)
20+
21+
if [ -z "$git_common_dir" ]; then
22+
log_error "Not in a git repository"
23+
return 1
24+
fi
25+
26+
# --git-common-dir returns:
27+
# ".git" (relative) when in the main repo
28+
# "/absolute/path/to/repo/.git" when in a worktree
29+
if [ "$git_common_dir" = ".git" ]; then
30+
root=$(git rev-parse --show-toplevel 2>/dev/null)
31+
else
32+
root="${git_common_dir%/.git}"
33+
fi
1934

2035
if [ -z "$root" ]; then
2136
log_error "Not in a git repository"

tests/cmd_go.bats

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,10 @@ teardown() {
5151
# stderr should contain human-readable info
5252
[[ "$stderr" == *"Worktree"* ]] || [[ "$stderr" == *"Branch"* ]]
5353
}
54+
55+
@test "cmd_go 1 from inside a worktree resolves to main repo" {
56+
cd "$TEST_WORKTREES_DIR/go-test"
57+
run cmd_go 1
58+
[ "$status" -eq 0 ]
59+
[[ "$output" == *"$TEST_REPO"* ]]
60+
}

tests/cmd_list.bats

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,22 @@ teardown() {
5757
[[ "$output" == *"BRANCH"* ]]
5858
[[ "$output" == *"PATH"* ]]
5959
}
60+
61+
@test "cmd_list from inside a worktree shows all worktrees" {
62+
create_test_worktree "wt-inside"
63+
cd "$TEST_WORKTREES_DIR/wt-inside"
64+
run cmd_list
65+
[ "$status" -eq 0 ]
66+
[[ "$output" == *"[main repo]"* ]]
67+
[[ "$output" == *"wt-inside"* ]]
68+
[[ "$output" == *"$TEST_REPO"* ]]
69+
}
70+
71+
@test "cmd_list --porcelain from inside a worktree includes main repo" {
72+
create_test_worktree "wt-porcelain"
73+
cd "$TEST_WORKTREES_DIR/wt-porcelain"
74+
local output
75+
output=$(cmd_list --porcelain)
76+
[[ "$output" == *"$TEST_REPO"* ]]
77+
[[ "$output" == *"wt-porcelain"* ]]
78+
}

tests/core_resolve_target.bats

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,23 @@ teardown() {
8080
run resolve_worktree "nope" "$TEST_REPO" "$TEST_WORKTREES_DIR" ""
8181
[ "$status" -eq 1 ]
8282
}
83+
84+
# ── discover_repo_root from worktree ──────────────────────────────────────────
85+
86+
@test "discover_repo_root returns main repo root when called from a worktree" {
87+
create_test_worktree "inside-wt"
88+
cd "$TEST_WORKTREES_DIR/inside-wt"
89+
local root expected
90+
root=$(discover_repo_root)
91+
# Resolve symlinks (macOS: /var -> /private/var) for comparison
92+
expected=$(cd "$TEST_REPO" && pwd -P)
93+
[ "$root" = "$expected" ]
94+
}
95+
96+
@test "discover_repo_root returns main repo root when called from main repo" {
97+
cd "$TEST_REPO"
98+
local root expected
99+
root=$(discover_repo_root)
100+
expected=$(pwd -P)
101+
[ "$root" = "$expected" ]
102+
}

0 commit comments

Comments
 (0)