Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Git, how to create a branch after resolving conflicts?

Tags:

git

merge

I frequently find myself in this situation:

  • pull changes from an upstream repo (implying merge)
  • the merge causes conflicts
  • resolve the conflicts
  • decide to commit not to the current branch but a new one

In the last step, I use git checkout -b new-branch-name, but after invoking that command, the current head no longer has references to the pulled changes. It only has one parent. It's no longer a merge.

How can I, while performing a merge, commit that merge to a new branch?

like image 564
Jason R. Coombs Avatar asked Nov 18 '25 09:11

Jason R. Coombs


1 Answers

Personally, I avoid git pull. By avoiding it, I can first run git fetch, then inspect what's coming in. Having looked, I then use git merge but usually wind up also avoiding this particular mistake in the first place because I can see that the merge might be difficult and start on a new branch in advance.

Still, it is useful to be able to recover ... and it's easy! Just do this, as you did already:

git pull
# oh no, merge conflicts!
# resolve conflicts
git commit                    # or git merge --continue

and commit to the wrong branch, and then fix it up afterward:

git branch new-branch-name    # create new branch to contain the merge
git reset --hard HEAD^1       # back up current branch to first-parent
git checkout new-branch-name  # return to new branch

Diagrammatically, you start with:

    o--o--o   <-- your-branch (HEAD)
   /
...
   \
    o--o--o   <-- origin/your-branch

The conclusion of the merge advances your-branch to point to the new merge commit * you make:

    o--o--o
   /       \
...         *   <-- your-branch (HEAD)
   \       /
    o--o--o   <-- origin/your-branch

We now add a new branch, pointing to this same merge commit:

    o--o--o
   /       \
...         *   <-- your-branch (HEAD), new-branch-name
   \       /
    o--o--o   <-- origin/your-branch

and then use git reset to drag the branch name to which HEAD is attached back one step, in the first-parent direction, which restores it to its previous commit:

    o--o--o   <-- your-branch (HEAD)
   /       \
...         *   <-- new-branch-name
   \       /
    o--o--o   <-- origin/your-branch

and we're all set.

All of this relies on a useful principle about Git: Git is really all about commits. The branch names move around all the time; they're just how we find the commits. The commits are permanent (well, as long as we can find them) and unchangeable. Get the commits the way you like and then you can reshuffle the names at leisure.

(If HEAD^1 is hard to type for various reasons, note that you can use any of HEAD^ or HEAD~ or even @^ or @~. That is:

  • HEAD and @ are synonyms.
  • The ^1 and ^ without 1, as suffixes, are synonyms: the 1 is implied if you omit it.
  • The ~1 and ~ without 1, as suffixes, are synonyms: the 1 is implied if you omit it.
  • While ^ and ~ do different things, both mean "go back some number of steps in the graph".

The number after the ^ is the parent-number and is meaningful only for merge commits: 1 means first parent. So HEAD^1 means first parent of HEAD, and HEAD~1 means go back one first-parent. Applying all the synonym rules is what gives us the @^ and @~ constructs.)

like image 83
torek Avatar answered Nov 20 '25 13:11

torek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!