diff --git a/README.md b/README.md index d1a7ff4..5255fce 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ git gtr new my-feature --name descriptive-variant - `--from `: Create from specific ref - `--from-current`: Create from current branch (useful for parallel variant work) +- `--remote `: Remote used for default base refs - `--track `: Tracking mode (auto|remote|local|none) - `--no-copy`: Skip file copying - `--no-fetch`: Skip git fetch @@ -388,6 +389,7 @@ git gtr config set gtr.ui.color never [defaults] editor = cursor ai = claude + remote = upstream ``` **Configuration precedence** (highest to lowest): diff --git a/completions/_git-gtr b/completions/_git-gtr index e0a5ba3..40044a1 100644 --- a/completions/_git-gtr +++ b/completions/_git-gtr @@ -61,6 +61,7 @@ _git-gtr() { '1:branch name:' \ '--from[Base ref]:ref:' \ '--from-current[Create from current branch]' \ + '--remote[Remote used for default base refs]:remote:' \ '--track[Track mode]:mode:(auto remote local none)' \ '--no-copy[Skip file copying]' \ '--no-fetch[Skip git fetch]' \ @@ -183,7 +184,7 @@ _git-gtr() { '--local[Use local git config]' \ '--global[Use global git config]' \ '--system[Use system git config]' \ - '*:config key:(gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.provider gtr.ui.color)' + '*:config key:(gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.defaultRemote gtr.provider gtr.ui.color)' ;; set|add|unset) # Write operations only support --local and --global @@ -191,7 +192,7 @@ _git-gtr() { _arguments \ '--local[Use local git config]' \ '--global[Use global git config]' \ - '*:config key:(gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.provider gtr.ui.color)' + '*:config key:(gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.defaultRemote gtr.provider gtr.ui.color)' ;; esac fi diff --git a/completions/git-gtr.fish b/completions/git-gtr.fish index 7fdc786..0c3f10f 100644 --- a/completions/git-gtr.fish +++ b/completions/git-gtr.fish @@ -60,6 +60,7 @@ complete -f -c git -n '__fish_git_gtr_needs_command' -a help -d 'Show help' # New command options complete -c git -n '__fish_git_gtr_using_command new' -l from -d 'Base ref' -r complete -c git -n '__fish_git_gtr_using_command new' -l from-current -d 'Create from current branch' +complete -c git -n '__fish_git_gtr_using_command new' -l remote -d 'Remote used for default base refs' -r complete -c git -n '__fish_git_gtr_using_command new' -l track -d 'Track mode' -r -a 'auto remote local none' complete -c git -n '__fish_git_gtr_using_command new' -l no-copy -d 'Skip file copying' complete -c git -n '__fish_git_gtr_using_command new' -l no-fetch -d 'Skip git fetch' @@ -140,6 +141,7 @@ complete -f -c git -n '__fish_git_gtr_using_command config' -a " gtr.worktrees.dir 'Worktrees base directory' gtr.worktrees.prefix 'Worktree folder prefix' gtr.defaultBranch 'Default branch' + gtr.defaultRemote 'Default remote' gtr.provider 'Hosting provider (github, gitlab)' gtr.ui.color 'Color output mode (auto, always, never)' " diff --git a/completions/gtr.bash b/completions/gtr.bash index 13314cf..4b939cb 100644 --- a/completions/gtr.bash +++ b/completions/gtr.bash @@ -99,7 +99,7 @@ _git_gtr() { new) # Complete flags if [[ "$cur" == -* ]]; then - COMPREPLY=($(compgen -W "--from --from-current --track --no-copy --no-fetch --no-hooks --force --name --folder --yes --editor -e --ai -a" -- "$cur")) + COMPREPLY=($(compgen -W "--from --from-current --remote --track --no-copy --no-fetch --no-hooks --force --name --folder --yes --editor -e --ai -a" -- "$cur")) elif [ "$prev" = "--track" ]; then COMPREPLY=($(compgen -W "auto remote local none" -- "$cur")) fi @@ -138,7 +138,7 @@ _git_gtr() { if [[ "$cur" == -* ]]; then COMPREPLY=($(compgen -W "--local --global --system" -- "$cur")) else - COMPREPLY=($(compgen -W "gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.provider gtr.ui.color" -- "$cur")) + COMPREPLY=($(compgen -W "gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.defaultRemote gtr.provider gtr.ui.color" -- "$cur")) fi ;; set|add|unset) @@ -146,7 +146,7 @@ _git_gtr() { if [[ "$cur" == -* ]]; then COMPREPLY=($(compgen -W "--local --global" -- "$cur")) else - COMPREPLY=($(compgen -W "gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.provider gtr.ui.color" -- "$cur")) + COMPREPLY=($(compgen -W "gtr.copy.include gtr.copy.exclude gtr.copy.includeDirs gtr.copy.excludeDirs gtr.hook.postCreate gtr.hook.preRemove gtr.hook.postRemove gtr.hook.postCd gtr.editor.default gtr.editor.workspace gtr.ai.default gtr.worktrees.dir gtr.worktrees.prefix gtr.defaultBranch gtr.defaultRemote gtr.provider gtr.ui.color" -- "$cur")) fi ;; esac diff --git a/docs/configuration.md b/docs/configuration.md index fba6ea5..261f905 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -62,6 +62,7 @@ Create a `.gtrconfig` file in your repository root to share configuration across [defaults] editor = cursor ai = claude + remote = upstream ``` > [!TIP] @@ -92,6 +93,9 @@ gtr.worktrees.prefix = dev- # Default branch (default: auto-detect) gtr.defaultBranch = main + +# Default remote for fetches, tracking, and default base refs (default: origin) +gtr.defaultRemote = upstream ``` > [!IMPORTANT] @@ -440,6 +444,7 @@ git gtr config add gtr.hook.postCreate "pnpm run build" # Set global preferences git gtr config set gtr.editor.default cursor --global git gtr config set gtr.ai.default claude --global +git gtr config set gtr.defaultRemote upstream --global ``` --- @@ -454,6 +459,7 @@ git gtr config set gtr.ai.default claude --global | `GTR_EDITOR_CMD_NAME` | First word of `GTR_EDITOR_CMD` for availability checks | None | | `GTR_AI_CMD` | Custom AI tool command (e.g., `copilot`) | None | | `GTR_AI_CMD_NAME` | First word of `GTR_AI_CMD` for availability checks | None | +| `GTR_DEFAULT_REMOTE` | Remote used for default base refs and tracking | `origin` | | `GTR_COLOR` | Override color output (`always`, `never`, `auto`) | `auto` | | `GTR_PROVIDER` | Override hosting provider (`github` or `gitlab`) | Auto-detected from URL | | `NO_COLOR` | Disable color output when set ([no-color.org](https://no-color.org)) | Unset | diff --git a/lib/commands/create.sh b/lib/commands/create.sh index bb991bb..917861a 100644 --- a/lib/commands/create.sh +++ b/lib/commands/create.sh @@ -63,22 +63,22 @@ _post_create_next_steps() { } # Determine the base ref for worktree creation -# Usage: _create_resolve_from_ref +# Usage: _create_resolve_from_ref [remote] # Prints: resolved ref _create_resolve_from_ref() { - local from_ref="$1" from_current="$2" repo_root="$3" + local from_ref="$1" from_current="$2" repo_root="$3" remote="${4:-$(resolve_default_remote)}" if [ -z "$from_ref" ]; then if [ "$from_current" -eq 1 ]; then from_ref=$(get_current_branch) if [ -z "$from_ref" ] || [ "$from_ref" = "HEAD" ]; then log_warn "Currently in detached HEAD state - falling back to default branch" - from_ref="origin/$(resolve_default_branch "$repo_root")" + from_ref="$remote/$(resolve_default_branch "$repo_root" "$remote")" else log_info "Creating from current branch: $from_ref" fi else - from_ref="origin/$(resolve_default_branch "$repo_root")" + from_ref="$remote/$(resolve_default_branch "$repo_root" "$remote")" fi fi @@ -89,6 +89,7 @@ cmd_create() { local _spec _spec="--from: value --from-current +--remote: value --track: value --no-copy --no-fetch @@ -104,6 +105,7 @@ cmd_create() { local branch_name="${_pa_positional[0]:-}" local from_ref="${_arg_from:-}" local from_current="${_arg_from_current:-0}" + local remote="${_arg_remote:-$(resolve_default_remote)}" local track_mode="${_arg_track:-auto}" local skip_copy="${_arg_no_copy:-0}" local skip_fetch="${_arg_no_fetch:-0}" @@ -152,7 +154,7 @@ cmd_create() { fi # Determine from_ref with precedence: --from > --from-current > default - from_ref=$(_create_resolve_from_ref "$from_ref" "$from_current" "$repo_root") + from_ref=$(_create_resolve_from_ref "$from_ref" "$from_current" "$repo_root" "$remote") # Construct folder name for display local folder_name @@ -170,7 +172,7 @@ cmd_create() { # Create the worktree local worktree_path - if ! worktree_path=$(create_worktree "$base_dir" "$prefix" "$branch_name" "$from_ref" "$track_mode" "$skip_fetch" "$force" "$custom_name" "$folder_override"); then + if ! worktree_path=$(create_worktree "$base_dir" "$prefix" "$branch_name" "$from_ref" "$track_mode" "$skip_fetch" "$force" "$custom_name" "$folder_override" "$remote"); then exit 1 fi @@ -196,4 +198,4 @@ cmd_create() { if [ "$open_editor" -eq 0 ] && [ "$start_ai" -eq 0 ]; then _post_create_next_steps "$branch_name" "$folder_name" "$folder_override" "$repo_root" "$base_dir" "$prefix" fi -} \ No newline at end of file +} diff --git a/lib/commands/help.sh b/lib/commands/help.sh index c680987..a5a2727 100644 --- a/lib/commands/help.sh +++ b/lib/commands/help.sh @@ -17,6 +17,7 @@ feature/user-auth becomes folder "feature-user-auth"). Options: --from Create from a specific ref (default: default branch) --from-current Create from the current branch (for parallel variants) + --remote Remote used for default base refs (default: gtr.defaultRemote) --track Branch tracking mode: auto|remote|local|none (default: auto) auto: tries remote first, then local, then creates new --no-copy Skip file copying (gtr.copy.include patterns) @@ -473,6 +474,7 @@ CORE COMMANDS (daily workflow): Create a new worktree (folder named after branch) --from : create from specific ref --from-current: create from current branch (for parallel variants) + --remote : remote used for default base refs --track : tracking mode (auto|remote|local|none) --no-copy: skip file copying --no-fetch: skip git fetch @@ -636,6 +638,7 @@ CONFIGURATION OPTIONS: gtr.worktrees.dir Worktrees base directory gtr.worktrees.prefix Worktree folder prefix (default: "") gtr.defaultBranch Default branch (default: auto) + gtr.defaultRemote Default remote (default: origin) gtr.editor.default Default editor Options: antigravity, atom, cursor, emacs, idea, nano, nvim, pycharm, sublime, vim, diff --git a/lib/config.sh b/lib/config.sh index 4c1a19e..45cc3b9 100644 --- a/lib/config.sh +++ b/lib/config.sh @@ -108,6 +108,7 @@ _CFG_KEY_MAP=( "gtr.worktrees.dir|worktrees.dir" "gtr.worktrees.prefix|worktrees.prefix" "gtr.defaultBranch|defaults.branch" + "gtr.defaultRemote|defaults.remote" "gtr.provider|defaults.provider" "gtr.ui.color|ui.color" ) diff --git a/lib/core.sh b/lib/core.sh index 5bd4dba..d630f8a 100644 --- a/lib/core.sh +++ b/lib/core.sh @@ -115,10 +115,17 @@ resolve_base_dir() { printf "%s" "$base_dir" } +# Resolve the default remote name +# Usage: resolve_default_remote +resolve_default_remote() { + cfg_default "gtr.defaultRemote" "GTR_DEFAULT_REMOTE" "origin" +} + # Resolve the default branch name -# Usage: resolve_default_branch [repo_root] +# Usage: resolve_default_branch [repo_root] [remote] resolve_default_branch() { local repo_root="${1:-$(pwd)}" + local remote="${2:-$(resolve_default_remote)}" local default_branch local configured_branch @@ -130,8 +137,9 @@ resolve_default_branch() { return 0 fi - # Auto-detect from origin/HEAD - default_branch=$(git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||') + # Auto-detect from the selected remote's HEAD + default_branch=$(git symbolic-ref --quiet "refs/remotes/$remote/HEAD" 2>/dev/null || true) + default_branch="${default_branch#refs/remotes/"$remote"/}" if [ -n "$default_branch" ]; then printf "%s" "$default_branch" @@ -139,9 +147,9 @@ resolve_default_branch() { fi # Fallback: try common branch names - if git show-ref --verify --quiet "refs/remotes/origin/main"; then + if git show-ref --verify --quiet "refs/remotes/$remote/main"; then printf "main" - elif git show-ref --verify --quiet "refs/remotes/origin/master"; then + elif git show-ref --verify --quiet "refs/remotes/$remote/master"; then printf "master" else # Last resort: just use 'main' @@ -370,34 +378,36 @@ _resolve_folder_name() { # Check if a branch exists on remote and/or locally. # Sets globals: _wt_remote_exists, _wt_local_exists (0 or 1) -# Usage: _check_branch_refs +# Usage: _check_branch_refs [remote] declare _wt_remote_exists _wt_local_exists _check_branch_refs() { + local branch_name="$1" remote="${2:-$(resolve_default_remote)}" + _wt_remote_exists=0 _wt_local_exists=0 - git show-ref --verify --quiet "refs/remotes/origin/$1" && _wt_remote_exists=1 - git show-ref --verify --quiet "refs/heads/$1" && _wt_local_exists=1 + git show-ref --verify --quiet "refs/remotes/$remote/$branch_name" && _wt_remote_exists=1 + git show-ref --verify --quiet "refs/heads/$branch_name" && _wt_local_exists=1 return 0 } # Auto-track: create local tracking branch from remote if needed, then add worktree. -# Usage: _worktree_add_tracked [force_args...] +# Usage: _worktree_add_tracked [remote] [force_args...] # shellcheck disable=SC2317 # Called indirectly from create_worktree _worktree_add_tracked() { - local wt_path="$1" branch_name="$2" - shift 2 + local wt_path="$1" branch_name="$2" remote="${3:-$(resolve_default_remote)}" + shift 3 - log_step "Branch '$branch_name' exists on remote" - if git branch --track "$branch_name" "origin/$branch_name" >/dev/null 2>&1; then - log_info "Created local branch tracking origin/$branch_name" + log_step "Branch '$branch_name' exists on $remote" + if git branch --track "$branch_name" "$remote/$branch_name" >/dev/null 2>&1; then + log_info "Created local branch tracking $remote/$branch_name" fi _try_worktree_add "$wt_path" "" \ - "Worktree created tracking origin/$branch_name" \ + "Worktree created tracking $remote/$branch_name" \ "$@" "$branch_name" } # Create a new git worktree -# Usage: create_worktree base_dir prefix branch_name from_ref track_mode [skip_fetch] [force] [custom_name] [folder_override] +# Usage: create_worktree base_dir prefix branch_name from_ref track_mode [skip_fetch] [force] [custom_name] [folder_override] [remote] # track_mode: auto, remote, local, or none # skip_fetch: 0 (default, fetch) or 1 (skip) # force: 0 (default, check branch) or 1 (allow same branch in multiple worktrees) @@ -407,6 +417,7 @@ create_worktree() { local base_dir="$1" prefix="$2" branch_name="$3" from_ref="$4" local track_mode="${5:-auto}" skip_fetch="${6:-0}" force="${7:-0}" local custom_name="${8:-}" folder_override="${9:-}" + local remote="${10:-$(resolve_default_remote)}" local sanitized_name sanitized_name=$(_resolve_folder_name "$branch_name" "$custom_name" "$folder_override") || return 1 @@ -424,31 +435,31 @@ create_worktree() { if [ "$skip_fetch" -eq 0 ]; then log_step "Fetching remote branches..." - git fetch origin 2>/dev/null || log_warn "Could not fetch from origin" + git fetch "$remote" 2>/dev/null || log_warn "Could not fetch from $remote" fi - _check_branch_refs "$branch_name" + _check_branch_refs "$branch_name" "$remote" # Resolve from_ref to a commit SHA to prevent git's guess-remote logic # from overriding the -b flag when from_ref matches a remote branch name. - # Try the ref as-is first, then with origin/ prefix for remote-only refs. + # Try the ref as-is first, then with the selected remote prefix for remote-only refs. local resolved_ref resolved_ref=$(git rev-parse --verify "${from_ref}^{commit}" 2>/dev/null) \ - || resolved_ref=$(git rev-parse --verify "origin/${from_ref}^{commit}" 2>/dev/null) \ + || resolved_ref=$(git rev-parse --verify "$remote/${from_ref}^{commit}" 2>/dev/null) \ || resolved_ref="$from_ref" case "$track_mode" in remote) if [ "$_wt_remote_exists" -eq 1 ]; then _try_worktree_add "$worktree_path" \ - "Creating worktree from remote branch origin/$branch_name" \ - "Worktree created tracking origin/$branch_name" \ - "${force_args[@]}" -b "$branch_name" "origin/$branch_name" && return 0 + "Creating worktree from remote branch $remote/$branch_name" \ + "Worktree created tracking $remote/$branch_name" \ + "${force_args[@]}" -b "$branch_name" "$remote/$branch_name" && return 0 _try_worktree_add "$worktree_path" "" \ - "Worktree created tracking origin/$branch_name" \ + "Worktree created tracking $remote/$branch_name" \ "${force_args[@]}" "$branch_name" && return 0 fi - log_error "Remote branch origin/$branch_name does not exist" + log_error "Remote branch $remote/$branch_name does not exist" return 1 ;; @@ -474,7 +485,7 @@ create_worktree() { auto|*) if [ "$_wt_remote_exists" -eq 1 ] && [ "$_wt_local_exists" -eq 0 ]; then - _worktree_add_tracked "$worktree_path" "$branch_name" "${force_args[@]}" && return 0 + _worktree_add_tracked "$worktree_path" "$branch_name" "$remote" "${force_args[@]}" && return 0 elif [ "$_wt_local_exists" -eq 1 ]; then _try_worktree_add "$worktree_path" \ "Using existing local branch $branch_name" \ diff --git a/scripts/generate-completions.sh b/scripts/generate-completions.sh index 1072645..49a1f5a 100755 --- a/scripts/generate-completions.sh +++ b/scripts/generate-completions.sh @@ -193,7 +193,7 @@ MIDDLE1 new) # Complete flags if [[ "$cur" == -* ]]; then - COMPREPLY=($(compgen -W "--from --from-current --track --no-copy --no-fetch --no-hooks --force --name --folder --yes --editor -e --ai -a" -- "$cur")) + COMPREPLY=($(compgen -W "--from --from-current --remote --track --no-copy --no-fetch --no-hooks --force --name --folder --yes --editor -e --ai -a" -- "$cur")) elif [ "$prev" = "--track" ]; then COMPREPLY=($(compgen -W "auto remote local none" -- "$cur")) fi @@ -320,6 +320,7 @@ _git-gtr() { '1:branch name:' \ '--from[Base ref]:ref:' \ '--from-current[Create from current branch]' \ + '--remote[Remote used for default base refs]:remote:' \ '--track[Track mode]:mode:(auto remote local none)' \ '--no-copy[Skip file copying]' \ '--no-fetch[Skip git fetch]' \ @@ -535,6 +536,7 @@ complete -f -c git -n '__fish_git_gtr_needs_command' -a help -d 'Show help' # New command options complete -c git -n '__fish_git_gtr_using_command new' -l from -d 'Base ref' -r complete -c git -n '__fish_git_gtr_using_command new' -l from-current -d 'Create from current branch' +complete -c git -n '__fish_git_gtr_using_command new' -l remote -d 'Remote used for default base refs' -r complete -c git -n '__fish_git_gtr_using_command new' -l track -d 'Track mode' -r -a 'auto remote local none' complete -c git -n '__fish_git_gtr_using_command new' -l no-copy -d 'Skip file copying' complete -c git -n '__fish_git_gtr_using_command new' -l no-fetch -d 'Skip git fetch' @@ -615,6 +617,7 @@ MIDDLE2 gtr.worktrees.dir) desc="Worktrees base directory" ;; gtr.worktrees.prefix) desc="Worktree folder prefix" ;; gtr.defaultBranch) desc="Default branch" ;; + gtr.defaultRemote) desc="Default remote" ;; gtr.editor.default) desc="Default editor" ;; gtr.editor.workspace) desc="Path to workspace file (.code-workspace)" ;; gtr.ai.default) desc="Default AI tool" ;; diff --git a/tests/cmd_create_integration.bats b/tests/cmd_create_integration.bats index 6920537..2b40140 100644 --- a/tests/cmd_create_integration.bats +++ b/tests/cmd_create_integration.bats @@ -25,6 +25,21 @@ teardown() { [ -d "$TEST_WORKTREES_DIR/track-none" ] } +@test "cmd_create --remote uses selected remote default branch" { + local old_sha expected_sha actual_sha + old_sha=$(git rev-parse HEAD) + git update-ref refs/remotes/origin/main "$old_sha" + + git commit --allow-empty -m "upstream main" --quiet + expected_sha=$(git rev-parse HEAD) + git update-ref refs/remotes/upstream/main "$expected_sha" + + cmd_create remote-default --remote upstream --track none --no-fetch --yes + + actual_sha=$(git -C "$TEST_WORKTREES_DIR/remote-default" rev-parse HEAD) + [ "$actual_sha" = "$expected_sha" ] +} + @test "cmd_create creates worktree with --name suffix" { cmd_create named-branch --from HEAD --name backend --no-fetch --yes [ -d "$TEST_WORKTREES_DIR/named-branch-backend" ] diff --git a/tests/core_create_worktree.bats b/tests/core_create_worktree.bats index 0bca387..0561ec2 100644 --- a/tests/core_create_worktree.bats +++ b/tests/core_create_worktree.bats @@ -47,6 +47,24 @@ teardown() { [ "$status" -eq 1 ] } +# ── default remote/branch resolution ───────────────────────────────────────── + +@test "resolve_default_remote reads gtr.defaultRemote" { + git config gtr.defaultRemote upstream + + local result + result=$(resolve_default_remote) + [ "$result" = "upstream" ] +} + +@test "resolve_default_branch detects branch from selected remote" { + git update-ref refs/remotes/upstream/main HEAD + + local result + result=$(resolve_default_branch "$TEST_REPO" "upstream") + [ "$result" = "main" ] +} + # ── _check_branch_refs ────────────────────────────────────────────────────── @test "_check_branch_refs detects local branch" { @@ -62,6 +80,14 @@ teardown() { [ "$_wt_remote_exists" -eq 0 ] } +@test "_check_branch_refs detects selected remote branch" { + git update-ref refs/remotes/upstream/remote-only HEAD + + _check_branch_refs "remote-only" "upstream" + [ "$_wt_remote_exists" -eq 1 ] + [ "$_wt_local_exists" -eq 0 ] +} + # ── create_worktree ───────────────────────────────────────────────────────── @test "create_worktree creates directory with track=none" { @@ -101,6 +127,21 @@ teardown() { [ -d "$wt_path" ] } +@test "create_worktree auto mode tracks selected remote branch" { + git remote add upstream "$TEST_REPO" 2>/dev/null || true + git branch selected-remote HEAD + git fetch upstream --quiet + git branch -D selected-remote >/dev/null + + local wt_path + wt_path=$(create_worktree "$TEST_WORKTREES_DIR" "" "selected-remote" "HEAD" "auto" "1" "0" "" "" "upstream") + [ -d "$wt_path" ] + + local upstream + upstream=$(git -C "$wt_path" rev-parse --abbrev-ref --symbolic-full-name '@{u}') + [ "$upstream" = "upstream/selected-remote" ] +} + @test "create_worktree rejects duplicate worktree" { create_worktree "$TEST_WORKTREES_DIR" "" "dup-test" "HEAD" "none" "1" >/dev/null run create_worktree "$TEST_WORKTREES_DIR" "" "dup-test" "HEAD" "none" "1"