Skip to content

Pull() rebase conflict raises GitPullConflictException saying "mid-merge"; caller can't tell the repository is actually mid-rebase #242

Open

Description

@matt-edmondson

What's wrong

GitPullBuilder.CreateException (GitIntegration/Builders/GitPullBuilder.cs:288-299) turns any failed pull whose stdout contains CONFLICT into a GitPullConflictException. Its own remarks (lines 280-285) say this covers both merge and rebase conflicts. The exception's documentation (GitIntegration/Execution/GitExceptions.cs:282-297) says "The repository is now mid-merge" and that git exits 128. Neither the exception nor the result says which operation actually stopped.

I reproduced this with git 2.43 using pull --rebase --progress and diverging commits that touch the same line. The result was:

  • The exit code is 1, not 128.
  • stdout is Auto-merging f / CONFLICT (content): Merge conflict in f.
  • The repository is left with .git/rebase-merge and REBASE_HEAD, and there is no MERGE_HEAD.
  • status --porcelain=v2 --branch reports # branch.head (detached) plus a u UU entry.
  • git merge --abort, which the "mid-merge" wording points the caller to, fails with fatal: There is no merge to abort (MERGE_HEAD missing) (exit 128).

Why it matters

This happens with .Rebase(). It also happens with a plain Pull() on any machine where pull.rebase=true is configured, because Pull() sets no reconciliation mode unless Merge() or Rebase() is called. In that case the caller may not even know a rebase was attempted. Recovery code written from the docs runs merge --abort, which fails and leaves the repository on a detached HEAD in the middle of a rebase.

Suggested fix / acceptance criteria

  • GitPullConflictException says which operation stopped, for example through a GitPullConflictKind { Merge, Rebase } property. The kind can be found by checking for REBASE_HEAD / rebase-merge after the failure. Checking the vector for --rebase alone misses the pull.rebase config case.
  • The exception remarks no longer claim "mid-merge" unconditionally or "exits 128"
  • An integration test in GitRemoteSyncTests covers a Rebase() conflict and asserts the kind
  • Another test covers a plain Pull() with pull.rebase=true set in the throwaway repository and asserts the kind

Activity

  1. matt-edmondson commented on Oct 18, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions