Skip to content

Commit 6d1f465

Browse files
fix: support configurable default remote (#171)
* fix: support configurable default remote Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> Assisted-by: opencode:gpt-5.5 * fix: quote default remote branch trim Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> Assisted-by: opencode:gpt-5.5 --------- Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
1 parent 025de7f commit 6d1f465

12 files changed

Lines changed: 126 additions & 39 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ git gtr new my-feature --name descriptive-variant
172172

173173
- `--from <ref>`: Create from specific ref
174174
- `--from-current`: Create from current branch (useful for parallel variant work)
175+
- `--remote <name>`: Remote used for default base refs
175176
- `--track <mode>`: Tracking mode (auto|remote|local|none)
176177
- `--no-copy`: Skip file copying
177178
- `--no-fetch`: Skip git fetch
@@ -388,6 +389,7 @@ git gtr config set gtr.ui.color never
388389
[defaults]
389390
editor = cursor
390391
ai = claude
392+
remote = upstream
391393
```
392394

393395
**Configuration precedence** (highest to lowest):

completions/_git-gtr

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ _git-gtr() {
6161
'1:branch name:' \
6262
'--from[Base ref]:ref:' \
6363
'--from-current[Create from current branch]' \
64+
'--remote[Remote used for default base refs]:remote:' \
6465
'--track[Track mode]:mode:(auto remote local none)' \
6566
'--no-copy[Skip file copying]' \
6667
'--no-fetch[Skip git fetch]' \
@@ -183,15 +184,15 @@ _git-gtr() {
183184
'--local[Use local git config]' \
184185
'--global[Use global git config]' \
185186
'--system[Use system git config]' \
186-
'*: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)'
187+
'*: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)'
187188
;;
188189
set|add|unset)
189190
# Write operations only support --local and --global
190191
# (--system may require root or appropriate file permissions)
191192
_arguments \
192193
'--local[Use local git config]' \
193194
'--global[Use global git config]' \
194-
'*: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)'
195+
'*: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)'
195196
;;
196197
esac
197198
fi

completions/git-gtr.fish

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ complete -f -c git -n '__fish_git_gtr_needs_command' -a help -d 'Show help'
6060
# New command options
6161
complete -c git -n '__fish_git_gtr_using_command new' -l from -d 'Base ref' -r
6262
complete -c git -n '__fish_git_gtr_using_command new' -l from-current -d 'Create from current branch'
63+
complete -c git -n '__fish_git_gtr_using_command new' -l remote -d 'Remote used for default base refs' -r
6364
complete -c git -n '__fish_git_gtr_using_command new' -l track -d 'Track mode' -r -a 'auto remote local none'
6465
complete -c git -n '__fish_git_gtr_using_command new' -l no-copy -d 'Skip file copying'
6566
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 "
140141
gtr.worktrees.dir 'Worktrees base directory'
141142
gtr.worktrees.prefix 'Worktree folder prefix'
142143
gtr.defaultBranch 'Default branch'
144+
gtr.defaultRemote 'Default remote'
143145
gtr.provider 'Hosting provider (github, gitlab)'
144146
gtr.ui.color 'Color output mode (auto, always, never)'
145147
"

completions/gtr.bash

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ _git_gtr() {
9999
new)
100100
# Complete flags
101101
if [[ "$cur" == -* ]]; then
102-
COMPREPLY=($(compgen -W "--from --from-current --track --no-copy --no-fetch --no-hooks --force --name --folder --yes --editor -e --ai -a" -- "$cur"))
102+
COMPREPLY=($(compgen -W "--from --from-current --remote --track --no-copy --no-fetch --no-hooks --force --name --folder --yes --editor -e --ai -a" -- "$cur"))
103103
elif [ "$prev" = "--track" ]; then
104104
COMPREPLY=($(compgen -W "auto remote local none" -- "$cur"))
105105
fi
@@ -138,15 +138,15 @@ _git_gtr() {
138138
if [[ "$cur" == -* ]]; then
139139
COMPREPLY=($(compgen -W "--local --global --system" -- "$cur"))
140140
else
141-
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"))
141+
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"))
142142
fi
143143
;;
144144
set|add|unset)
145145
# Write operations only support --local and --global (--system requires root)
146146
if [[ "$cur" == -* ]]; then
147147
COMPREPLY=($(compgen -W "--local --global" -- "$cur"))
148148
else
149-
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"))
149+
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"))
150150
fi
151151
;;
152152
esac

docs/configuration.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Create a `.gtrconfig` file in your repository root to share configuration across
6262
[defaults]
6363
editor = cursor
6464
ai = claude
65+
remote = upstream
6566
```
6667

6768
> [!TIP]
@@ -92,6 +93,9 @@ gtr.worktrees.prefix = dev-
9293

9394
# Default branch (default: auto-detect)
9495
gtr.defaultBranch = main
96+
97+
# Default remote for fetches, tracking, and default base refs (default: origin)
98+
gtr.defaultRemote = upstream
9599
```
96100

97101
> [!IMPORTANT]
@@ -440,6 +444,7 @@ git gtr config add gtr.hook.postCreate "pnpm run build"
440444
# Set global preferences
441445
git gtr config set gtr.editor.default cursor --global
442446
git gtr config set gtr.ai.default claude --global
447+
git gtr config set gtr.defaultRemote upstream --global
443448
```
444449

445450
---
@@ -454,6 +459,7 @@ git gtr config set gtr.ai.default claude --global
454459
| `GTR_EDITOR_CMD_NAME` | First word of `GTR_EDITOR_CMD` for availability checks | None |
455460
| `GTR_AI_CMD` | Custom AI tool command (e.g., `copilot`) | None |
456461
| `GTR_AI_CMD_NAME` | First word of `GTR_AI_CMD` for availability checks | None |
462+
| `GTR_DEFAULT_REMOTE` | Remote used for default base refs and tracking | `origin` |
457463
| `GTR_COLOR` | Override color output (`always`, `never`, `auto`) | `auto` |
458464
| `GTR_PROVIDER` | Override hosting provider (`github` or `gitlab`) | Auto-detected from URL |
459465
| `NO_COLOR` | Disable color output when set ([no-color.org](https://no-color.org)) | Unset |

lib/commands/create.sh

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,22 @@ _post_create_next_steps() {
6363
}
6464

6565
# Determine the base ref for worktree creation
66-
# Usage: _create_resolve_from_ref <from_ref> <from_current> <repo_root>
66+
# Usage: _create_resolve_from_ref <from_ref> <from_current> <repo_root> [remote]
6767
# Prints: resolved ref
6868
_create_resolve_from_ref() {
69-
local from_ref="$1" from_current="$2" repo_root="$3"
69+
local from_ref="$1" from_current="$2" repo_root="$3" remote="${4:-$(resolve_default_remote)}"
7070

7171
if [ -z "$from_ref" ]; then
7272
if [ "$from_current" -eq 1 ]; then
7373
from_ref=$(get_current_branch)
7474
if [ -z "$from_ref" ] || [ "$from_ref" = "HEAD" ]; then
7575
log_warn "Currently in detached HEAD state - falling back to default branch"
76-
from_ref="origin/$(resolve_default_branch "$repo_root")"
76+
from_ref="$remote/$(resolve_default_branch "$repo_root" "$remote")"
7777
else
7878
log_info "Creating from current branch: $from_ref"
7979
fi
8080
else
81-
from_ref="origin/$(resolve_default_branch "$repo_root")"
81+
from_ref="$remote/$(resolve_default_branch "$repo_root" "$remote")"
8282
fi
8383
fi
8484

@@ -89,6 +89,7 @@ cmd_create() {
8989
local _spec
9090
_spec="--from: value
9191
--from-current
92+
--remote: value
9293
--track: value
9394
--no-copy
9495
--no-fetch
@@ -104,6 +105,7 @@ cmd_create() {
104105
local branch_name="${_pa_positional[0]:-}"
105106
local from_ref="${_arg_from:-}"
106107
local from_current="${_arg_from_current:-0}"
108+
local remote="${_arg_remote:-$(resolve_default_remote)}"
107109
local track_mode="${_arg_track:-auto}"
108110
local skip_copy="${_arg_no_copy:-0}"
109111
local skip_fetch="${_arg_no_fetch:-0}"
@@ -152,7 +154,7 @@ cmd_create() {
152154
fi
153155

154156
# Determine from_ref with precedence: --from > --from-current > default
155-
from_ref=$(_create_resolve_from_ref "$from_ref" "$from_current" "$repo_root")
157+
from_ref=$(_create_resolve_from_ref "$from_ref" "$from_current" "$repo_root" "$remote")
156158

157159
# Construct folder name for display
158160
local folder_name
@@ -170,7 +172,7 @@ cmd_create() {
170172

171173
# Create the worktree
172174
local worktree_path
173-
if ! worktree_path=$(create_worktree "$base_dir" "$prefix" "$branch_name" "$from_ref" "$track_mode" "$skip_fetch" "$force" "$custom_name" "$folder_override"); then
175+
if ! worktree_path=$(create_worktree "$base_dir" "$prefix" "$branch_name" "$from_ref" "$track_mode" "$skip_fetch" "$force" "$custom_name" "$folder_override" "$remote"); then
174176
exit 1
175177
fi
176178

@@ -196,4 +198,4 @@ cmd_create() {
196198
if [ "$open_editor" -eq 0 ] && [ "$start_ai" -eq 0 ]; then
197199
_post_create_next_steps "$branch_name" "$folder_name" "$folder_override" "$repo_root" "$base_dir" "$prefix"
198200
fi
199-
}
201+
}

lib/commands/help.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ feature/user-auth becomes folder "feature-user-auth").
1717
Options:
1818
--from <ref> Create from a specific ref (default: default branch)
1919
--from-current Create from the current branch (for parallel variants)
20+
--remote <name> Remote used for default base refs (default: gtr.defaultRemote)
2021
--track <mode> Branch tracking mode: auto|remote|local|none (default: auto)
2122
auto: tries remote first, then local, then creates new
2223
--no-copy Skip file copying (gtr.copy.include patterns)
@@ -473,6 +474,7 @@ CORE COMMANDS (daily workflow):
473474
Create a new worktree (folder named after branch)
474475
--from <ref>: create from specific ref
475476
--from-current: create from current branch (for parallel variants)
477+
--remote <name>: remote used for default base refs
476478
--track <mode>: tracking mode (auto|remote|local|none)
477479
--no-copy: skip file copying
478480
--no-fetch: skip git fetch
@@ -636,6 +638,7 @@ CONFIGURATION OPTIONS:
636638
gtr.worktrees.dir Worktrees base directory
637639
gtr.worktrees.prefix Worktree folder prefix (default: "")
638640
gtr.defaultBranch Default branch (default: auto)
641+
gtr.defaultRemote Default remote (default: origin)
639642
gtr.editor.default Default editor
640643
Options: antigravity, atom, cursor, emacs,
641644
idea, nano, nvim, pycharm, sublime, vim,

lib/config.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ _CFG_KEY_MAP=(
108108
"gtr.worktrees.dir|worktrees.dir"
109109
"gtr.worktrees.prefix|worktrees.prefix"
110110
"gtr.defaultBranch|defaults.branch"
111+
"gtr.defaultRemote|defaults.remote"
111112
"gtr.provider|defaults.provider"
112113
"gtr.ui.color|ui.color"
113114
)

lib/core.sh

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,17 @@ resolve_base_dir() {
115115
printf "%s" "$base_dir"
116116
}
117117

118+
# Resolve the default remote name
119+
# Usage: resolve_default_remote
120+
resolve_default_remote() {
121+
cfg_default "gtr.defaultRemote" "GTR_DEFAULT_REMOTE" "origin"
122+
}
123+
118124
# Resolve the default branch name
119-
# Usage: resolve_default_branch [repo_root]
125+
# Usage: resolve_default_branch [repo_root] [remote]
120126
resolve_default_branch() {
121127
local repo_root="${1:-$(pwd)}"
128+
local remote="${2:-$(resolve_default_remote)}"
122129
local default_branch
123130
local configured_branch
124131

@@ -130,18 +137,19 @@ resolve_default_branch() {
130137
return 0
131138
fi
132139

133-
# Auto-detect from origin/HEAD
134-
default_branch=$(git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
140+
# Auto-detect from the selected remote's HEAD
141+
default_branch=$(git symbolic-ref --quiet "refs/remotes/$remote/HEAD" 2>/dev/null || true)
142+
default_branch="${default_branch#refs/remotes/"$remote"/}"
135143

136144
if [ -n "$default_branch" ]; then
137145
printf "%s" "$default_branch"
138146
return 0
139147
fi
140148

141149
# Fallback: try common branch names
142-
if git show-ref --verify --quiet "refs/remotes/origin/main"; then
150+
if git show-ref --verify --quiet "refs/remotes/$remote/main"; then
143151
printf "main"
144-
elif git show-ref --verify --quiet "refs/remotes/origin/master"; then
152+
elif git show-ref --verify --quiet "refs/remotes/$remote/master"; then
145153
printf "master"
146154
else
147155
# Last resort: just use 'main'
@@ -370,34 +378,36 @@ _resolve_folder_name() {
370378

371379
# Check if a branch exists on remote and/or locally.
372380
# Sets globals: _wt_remote_exists, _wt_local_exists (0 or 1)
373-
# Usage: _check_branch_refs <branch_name>
381+
# Usage: _check_branch_refs <branch_name> [remote]
374382
declare _wt_remote_exists _wt_local_exists
375383
_check_branch_refs() {
384+
local branch_name="$1" remote="${2:-$(resolve_default_remote)}"
385+
376386
_wt_remote_exists=0
377387
_wt_local_exists=0
378-
git show-ref --verify --quiet "refs/remotes/origin/$1" && _wt_remote_exists=1
379-
git show-ref --verify --quiet "refs/heads/$1" && _wt_local_exists=1
388+
git show-ref --verify --quiet "refs/remotes/$remote/$branch_name" && _wt_remote_exists=1
389+
git show-ref --verify --quiet "refs/heads/$branch_name" && _wt_local_exists=1
380390
return 0
381391
}
382392

383393
# Auto-track: create local tracking branch from remote if needed, then add worktree.
384-
# Usage: _worktree_add_tracked <worktree_path> <branch_name> [force_args...]
394+
# Usage: _worktree_add_tracked <worktree_path> <branch_name> [remote] [force_args...]
385395
# shellcheck disable=SC2317 # Called indirectly from create_worktree
386396
_worktree_add_tracked() {
387-
local wt_path="$1" branch_name="$2"
388-
shift 2
397+
local wt_path="$1" branch_name="$2" remote="${3:-$(resolve_default_remote)}"
398+
shift 3
389399

390-
log_step "Branch '$branch_name' exists on remote"
391-
if git branch --track "$branch_name" "origin/$branch_name" >/dev/null 2>&1; then
392-
log_info "Created local branch tracking origin/$branch_name"
400+
log_step "Branch '$branch_name' exists on $remote"
401+
if git branch --track "$branch_name" "$remote/$branch_name" >/dev/null 2>&1; then
402+
log_info "Created local branch tracking $remote/$branch_name"
393403
fi
394404
_try_worktree_add "$wt_path" "" \
395-
"Worktree created tracking origin/$branch_name" \
405+
"Worktree created tracking $remote/$branch_name" \
396406
"$@" "$branch_name"
397407
}
398408

399409
# Create a new git worktree
400-
# Usage: create_worktree base_dir prefix branch_name from_ref track_mode [skip_fetch] [force] [custom_name] [folder_override]
410+
# Usage: create_worktree base_dir prefix branch_name from_ref track_mode [skip_fetch] [force] [custom_name] [folder_override] [remote]
401411
# track_mode: auto, remote, local, or none
402412
# skip_fetch: 0 (default, fetch) or 1 (skip)
403413
# force: 0 (default, check branch) or 1 (allow same branch in multiple worktrees)
@@ -407,6 +417,7 @@ create_worktree() {
407417
local base_dir="$1" prefix="$2" branch_name="$3" from_ref="$4"
408418
local track_mode="${5:-auto}" skip_fetch="${6:-0}" force="${7:-0}"
409419
local custom_name="${8:-}" folder_override="${9:-}"
420+
local remote="${10:-$(resolve_default_remote)}"
410421

411422
local sanitized_name
412423
sanitized_name=$(_resolve_folder_name "$branch_name" "$custom_name" "$folder_override") || return 1
@@ -424,31 +435,31 @@ create_worktree() {
424435

425436
if [ "$skip_fetch" -eq 0 ]; then
426437
log_step "Fetching remote branches..."
427-
git fetch origin 2>/dev/null || log_warn "Could not fetch from origin"
438+
git fetch "$remote" 2>/dev/null || log_warn "Could not fetch from $remote"
428439
fi
429440

430-
_check_branch_refs "$branch_name"
441+
_check_branch_refs "$branch_name" "$remote"
431442

432443
# Resolve from_ref to a commit SHA to prevent git's guess-remote logic
433444
# from overriding the -b flag when from_ref matches a remote branch name.
434-
# Try the ref as-is first, then with origin/ prefix for remote-only refs.
445+
# Try the ref as-is first, then with the selected remote prefix for remote-only refs.
435446
local resolved_ref
436447
resolved_ref=$(git rev-parse --verify "${from_ref}^{commit}" 2>/dev/null) \
437-
|| resolved_ref=$(git rev-parse --verify "origin/${from_ref}^{commit}" 2>/dev/null) \
448+
|| resolved_ref=$(git rev-parse --verify "$remote/${from_ref}^{commit}" 2>/dev/null) \
438449
|| resolved_ref="$from_ref"
439450

440451
case "$track_mode" in
441452
remote)
442453
if [ "$_wt_remote_exists" -eq 1 ]; then
443454
_try_worktree_add "$worktree_path" \
444-
"Creating worktree from remote branch origin/$branch_name" \
445-
"Worktree created tracking origin/$branch_name" \
446-
"${force_args[@]}" -b "$branch_name" "origin/$branch_name" && return 0
455+
"Creating worktree from remote branch $remote/$branch_name" \
456+
"Worktree created tracking $remote/$branch_name" \
457+
"${force_args[@]}" -b "$branch_name" "$remote/$branch_name" && return 0
447458
_try_worktree_add "$worktree_path" "" \
448-
"Worktree created tracking origin/$branch_name" \
459+
"Worktree created tracking $remote/$branch_name" \
449460
"${force_args[@]}" "$branch_name" && return 0
450461
fi
451-
log_error "Remote branch origin/$branch_name does not exist"
462+
log_error "Remote branch $remote/$branch_name does not exist"
452463
return 1
453464
;;
454465

@@ -474,7 +485,7 @@ create_worktree() {
474485

475486
auto|*)
476487
if [ "$_wt_remote_exists" -eq 1 ] && [ "$_wt_local_exists" -eq 0 ]; then
477-
_worktree_add_tracked "$worktree_path" "$branch_name" "${force_args[@]}" && return 0
488+
_worktree_add_tracked "$worktree_path" "$branch_name" "$remote" "${force_args[@]}" && return 0
478489
elif [ "$_wt_local_exists" -eq 1 ]; then
479490
_try_worktree_add "$worktree_path" \
480491
"Using existing local branch $branch_name" \

0 commit comments

Comments
 (0)