Curate the error when the sync target is checked out in another worktree - #14076
Merged
sergiou87 merged 2 commits intoAug 5, 2026
Merged
Conversation
`git branch --force` already refuses to move a branch that another worktree has
checked out, but it surfaces a raw git fatal. Detect the case up front with
`git worktree list --porcelain` and return a curated error naming the worktree,
matching the guidance we already give for uncommitted changes.
Detection parses porcelain output rather than matching git's message, which
varies by version ("used by worktree at" on 2.53, "checked out at" on older).
`branch --force` stays as the safety net for states the check can't see, such
as an in-progress bisect.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4f88d532-4a31-47c7-ab50-a3d0b9a77fc4
Reproduces the issue end to end: `main` checked out in the primary worktree while `gh repo sync` runs from a linked worktree. Asserts the sync refuses, the primary worktree's ref and working tree are untouched, and that syncing from the worktree that owns the branch still fast forwards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4f88d532-4a31-47c7-ab50-a3d0b9a77fc4
williammartin
changed the base branch from
trunk
to
12927-fix-repo-sync-worktree
August 5, 2026 09:53
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the gh repo sync user experience when the target branch is checked out in a different Git worktree by detecting that state up front and returning a curated, actionable error message (instead of surfacing Git’s raw fatal text). This aligns the worktree-occupancy failure path with the command’s existing “refuse + tip” style used for dirty working trees.
Changes:
- Add a pre-check using
git worktree list --porcelainto detect when the target branch is checked out in another worktree and refuse with a curated message. - Introduce a porcelain parser helper (
worktreePathForBranch) and wire it through the sync command’s git client. - Expand coverage with a new unit test for the parser, updated real-git tests, a mocked sync-run case, and a new acceptance test reproducing the end-to-end scenario.
Show a summary per file
| File | Description |
|---|---|
| pkg/cmd/repo/sync/sync.go | Refuse local sync with a curated error when the target branch is checked out in another worktree. |
| pkg/cmd/repo/sync/sync_test.go | Update integration/unit expectations to assert the curated error and correct worktree identification. |
| pkg/cmd/repo/sync/mocks.go | Extend the git mock to support the new BranchWorktreePath method. |
| pkg/cmd/repo/sync/git.go | Add BranchWorktreePath and a porcelain parser to detect worktree occupancy. |
| pkg/cmd/repo/sync/git_test.go | New table test validating worktreePathForBranch parsing behavior across edge cases. |
| acceptance/testdata/repo/repo-sync-worktree.txtar | New acceptance scenario reproducing the worktree sync refusal and ensuring no unintended mutation occurs. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Lite
6 tasks
sergiou87
approved these changes
Aug 5, 2026
sergiou87
left a comment
Contributor
There was a problem hiding this comment.
Love it! Thank you 🙇♂️
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #14060, which fixes #12927. Targets
12927-fix-repo-sync-worktreeso @sergiou87 can take it or leave it.Description
A git repository can have more than one working directory attached to it, via
git worktree. Each one has its own checked-out branch, its own index, and its own files on disk, but they all share one set of branch refs.gh repo syncupdates your local branch to match the remote. When the branch you're syncing isn't the one you currently have checked out, it can't merge into your working directory, so it moves the branch ref directly instead. #12927 reported that this silently corrupts things: if some other worktree has that branch checked out, moving the ref leaves that worktree's index and files pointing at the old commit, so everything shows up as staged changes.#14060 fixes that by switching from
git update-reftogit branch --force. Git itself refuses to move a branch another worktree is using, so silent corruption becomes a hard failure. That's the right call. The gap is what the user sees:That's a raw git error with a doubled
failed to run git: fatal:prefix. It's also inconsistent with whatgh repo syncdoes six lines away in the same function, where a dirty working directory produces a curated message and a tip:This PR detects the worktree case before attempting the update and gives it the same treatment:
Detection runs
git worktree list --porcelainand looks for a worktree holdingrefs/heads/<branch>.git branch --forcestays in place as the safety net.How did you test this change?
I also ran that acceptance test against
trunkto confirm it's a real regression test and not just a test that agrees with the new code. It fails there, becauseupdate-refsilently succeeds:Key points
We use the porcelain output to avoid matching on the git error message which I believe version and locale dependent. This does introduce a small potential race, but it seems like a reasonable first step.
Notes for reviewers
None
Authorship and follow-up
Who wrote this:
Who answers review comments: