Git: switch branches mid-merge

The short answer is that you can't (switch branches ... well, sort of, although you can, sort of, but it's not advisable—it involves monkeying directly with the innards of Git):

$ git checkout -b newbr
foo.txt: needs merge
error: you need to resolve your current index first

Your index is in this special "merging" state and therefore you cannot stash either. Fortunately, there is no need to do so. (If you resolve everything and then run git checkout -b newbr and commit, you get a non-merge commit. You can use this, but let's not do it that way.)

What you should do is go ahead and finish the merge: this gives you the merge result you want. You can then re-start the merge in the branch you want, and grab the result you just committed as the result you want, if that's even necessary. You then discard the original merge commit entirely (if necessary). (This is also what you need to do if you accidentally wrecked the merge with a successful git checkout -b as almost shown above.)

In some cases—including your case—the original merge's parent links are the ones you wanted, and it's just a matter of re-labeling the merge.

I'll show the recipe first, but then explain why it works:

... finish merging ...
$ git commit                         # commit the merge
$ git checkout -b AB-merge-branch    # create the merge branch
$ git checkout big-feature-branch-A  # get back to other branch
$ git reset --hard HEAD^             # take the merge off of it

Why this is an answer (there are multiple ways so I won't say the answer)

Let's draw your setup as it was when you first started to merge:

       o--...--o--o   <-- big-feature-branch-A (HEAD)
      /
...--*
      \
       o--...--o--o   <-- big-feature-branch-B

That is, you were, as git status would put it, "on branch big-feature-branch-A" and all was well. Each o represents a commit (and I marked the merge base, for the merge, with an asterisk).

You then intended to run git checkout -b AB-merge-branch. If you had done that, the picture would look like this:

    o--...--o--o   <-- big-feature-branch-A, AB-merge-branch (HEAD)
   /
--*
   \
    o--...--o--o   <-- big-feature-branch-B

You ran git merge, which failed with conflicts. You resolved (most of) the conflicts (you must resolve them all before you proceed). When you finally commit, you will get a new merge commit, and that will move the current branch (the one remembered by HEAD):

    o--...--o--o   <-- big-feature-branch-A
   /            \
--*              M   <-- AB-merge-branch (HEAD)
   \            /
    o--...--o--o   <-- big-feature-branch-B

Not shown here (because it is too hard to show) is that the first parent of the new merge M is the rightmost (latest) upper row commit, and the second is the lower. (This first vs second stuff matters later, if at all, when someone wants to follow the "main" branch vs "side features that were merged in": the main branch is always the first parent, by definition.)

You forgot to create a new branch name, so what happens now, instead, is this:

    o--...--o--o
   /            \
--*              M   <-- big-feature-branch-A (HEAD)
   \            /
    o--...--o--o   <-- big-feature-branch-B

The first parent is still the top-and-right-most commit, the second parent is still the bottom such commit. The new merge commit M is exactly the same. It's just that the label that moved, to point to new merge M, is big-feature-branch-A (the one that is HEAD), and not the nonexistent AB-merge-branch (which obviously isn't HEAD).

So all you have to do now is create the labeling you wanted. Anything that makes the new branch name AB-merge-branch, pointing to M, suffices for that part. You can use git checkout -b AB-merge-branch or git branch AB-merge-branch to do that.

If you do use git checkout -b you now have to get back to big-feature-branch-A to fix it up using git reset (there are other commands you could use, but I'm sticking with reset here). If you use git branch to create the new branch, your current branch is undisturbed: you are still on big-feature-branch-A.

In any case, you want this big-feature-branch-A branch name to move back one step, to the first parent of M, as if it had never moved forward to M in the first place. So you get back on (or stay on) this branch. Once you're on this branch, you can use git reset --hard HEAD^ to achieve this moving-back-one-step. HEAD^ means "find the first parent of HEAD", which—if HEAD names commit M (it does)—means the rightmost upper row commit, which is where you want the branch to point. The git reset command does this re-pointing of the branch, and also re-sets your index and work-tree (so that everything is cleanly on the new commit).


First, simply finish your merge and commit. That way, you won't lose anything.
This is a local operation and won't be seen by anybody else.

From that commit, you can create your branch AB-merge-branch

 git checkout -b AB-merge-branch

And you can reset your previous big-feature-branch-A to its first parent commit.

 git branch --force big-feature-branch-A big-feature-branch-A^1

Tags:

Git

Git Merge