From ce2cfc5b413e494667fc87f88a8ec084a22ac5e2 Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Thu, 9 Apr 2026 16:37:13 -0700 Subject: [PATCH 1/2] fix: resolve repo root from subdirectories --- lib/config.sh | 42 ++++++++++++++++++++++------------ lib/core.sh | 18 ++------------- tests/cmd_list.bats | 9 ++++++++ tests/config.bats | 31 +++++++++++++++++++++++++ tests/core_resolve_target.bats | 9 ++++++++ 5 files changed, 79 insertions(+), 30 deletions(-) diff --git a/lib/config.sh b/lib/config.sh index f1935f9..4c1a19e 100644 --- a/lib/config.sh +++ b/lib/config.sh @@ -10,24 +10,38 @@ # 5. Environment variables # 6. Fallback values +# Resolve the main repo root from the current git context. +# Works from the main repo root, a subdirectory, or a linked worktree. +# Returns: absolute path to main repo root or empty on failure +_resolve_main_repo_root() { + local git_common_dir repo_root + git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || return 1 + + [ -n "$git_common_dir" ] || return 1 + + case "$git_common_dir" in + /*) + repo_root="${git_common_dir%/.git}" + ;; + *) + repo_root=$( + unset CDPATH + cd -P -- "$git_common_dir/.." 2>/dev/null && pwd -P + ) || return 1 + ;; + esac + + [ -n "$repo_root" ] || return 1 + printf "%s" "$repo_root" +} + # Get the path to .gtrconfig file in main repo root # Usage: _gtrconfig_path # Returns: path to .gtrconfig or empty if not in a repo -# Note: Uses --git-common-dir to find main repo even from worktrees +# Note: Uses _resolve_main_repo_root to find main repo even from worktrees/subdirectories _gtrconfig_path() { - local git_common_dir repo_root - git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || return 0 - - # git-common-dir returns: - # - ".git" when in main repo (relative) - # - "/absolute/path/to/repo/.git" when in worktree (absolute) - if [ "$git_common_dir" = ".git" ]; then - # In main repo - use show-toplevel - repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || return 0 - else - # In worktree - strip /.git suffix from absolute path - repo_root="${git_common_dir%/.git}" - fi + local repo_root + repo_root=$(_resolve_main_repo_root) || return 0 printf "%s/.gtrconfig" "$repo_root" } diff --git a/lib/core.sh b/lib/core.sh index 37a53e4..399f3f6 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -15,22 +15,8 @@ declare _ctx_is_main _ctx_worktree_path _ctx_branch # Returns: absolute path to main repo root # Exit code: 0 on success, 1 if not in a git repo discover_repo_root() { - local root git_common_dir - git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null) - - if [ -z "$git_common_dir" ]; then - log_error "Not in a git repository" - return 1 - fi - - # --git-common-dir returns: - # ".git" (relative) when in the main repo - # "/absolute/path/to/repo/.git" when in a worktree - if [ "$git_common_dir" = ".git" ]; then - root=$(git rev-parse --show-toplevel 2>/dev/null) - else - root="${git_common_dir%/.git}" - fi + local root + root=$(_resolve_main_repo_root) if [ -z "$root" ]; then log_error "Not in a git repository" diff --git a/tests/cmd_list.bats b/tests/cmd_list.bats index 2b37693..bc96502 100644 --- a/tests/cmd_list.bats +++ b/tests/cmd_list.bats @@ -76,3 +76,12 @@ teardown() { [[ "$output" == *"$TEST_REPO"* ]] [[ "$output" == *"wt-porcelain"* ]] } + +@test "cmd_list from a repo subdirectory shows the main repo root" { + mkdir -p "$TEST_REPO/subdir/nested" + cd "$TEST_REPO/subdir/nested" + run cmd_list + [ "$status" -eq 0 ] + [[ "$output" == *"$TEST_REPO"* ]] + [[ "$output" != *"subdir/..-worktrees"* ]] +} diff --git a/tests/config.bats b/tests/config.bats index e8abb5f..8d85f61 100644 --- a/tests/config.bats +++ b/tests/config.bats @@ -6,6 +6,13 @@ setup() { source "$PROJECT_ROOT/lib/config.sh" } +teardown() { + if [ -n "${TEST_REPO:-}" ]; then + teardown_integration_repo + unset TEST_REPO TEST_WORKTREES_DIR + fi +} + # ── Key mapping ────────────────────────────────────────────────────────────── @test "cfg_map_to_file_key maps gtr.copy.include to copy.include" { @@ -125,3 +132,27 @@ setup() { [[ "$result" == *"vscode"* ]] [[ "$result" == *"[local]"* ]] } + +# ── Repo context integration ───────────────────────────────────────────────── + +@test "_resolve_main_repo_root returns the repo root from a subdirectory" { + setup_integration_repo + mkdir -p "$TEST_REPO/subdir/nested" + cd "$TEST_REPO/subdir/nested" + local expected + expected=$(cd "$TEST_REPO" && pwd -P) + + result=$(_resolve_main_repo_root) + [ "$result" = "$expected" ] +} + +@test "_gtrconfig_path points at the repo root from a subdirectory" { + setup_integration_repo + mkdir -p "$TEST_REPO/subdir/nested" + cd "$TEST_REPO/subdir/nested" + local expected + expected="$(cd "$TEST_REPO" && pwd -P)/.gtrconfig" + + result=$(_gtrconfig_path) + [ "$result" = "$expected" ] +} diff --git a/tests/core_resolve_target.bats b/tests/core_resolve_target.bats index 7d8f9b3..6320ddd 100644 --- a/tests/core_resolve_target.bats +++ b/tests/core_resolve_target.bats @@ -112,3 +112,12 @@ teardown() { expected=$(pwd -P) [ "$root" = "$expected" ] } + +@test "discover_repo_root returns main repo root when called from a repo subdirectory" { + mkdir -p "$TEST_REPO/subdir/nested" + cd "$TEST_REPO/subdir/nested" + local root expected + root=$(discover_repo_root) + expected=$(cd "$TEST_REPO" && pwd -P) + [ "$root" = "$expected" ] +} From 5dd7fa4113c99625efddc4c459650d3dc32cb7d2 Mon Sep 17 00:00:00 2001 From: Tom Elizaga Date: Thu, 9 Apr 2026 16:49:33 -0700 Subject: [PATCH 2/2] fix: guard discover_repo_root under set -e --- lib/core.sh | 4 +--- tests/core_resolve_target.bats | 11 +++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/core.sh b/lib/core.sh index 399f3f6..5bd4dba 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -16,9 +16,7 @@ declare _ctx_is_main _ctx_worktree_path _ctx_branch # Exit code: 0 on success, 1 if not in a git repo discover_repo_root() { local root - root=$(_resolve_main_repo_root) - - if [ -z "$root" ]; then + if ! root=$(_resolve_main_repo_root); then log_error "Not in a git repository" return 1 fi diff --git a/tests/core_resolve_target.bats b/tests/core_resolve_target.bats index 6320ddd..1ab1a3f 100644 --- a/tests/core_resolve_target.bats +++ b/tests/core_resolve_target.bats @@ -121,3 +121,14 @@ teardown() { expected=$(cd "$TEST_REPO" && pwd -P) [ "$root" = "$expected" ] } + +@test "discover_repo_root returns 1 outside a git repository" { + local outside_repo + outside_repo=$(mktemp -d) + cd "$outside_repo" + + run discover_repo_root + [ "$status" -eq 1 ] + + rm -rf "$outside_repo" +}