Skip to content

Commit f079e1c

Browse files
authored
fix repo root resolution from subdirectories (#166)
* fix: resolve repo root from subdirectories * fix: guard discover_repo_root under set -e
1 parent c765e62 commit f079e1c

File tree

5 files changed

+90
-32
lines changed

5 files changed

+90
-32
lines changed

lib/config.sh

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,38 @@
1010
# 5. Environment variables
1111
# 6. Fallback values
1212

13+
# Resolve the main repo root from the current git context.
14+
# Works from the main repo root, a subdirectory, or a linked worktree.
15+
# Returns: absolute path to main repo root or empty on failure
16+
_resolve_main_repo_root() {
17+
local git_common_dir repo_root
18+
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || return 1
19+
20+
[ -n "$git_common_dir" ] || return 1
21+
22+
case "$git_common_dir" in
23+
/*)
24+
repo_root="${git_common_dir%/.git}"
25+
;;
26+
*)
27+
repo_root=$(
28+
unset CDPATH
29+
cd -P -- "$git_common_dir/.." 2>/dev/null && pwd -P
30+
) || return 1
31+
;;
32+
esac
33+
34+
[ -n "$repo_root" ] || return 1
35+
printf "%s" "$repo_root"
36+
}
37+
1338
# Get the path to .gtrconfig file in main repo root
1439
# Usage: _gtrconfig_path
1540
# Returns: path to .gtrconfig or empty if not in a repo
16-
# Note: Uses --git-common-dir to find main repo even from worktrees
41+
# Note: Uses _resolve_main_repo_root to find main repo even from worktrees/subdirectories
1742
_gtrconfig_path() {
18-
local git_common_dir repo_root
19-
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || return 0
20-
21-
# git-common-dir returns:
22-
# - ".git" when in main repo (relative)
23-
# - "/absolute/path/to/repo/.git" when in worktree (absolute)
24-
if [ "$git_common_dir" = ".git" ]; then
25-
# In main repo - use show-toplevel
26-
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || return 0
27-
else
28-
# In worktree - strip /.git suffix from absolute path
29-
repo_root="${git_common_dir%/.git}"
30-
fi
43+
local repo_root
44+
repo_root=$(_resolve_main_repo_root) || return 0
3145

3246
printf "%s/.gtrconfig" "$repo_root"
3347
}

lib/core.sh

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,8 @@ declare _ctx_is_main _ctx_worktree_path _ctx_branch
1515
# Returns: absolute path to main repo root
1616
# Exit code: 0 on success, 1 if not in a git repo
1717
discover_repo_root() {
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
34-
35-
if [ -z "$root" ]; then
18+
local root
19+
if ! root=$(_resolve_main_repo_root); then
3620
log_error "Not in a git repository"
3721
return 1
3822
fi

tests/cmd_list.bats

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,12 @@ teardown() {
7676
[[ "$output" == *"$TEST_REPO"* ]]
7777
[[ "$output" == *"wt-porcelain"* ]]
7878
}
79+
80+
@test "cmd_list from a repo subdirectory shows the main repo root" {
81+
mkdir -p "$TEST_REPO/subdir/nested"
82+
cd "$TEST_REPO/subdir/nested"
83+
run cmd_list
84+
[ "$status" -eq 0 ]
85+
[[ "$output" == *"$TEST_REPO"* ]]
86+
[[ "$output" != *"subdir/..-worktrees"* ]]
87+
}

tests/config.bats

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ setup() {
66
source "$PROJECT_ROOT/lib/config.sh"
77
}
88

9+
teardown() {
10+
if [ -n "${TEST_REPO:-}" ]; then
11+
teardown_integration_repo
12+
unset TEST_REPO TEST_WORKTREES_DIR
13+
fi
14+
}
15+
916
# ── Key mapping ──────────────────────────────────────────────────────────────
1017

1118
@test "cfg_map_to_file_key maps gtr.copy.include to copy.include" {
@@ -125,3 +132,27 @@ setup() {
125132
[[ "$result" == *"vscode"* ]]
126133
[[ "$result" == *"[local]"* ]]
127134
}
135+
136+
# ── Repo context integration ─────────────────────────────────────────────────
137+
138+
@test "_resolve_main_repo_root returns the repo root from a subdirectory" {
139+
setup_integration_repo
140+
mkdir -p "$TEST_REPO/subdir/nested"
141+
cd "$TEST_REPO/subdir/nested"
142+
local expected
143+
expected=$(cd "$TEST_REPO" && pwd -P)
144+
145+
result=$(_resolve_main_repo_root)
146+
[ "$result" = "$expected" ]
147+
}
148+
149+
@test "_gtrconfig_path points at the repo root from a subdirectory" {
150+
setup_integration_repo
151+
mkdir -p "$TEST_REPO/subdir/nested"
152+
cd "$TEST_REPO/subdir/nested"
153+
local expected
154+
expected="$(cd "$TEST_REPO" && pwd -P)/.gtrconfig"
155+
156+
result=$(_gtrconfig_path)
157+
[ "$result" = "$expected" ]
158+
}

tests/core_resolve_target.bats

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,23 @@ teardown() {
112112
expected=$(pwd -P)
113113
[ "$root" = "$expected" ]
114114
}
115+
116+
@test "discover_repo_root returns main repo root when called from a repo subdirectory" {
117+
mkdir -p "$TEST_REPO/subdir/nested"
118+
cd "$TEST_REPO/subdir/nested"
119+
local root expected
120+
root=$(discover_repo_root)
121+
expected=$(cd "$TEST_REPO" && pwd -P)
122+
[ "$root" = "$expected" ]
123+
}
124+
125+
@test "discover_repo_root returns 1 outside a git repository" {
126+
local outside_repo
127+
outside_repo=$(mktemp -d)
128+
cd "$outside_repo"
129+
130+
run discover_repo_root
131+
[ "$status" -eq 1 ]
132+
133+
rm -rf "$outside_repo"
134+
}

0 commit comments

Comments
 (0)