Strategies to resolve git conflicts using "theirs" and "ours"
Tags: git, productivity, tooling, webdev
Published: 2021-08-26 07:08:21

[Git branching](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging) allows developers to work in isolation. Once your work is completed, you can merge your branch back into the master. In a few cases, it could cause merge conflicts where Git doesn't know what to do and hands control back to you.

What if you just want to **override your changes in master with another branch**, or vice-versa? You can resolve all the conflicts manually by navigating to each file, and accepting all incoming changes to the master. It could be a little time-consuming. But can we do better? 

In this article, I'll first explain the reasoning behind the conflicts, and then I'll show you a command which allows you to do the same in a few keystrokes. If you already know git internals, you can [skip straight to the command](#resolving-conflicts-using-xours-and-xtheirs).

## Why do merge conflicts occur? 

A branch is just a lightweight, movable pointer to one of the commits you are on. When you create a new branch off from master with `git branch feature`, a new pointer is created as shown in the figure below:
```
feature
   |
   A
   |
 master
   |
 HEAD
```
And when you switch branch with `git checkout feature`, Git moves the `HEAD` pointer, which was initially pointing to `master`, to your branch. This is how Git knows which branch you are on. You can read more about this concept in [official git docs](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell) and [an article](https://amanexplains.com/how-to-smartly-cleanup-your-commit-history-using-git/#pointers-) written by yours truly.
```
  HEAD
   |
 feature
   |
   A
   |
 master
```
While you are happily working on your branch (e.g. feature), the master (pointer) can move forward — your colleague's branch (testing) has been merged into the master already. The scenario now looks like this.

```
   E---F---G feature
  /
 A---B---C master
```
It's time to merge your branch to master. You ran `git checkout master` followed by `git merge feature`. The `git merge feature` will try to replay changes made on the `feature` branch since it diverged from master (i.e "A") until its current commit (i.e "G"). But you met with some merge conflicts. This is because the feature and testing branch both have modified the same file. And now git doesn't know what changes to keep and what to discard. It handles the control back to you instead of overriding things on its own, which might not be desirable.

You can use `git merge --abort` command to abort the merge process when a merge conflict has already occurred.

## Resolving conflicts using "Xours" and "Xtheirs"

In those situations where you just want to override changes from one branch to another, you can use two merge strategy options: `-Xtheirs` and `-Xours`.

If you want to override the changes in the `master` branch with your `feature` branch, you can run the following command after checking out to master:
```
git merge -Xtheirs feature
```
And to keep the master branch changes, you can use:
```
git merge -Xours feature
```
Interestingly, it works in **reverse** order if you want to do [**rebasing of your branch onto the master**](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) and want to keep your changes over the master. 

So, if you are on your feature branch, the command you need to run will be:
```
git rebase master -Xtheirs
```
And to keep `master` branch changes over yours, you need to do:
```
git rebase master -Xours
```

## Understanding the reverse conundrum in rebasing
If you wish to understand the reasoning behind why the order was reversed, you need to first understand how rebasing works.

Let's assume we have the following history with the feature branch checked out:
```
   E---F---G feature
  /    
 A---B---C master
```
When you rebase onto master via `git rebase master`, the process goes through the following steps:

- Roll back to the **common ancestor commit** of the feature and the master branch (i.e "A").
- Save all changes made by commits in the feature branch, but that are not in master, to a temporary area.
- Reset feature branch to current commit on master.

The intermediate history looks like this now:
```
 E---F---G (saved in a temporary area)

         feature
          /    
 A---B---C master
```
After the reset, all the changes in the temporarily saved area (i.e "E", "F", & "G"), are applied in turn onto the master branch. 

The final new history looks like this:
```
           E---F---G feature
         /    
A---B---C master
```
The key point to remember is that the feature branch was reset to the current master, and changes were applied from there onwards. So, `theirs` refers to the `feature` branch but not `master` and `ours` will be the `master` branch as internally we're on this master branch already. 

## Summary 
The following table would make it easier to understand and remember `Xtheirs` and `Xours` strategies.

|   Currently on     |    Command      | Strategy     | Outcome |
| :------------- | :----------: | -----------: | -----------: |
|  master | git merge feature   | **-Xtheirs**    | Keep changes from feature branch |
| master | git merge feature| **-Xours** | keep changes from master branch |
|  feature | git rebase master   | **-Xtheirs**    | Keep changes from feature branch |
| feature | git rebase master | **-Xours**      | keep changes from master branch |


### Other resources: 
- [Branching and Merging -- git docs](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging)
- [Git rebasing — offical git docs](https://git-scm.com/book/en/v2/Git-Branching-Rebasing)
- [Pointers in git](https://amanexplains.com/how-to-smartly-cleanup-your-commit-history-using-git/#pointers-)