Merge conflict hell
Tags: git, devops, development, discuss
Published: 2019-04-27 18:28:47

---
title: Merge conflict hell
published: true
description: Explaining git merge conflict for a three-year-old.
cover_image: https://i.imgur.com/8EnTyOs.jpg
tags: git, devops, development, discuss
---

**Imagine a castle made of git!**
Wait a second.
That's not the sentence I wanted to start with. Anyway.

---

## The problem
If you have ever worked together with a team on a project in git, you might have ended up working on a *chain of commits*. All lining up nicely, ready to be pushed to the common remote repository that sits behind your team's gihub/bitbucket/gerrit/whatever server.
This is when one of your colleagues would say something like this: 

*"Hey Caren, you forgot to add a semicolon in that file. You know the one you added in the middle of your commit chain."*

And this is where you start checking-out and rebasing and possibly end up in a merge conflict hell. 

###If you don't really understand what's going on here this article is for you!
Let's discover the problem with a very simple example. This is where the castle comes to the picture.

---

## Meet your git castle!
We will create our **git castle** as simple as this:

```bash
mkdir git-castle
cd git-castle
git init
touch castle.yaml
```
> As you might have thought our git castle will be **just a file, representing a piece of code** that we will edit and make commits with until we properly suffocate ourselves with merge conflicts.

Let's create our **code representation**, as a yaml file, describing parameters of our castle. We will also create a chain of commits to work with.

**Our code representation file:**

```yaml
# This is my git castle!
---
location:   Camelot
built:      2010-10-10
owner:      Penny Bunn

rooms:
    - room_name: Living Room

    - room_name: Kitchen
```

Let's create our initial commit with adding this file.
```git
git add castle.yaml
git commit -m "Initial Commit"
```

Now we add some parameters, and **create a second commit** as **"Add room parameters"**:

```yaml
# This is my git castle!
---
location:   Camelot
built:      2010-10-10
owner:      Penny Bunn

rooms:
    - room_name: Living Room
      size:      30m2
      windows:   2
      doors:     2

    - room_name: Kitchen
      size:      15m2
      windows:   2
      doors:     1
```

Now we add another room, and a **third commit** named **"Add bathroom"***:

```yaml
    - room_name: Bathroom,
      size:      10m2
      windows:   1
      doors:     1
```
Cool! We have a three commit long commit chain!

---

## Going back to fix something

But no! We suddenly learn, that according to our plans **the living room should have 3 windows!** 

Naturally, it would be nicer to fix this on the original "Add room parameters" commit. Especially now, that we noticed our mistake in the commit chain before pushing it to the remote master.

This is where **interactive rebase** comes to the picture. 

> In git, the `rebase` command is used to reapply commits to a chosen base tip. Luckily it has an interactive flag, and with it, you can simply go back to a chosen commit, edit it, and reapply all the following commits till master.

`git rebase -i ${base_tip}`

As a `base_tip` we will choose any commit that is *older* (further back) than the point we want to edit. When you have a remote repo, that point will be the remote master, which will also be the base of your commit chain, so best to rebase on that. Now in our case we will just use the hash of our initial commit. 

> We can figure that out with `git log` or `gitk`, or `git rev-parse HEAD~$num` where $num is our *distance* from the initial commit.

After smashing enter we will be greeted with an **interactive editor window**. We will see all the commits above our chosen `base_tip` and we can choose which commit to edit by simply *replacing* the chosen `pick` with an `e`. Like this:

```
e ddbef6b Add room parameters
pick 607bd93 Add bathroom


# Rebase cf0e9f4..550ab2c onto cf0e9f4 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
```

> If you have never seen these interactive windows either here or when committing and you also don't know the default editor of git (emacs or vim) you may have some trouble how to edit and save the document. You might want to look into vim/emacs if this is the case.

After the successful edit, we can check with `gitk` or `git rev-parse HEAD` that we are indeed at the commit which we wanted to edit. We will do our fix like this:
```yaml
rooms:
    - room_name: Living Room
      size:      30m2
      windows:   2
      doors:     2
```
With `git add` and `git commit --amend` we can add the change to the "Add room parameters" commit. After this, we can put back on the "Add bathroom" commit and check it out by one simple command: `git rebase --continue` 
> Actually even git itself is warning us if we do a `git status` that we are in an interactive rebase session, and we should abort or continue it.

## All seem so simple now, but still...

Let's make the bathroom much bigger and with 2 windows, also add a pantry, and make a commit, named "Resize bathroom and add pantry":
```yaml
    - room_name: Bathroom,
      size:      20m2
      windows:   2
      doors:     1

    - room_name: Pantry,
      size:      5m2
      windows:   0
      doors:     1
```


**Now comes the problem.** We decide to do a toilet not the bathroom on this level of the castle. So just to clarify, we decide to do it like this:
```yaml
# This is my git castle!
---
location:   Camelot
built:      2010-10-10
owner:      Penny Bunn

rooms:
    - room_name: Living Room
      size:      30m2
      windows:   3
      doors:     2

    - room_name: Kitchen
      size:      15m2
      windows:   2
      doors:     1

    - room_name: Toilet
      size:      3m2
      windows:   0
      doors:     1

    - room_name: Pantry,
      size:      5m2
      windows:   0
      doors:     1
```

Ok, simple enough. We know interactive rebase, so we can do the whole swap at the "Add bathroom" commit. We simply go back to "Add bathroom" to make it "Add Toilet", also changing the yaml file. We do the rebase relative to the initial commit, edit "Add bathroom", and create toilet in the place of the bathroom:

```yaml
    - room_name: Toilet
      size:      3m2
      windows:   0
      doors:     1
```
Do `git add` and `git commit --amend` as "Add Toilet" then simply `git rebase --continue`.
But this time we get an **error**:

```
git-castle> git rebase --continue
Auto-merging castle.yaml
CONFLICT (content): Merge conflict in castle.yaml
error: could not apply 30637b3... Resize bathroom and add pantry
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 30637b3... Resize bathroom and add pantry
```

Now, this happened because in the "Resize bathroom and add pantry" commit we also edited the same lines. All git sees is that we did an edit on the new "Add Toilet" commit on those lines, then while rebasing it realizes that there are conflicting edits on the same line. It makes sense, since **how should git know which edit should persist** right? That's why the rebasing stops at this point so we can edit the file by choosing which change to persist on the commit where the conflict occurred (*this time it's the "Resize bathroom and add pantry" commit*).

If you open the file you can actually see that **git even inserted assisting separators to see what content it's having a dilemma about**. We do the fix, and with `git add` and `git rebase --continue` the rebasing goes through.
Naturally and edit window will jump up for the fixed commit, which we could rename as "Add pantry", as that is it's true content after there is no bathroom resize in it.

Woila.
Check gitk to be sure.
`git push`
Profit.