diff --git a/apps/desktop/electron/git-worktree-ops.test.ts b/apps/desktop/electron/git-worktree-ops.test.ts index a543ecd39..be40e9fe5 100644 --- a/apps/desktop/electron/git-worktree-ops.test.ts +++ b/apps/desktop/electron/git-worktree-ops.test.ts @@ -262,3 +262,39 @@ test('addWorktree: base param branches off a specified local branch', async () = fs.rmSync(dir, { recursive: true, force: true }) } }) + +test('addWorktree: base origin/main does not set up upstream tracking', async () => { + // Two repos: a bare "remote" and a clone, so origin/main resolves as a + // remote-tracking ref — the condition that triggers auto-tracking. + const remoteDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-remote-')) + const cloneDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-clone-')) + const git = (...args) => execFileSync('git', args, { cwd: cloneDir }).toString().trim() + + try { + // Seed the remote with a commit on main. + execFileSync('git', ['init', '-b', 'main', remoteDir]) + execFileSync('git', ['-C', remoteDir, 'commit', '--allow-empty', '-m', 'root']) + + // Clone so origin/main exists as a remote-tracking ref. + execFileSync('git', ['clone', remoteDir, cloneDir]) + + const result = await addWorktree(cloneDir, { base: 'origin/main', branch: 'feature-branch', name: 'feature-branch' }, 'git') + + assert.equal(result.branch, 'feature-branch') + + // The new branch must NOT have an upstream — like `git checkout origin/main + // && git checkout -b feature-branch`, not `git worktree add -b … origin/main`. + let hasUpstream = true + + try { + execFileSync('git', ['-C', result.path, 'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']) + } catch { + hasUpstream = false + } + + assert.equal(hasUpstream, false) + } finally { + fs.rmSync(remoteDir, { recursive: true, force: true }) + fs.rmSync(cloneDir, { recursive: true, force: true }) + } +}) diff --git a/apps/desktop/electron/git-worktree-ops.ts b/apps/desktop/electron/git-worktree-ops.ts index e84324a00..1acd029e1 100644 --- a/apps/desktop/electron/git-worktree-ops.ts +++ b/apps/desktop/electron/git-worktree-ops.ts @@ -265,8 +265,15 @@ async function addWorktree(repoPath, options, gitBin) { } catch { // The fetch isn't mandatory, but it would be nice to do if possible. // If it's not possible, just use the local ref of the remote branch. - // If it doesn't exist locally, we'll get an error anyways + // If it doesn't exist locally, we'll get an error } + + // When branching off a remote-tracking ref, git auto-sets up tracking + // (e.g. `new-branch` → tracks `origin/main`). The user almost certainly + // wants a standalone local branch — like `git checkout origin/main && + // git checkout -b new-branch` — not a branch silently wired to the + // remote's upstream. `--no-track` prevents that. + args.push('--no-track') } args.push(base)