Dealing with Merge Conflicts
For a lot of people, merge conflicts are as scary as accidentally formatting their hard drive. In the course of this chapter, I want to relieve you from this fear.
You Cannot Break Things
The first thing that you should keep in mind is that you can always undo a merge and go back to the state before the conflict occurred. You're always able to undo and start fresh.
If you're coming from another version control system like e.g. Subversion you might be traumatized: conflicts in Subversion have the (rightful) reputation of being incredibly complex and nasty. One reason for this is that Git, simply stated, works completely different in this regard than Subversion. As a consequence, Git is able to take care of most things during a merge - leaving you with comparatively simple scenarios to solve.
Also, a conflict will only ever handicap yourself. It will not bring your complete team to a halt or cripple your central repository. This is because, in Git, conflicts can only occur on a developer's local machine - and not on the server.
How a Merge Conflict Occurs
In Git, "merging" is the act of integrating another branch into your current working branch. You're taking changes from another context (that's what a branch effectively is: a context) and combine them with your current working files.
A great thing about having Git as your version control system is that it makes merging extremely easy: in most cases, Git will figure out how to integrate new changes.
However, there's a handful of situations where you might have to step in and tell Git what to do. Most notably, this is when changing the same file. Even in this case, Git will most likely be able to figure it out on its own. But if two people changed the same lines in that same file, or if one person decided to delete it while the other person decided to modify it, Git simply cannot know what is correct. Git will then mark the file as having a conflict - which you'll have to solve before you can continue your work.
How to Solve a Merge Conflict
When faced with a merge conflict, the first step is to understand what happened. E.g.: Did one of your colleagues edit the same file on the same lines as you? Did he delete a file that you modified? Did you both add a file with the same name?
If you have any merge conflicts, Tower will inform you in a very clear way:
In this example, we have three conflicting changes:
- about.html was deleted in one version and modified in the other. Which is correct - the modification or the deletion?
- box.php was created in both versions, but with different contents. Which version is the correct one?
- index.html was modified in both versions in different ways (the "U" icon in the Status column stands for "unmerged"). Which modification is correct?
Under the Hood - What a Conflict Looks Like
Before we look at how to solve these conflicts in Tower, we'll risk a look under the hood: we'll look at the contents of the (currently conflicted) faq.html file in an editor:
Git was nice enough to mark the problematic area in the file by enclosing it in "<<<<<<< HEAD" and ">>>>>>> [other/branch/name]". The contents after the first marker originate from our current working branch. A line with "=======" separates the two conflicting changes.
Our job is now to clean up these lines: when we're done, the file should look exactly as we want it to look. It can be necessary to consult the teammate who wrote the conflicting changes to decide which code is finally correct. Maybe it's yours, maybe it's his - or maybe a mixture between the two.
Solving a Conflict in Tower
Opening the raw file in your editor and cleaning it up there is perfectly valid, but not very comfortable. Tower offers some more elegant ways to solve conflicts.
Simply select a conflicted item in the list to open Tower's "Conflict Wizard" on the right. Three containers illustrate the case:
- Top left: the version from your current HEAD branch
- Top right: the version from the merged-in branch
- Bottom: the resulting version of the file that you have to compose in the resolution process
You now have the following options to solve the conflict:
- Determine one of the original versions to be correct. To do so, select one of the versions on the top.
- Compose a mixture of both versions as the correct version. To do so, select both of the versions on the top.
After you've made your selection, the bottom container will have adjusted - along with the button below. In case you selected just one of the original versions to be correct, you can simply click the "Resolve Using Their/My Version" button to move on. In case you want to combine both versions, you need to compose this in a "Merge Tool".
Using a Merge Tool
Although you could, in theory, edit such a merged version in any text editor, I highly recommend using a dedicated merge tool for this job. See the chapter about Diff & Merge Tools later for recommended tools if you don't have your favorite, yet.
After configuring your tool in Tower's preferences, it will be opened upon clicking the respective button on the bottom of the Conflict Wizard. For this example, I've used "Kaleidoscope.app" on my Mac:
The left and right panes stand for the conflicting changes; a far more elegant visualization than "<<<<<<<" and ">>>>>>>". You can now simply toggle which change shall be taken. The middle pane shows the resulting code; in good tools, you can even edit this further.
Now, after cleaning up the file with the final code, all that's left is to save it. To give Tower a hint that you're done with this file, you should quit the merge tool to continue and return to Tower.
Conclude the Resolution with a Commit
Finally, after solving all conflicted files, a merge conflict situation needs to be concluded by a regular commit.
How to Undo a Merge
You should always keep in mind that you can return to the state before you started the merge at any time. This should give you the confidence that you can't break anything. In Tower, simply click the "Abort" button on top of the file list in the "Working Copy" view.
In case you've made a mistake while resolving a conflict and realize this only after completing the merge, you can still easily undo it: just roll back to the commit before the merge happened with the "reset" command and start over again.