Skip to content

add: remote-only base silently creates the base branch instead of <branch>, exit 0 #61

Description

@leogdion

Summary

git trees add <branch> <base> silently creates the wrong branch when <base> names a branch
that exists only on the remote. The worktree is checked out on <base> instead of <branch>,
<branch> is never created, and the command exits 0.

Worse, it leaves behind a local <base> branch that nothing checks out. That ref then goes stale
as origin/<base> advances, and because cmd_add passes $base through verbatim, the next
git trees add <other> <base> bases the new worktree on the stale commit — also silently.

Both symptoms bit us in one session: a worktree created on the integration branch instead of a
feature branch (any commit would have gone straight onto the integration branch), and later a
worktree based one commit behind, missing a merged PR.

Root cause

cmd_add, in the "branch doesn't exist yet" path:

: "${base:=origin/$(_default_branch)}"
git worktree add --no-track -b "$br" "$dir" "$base" || return 1

$base is passed unqualified. When it names a remote-only branch, git's DWIM for
git worktree add takes over: it treats the start-point as "create a local branch tracking that
remote branch" and overrides the explicit -b "$br". The --no-track guard doesn't prevent
this — it only affects upstream, not the branch name.

Reproduction

git init -q --bare origin.git
git init -q work && cd work
git -c user.email=t@t -c user.name=t commit -q --allow-empty -m init
git remote add origin ../origin.git
git push -q origin HEAD:refs/heads/main
git push -q origin HEAD:refs/heads/v1.0.0-alpha.1
git fetch -q origin
git branch -D v1.0.0-alpha.1        # remote-only from here on

# exactly what cmd_add runs:
git worktree add --no-track -b newbranch ../wt-new v1.0.0-alpha.1

Actual:

Preparing worktree (new branch 'v1.0.0-alpha.1')     <- not 'newbranch'
HEAD is now at 6a6a443 init
$ echo $?
0
$ git worktree list
.../wt-new  6a6a443 [v1.0.0-alpha.1]                 <- wrong branch
$ git for-each-ref --format='%(refname:short)' refs/heads
main
v1.0.0-alpha.1                                       <- stray local branch; 'newbranch' absent

Expected: worktree on newbranch, created at origin/v1.0.0-alpha.1, with no stray local ref.

Note git rev-parse --verify v1.0.0-alpha.1 fails in this state, so the bare name is not
resolvable as a commit-ish — the only reason the command succeeds at all is the DWIM path.

Fix

Either works; both verified against the repro above.

A — resolve the base to a commit before creating the worktree (also gives a clear error when
the base is bogus, instead of a confusing worktree):

: "${base:=origin/$(_default_branch)}"
base_sha=$(git rev-parse --verify "${base}^{commit}" 2>/dev/null) || {
  echo "git trees add: base '$base' is not a valid commit" >&2; return 1; }
git worktree add --no-track -b "$br" "$dir" "$base_sha" || return 1

B — resolve a bare branch name to origin/<name> when there's no local ref, then pass that.

Suggest A: it fixes the DWIM hijack and the stale-local-ref problem in one go, since nothing named
$base is ever created locally.

Worth adding regardless: assert after creation that the worktree is actually on $br, so a future
DWIM change can't reintroduce a silent wrong-branch.

Environment

  • git-trees 0.4.9 usage line: add <branch> [base] [--print-path] [--no-push]
  • git 2.x, macOS 26.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions