1 September 2020
Once you start dealing with conflicts in Git, one question often comes back:
How to accept/discard all changes from the other/base branch?
And, as always in tech, the answer is: it depends. It depends on whether you are merging from another branch, or rebasing on a base branch. Here’s why.
Conflicts when merging
When you are merging a branch, say
feat, into the base branch on which you are currently working, say
master, the command you would use is
git merge feat. By doing so, git will produce a new commit on
master. This commit will in effect be the top of the
master branch, leaving the
feat branch where it was. This may sound trivial but it has some importance.
Now, let’s imagine that your
feat branch is conflicting with your base branch, and you would like to retain all the changes from
feat no matter what. In that case, you can tell git to automatically deal with conflicts by accepting all
feat changes with a
git merge -X theirs feat. The extra strategy option (
--strategy-option) we passed here indicates that all conflicts must be resolved by preferring the changes from the branch we are bringing in, in other words, their changes.
This should come to no surprise but you can get the opposite behaviour by preferring the changes from the base branch using the
-X ours strategy option. In that case, because the changes of the currently checked out branch are preferred, those are considered as our changes, as opposed to the changes from the branch we are bringing in.
As always, don’t hesitate to refer to the official man page of the merge command using
man git-merge which is well detailed and is completely offline. The equivalent online documentation is available here.
Does it all make sense now?
ours refers to the base branch while
theirs corresponds to the other branch. Well, during a rebase, this is the opposite. Git’s fun, right?
Conflicts when rebasing
In case you’re wondering: no, git authors are not mad men who just want their users to suffer every time they use
rebase. The real reason is consistency.
Let’s perform a rebase from our previous example using the
git rebase master command (supposing we started from the
feat branch) to understand why
theirs work oppositely.
When conflicts arise during a rebase, you would have to specify
-X theirs to force git to resolve conflicts by applying
feat branch changes. Conversely, if your goal is to resolve the conflicts by always using the changes from the base branch, you would need to use
This surely sounds counter-intuitive at first. However, if we take the time to put ourselves in the shoes of
git rebase, this makes more sense.
Here’s what is (mostly) happening during our
git rebase master operation:
# Step 1. git checkout <HASH_OF_THE_COMMIT_POINTED_BY_THE_MASTER_BRANCH> # Step 2. git cherry-pick <FIRST_COMMIT_OF_FEAT_BRANCH> git cherry-pick <SECOND_COMMIT_OF_FEAT_BRANCH> # ... a few more cherry-picks may happen here # Step 3. git branch --force feat HEAD
You can notice from steps 1. and 3. that the rebase does not operate directly onto the
feat branch. Instead, it cherry-picks the commits from the
feat branch, one by one (this is step 2), directly onto the original commit pointed by the
In that context, we can see why the changes of the
feat branch, from which we started the rebase, are considered as
theirs. Meanwhile, the commits from the base branch
master are considered as
ours. What happens here is the same as what we described in the previous section. The difficulty only lies in the fact that rebase moves around while doing its duties, and does not remain on the branch it started from.