Visitar URL original
submit: better handling of divergent HEAD for pushed, but not published branches · Issue #845 · abhinav/git-spice · GitHub
Skip to content

submit: better handling of divergent HEAD for pushed, but not published branches #845

Open

Description

@abhinav

When submitting a branch, git-spice attempts to match the branch to open PRs on the repo in case the PR was opened without using git-spice's submit command.
Right now, this will refuse to match branches with the same name that have a different HEAD.

This has led to the following scenario for some users:

  1. Use git-spice to submit branch but not publish a PR.
  2. Create PR manually
  3. A CI job that updates the PR (e.g. adjusting formatting)
  4. Make local updates and try to submit to update the branch

The submit fails because the branch matching fails (because of divergent HEAD).
We need the following fixes here:

  • when rejecting a branch because of a divergent head, prompt the user in case they know for sure that they own this branch
  • submit should probably remember that it pushed this branch previously, so even if there's no PR information, it's owned by us

Activity

  1. jasperSpeicher commented on Mar 13, 2026

    @jasperSpeicher

    hey @abhinav i was thinking i might see if i can direct an agent to help out with this one. here is the plan at the moment:

    We fix three independent failure points that cause git-spice to create -2 branches instead of reusing existing ones. Each fix is small and focused.

    Task 1: Track stores upstream branch name
    Problem: gs branch track on a branch that's already been pushed to remote (e.g., origin/feat1) doesn't record the upstream branch name in git-spice state. So when submit runs later, it doesn't know the branch was already pushed.

    Fix: In TrackBranch(), check if the branch has a git upstream set. If it does and it points to the expected remote, store it as UpstreamBranch in the upsert request.

    Impact: Highest. This alone prevents the -2 suffix in the most common workflow (push → track → submit).

    Task 2: --no-publish still discovers existing CRs
    Problem: The condition if branch.Change == nil && opts.Publish means --no-publish skips CR discovery entirely. Even if a PR exists for the branch, git-spice doesn't find it, treats the branch as new, and calls UnusedBranchName().

    Fix: Decouple CR discovery from the Publish flag. Always look for existing CRs to determine the upstream branch name — just don't create a new CR when --no-publish is set.

    Impact: Medium. Fixes the --no-publish path specifically.

    Task 3: Relax HEAD hash comparison for existing CRs
    Problem: When submit does find a matching CR by branch name, it rejects the match if the HEAD hashes differ (line 583). After any rebase/restack, local HEAD always differs from remote — so the match is rejected and a new -2 branch is created.

    Fix: Per your suggestion in #845, prompt the user when HEAD diverges instead of silently rejecting. In non-interactive mode, accept the match (the branch name matches, so it's almost certainly the same branch).

    Impact: Medium. Fixes the rebase-then-submit scenario. Directly addresses your open issue.

    how do you feel about this plan?

  2. abhinav commented on Mar 14, 2026

    @abhinav
    OwnerAuthor

    Hey, @jasperSpeicher, thanks for looking into this.
    Sorry, I will be unable to read or respond to the plan until early April.

  3. abhinav commented on Apr 6, 2026

    @abhinav
    OwnerAuthor

    Hey @jasperSpeicher I'm back and able to put some time to this.

    Task 1: Track stores upstream branch name
    Problem: gs branch track on a branch that's already been pushed to remote (e.g., origin/feat1) doesn't record the upstream branch name in git-spice state. So when submit runs later, it doesn't know the branch was already pushed.

    So gs does already track the upstream branch name, but only when you submit a CR.
    The right fix might be to get gs branch submit --no-publish (which pushes but does not create a CR) to also update the state about the upstream branch name.

    Task 2: --no-publish still discovers existing CRs

    The core functionality has to remain as-is — and CR detection MUST be fallible.
    --no-publish cannot rely on Forge APIs because people use it to use git-spice with unsupported forges, or with vanilla Git repos. So no-publish cannot require a Forge to be available.
    But otherwise, what you're suggesting sounds good — CR discovery can be decoupled and re-used, as long as it's not required for --no-publish.

    Task 3: Relax HEAD hash comparison for existing CRs
    [..] In non-interactive mode, accept the match [..]

    Everything else is fine but I would say in non-interactive mode, don't accept the match.
    In larger repos, it's not uncommon for people to name branches "fix" or "test" or something non-descriptive like that.
    We absolutely do not want to pick up someone else's branch because we force push, so we would effectively delete someone else's work. That's part of the reason why we compare the HEADs in the first place.

    So the right fix here is: if HEAD does not match (which, again, only happens if someone pushes to your branch after you created the PR) — in non-interactive mode, reject the branch as today, but in interactive mode (more common), prompt the user to confirm.

    @jasperSpeicher this looks great otherwise. As you've identified three separate tasks with clear boundaries between them, I'd like to request that these be separate PRs if/when you make them.

    Thanks for your patience with my reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions