diff --git a/lib/core.sh b/lib/core.sh index 5bd4266..9622120 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -10,12 +10,27 @@ declare _ctx_repo_root _ctx_base_dir _ctx_prefix declare _ctx_is_main _ctx_worktree_path _ctx_branch -# Discover the root of the current git repository -# Returns: absolute path to repo root +# Discover the root of the main git repository +# Works correctly from both the main repo and from inside worktrees. +# Returns: absolute path to main repo root # Exit code: 0 on success, 1 if not in a git repo discover_repo_root() { - local root - root=$(git rev-parse --show-toplevel 2>/dev/null) + 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 if [ -z "$root" ]; then log_error "Not in a git repository" diff --git a/tests/cmd_go.bats b/tests/cmd_go.bats index 166fb39..8db87a3 100644 --- a/tests/cmd_go.bats +++ b/tests/cmd_go.bats @@ -51,3 +51,10 @@ teardown() { # stderr should contain human-readable info [[ "$stderr" == *"Worktree"* ]] || [[ "$stderr" == *"Branch"* ]] } + +@test "cmd_go 1 from inside a worktree resolves to main repo" { + cd "$TEST_WORKTREES_DIR/go-test" + run cmd_go 1 + [ "$status" -eq 0 ] + [[ "$output" == *"$TEST_REPO"* ]] +} diff --git a/tests/cmd_list.bats b/tests/cmd_list.bats index e652bc4..2b37693 100644 --- a/tests/cmd_list.bats +++ b/tests/cmd_list.bats @@ -57,3 +57,22 @@ teardown() { [[ "$output" == *"BRANCH"* ]] [[ "$output" == *"PATH"* ]] } + +@test "cmd_list from inside a worktree shows all worktrees" { + create_test_worktree "wt-inside" + cd "$TEST_WORKTREES_DIR/wt-inside" + run cmd_list + [ "$status" -eq 0 ] + [[ "$output" == *"[main repo]"* ]] + [[ "$output" == *"wt-inside"* ]] + [[ "$output" == *"$TEST_REPO"* ]] +} + +@test "cmd_list --porcelain from inside a worktree includes main repo" { + create_test_worktree "wt-porcelain" + cd "$TEST_WORKTREES_DIR/wt-porcelain" + local output + output=$(cmd_list --porcelain) + [[ "$output" == *"$TEST_REPO"* ]] + [[ "$output" == *"wt-porcelain"* ]] +} diff --git a/tests/core_resolve_target.bats b/tests/core_resolve_target.bats index 961a356..9ad196d 100644 --- a/tests/core_resolve_target.bats +++ b/tests/core_resolve_target.bats @@ -80,3 +80,23 @@ teardown() { run resolve_worktree "nope" "$TEST_REPO" "$TEST_WORKTREES_DIR" "" [ "$status" -eq 1 ] } + +# ── discover_repo_root from worktree ────────────────────────────────────────── + +@test "discover_repo_root returns main repo root when called from a worktree" { + create_test_worktree "inside-wt" + cd "$TEST_WORKTREES_DIR/inside-wt" + local root expected + root=$(discover_repo_root) + # Resolve symlinks (macOS: /var -> /private/var) for comparison + expected=$(cd "$TEST_REPO" && pwd -P) + [ "$root" = "$expected" ] +} + +@test "discover_repo_root returns main repo root when called from main repo" { + cd "$TEST_REPO" + local root expected + root=$(discover_repo_root) + expected=$(pwd -P) + [ "$root" = "$expected" ] +}