Practical Merging in Git

Merging in Git can be done using various techniques.

Published on March 21, 2021

When I first started using Git, I didn't know anyone that actually knew what git was doing. I used commands that everyone else knew and used them the way they told me to use them. That works until you actually need to understand what your doing to avoid problems in the future. In this tutorial I'll explain some practical merging techniques and what I prefer. As a side note, I wanted to mention that the original title of this tutorial was Git merge strategies, but then I found out it's actually a related, but different topic of its own.

Regular ol' merging

Let's set up an example. I have a feature branch, new-login, that I branched off of from my master branch. In this example, I never work directly on the master branch. I always create a feature branch.

If I get lucky, once I finish working on my feature branch, there will be no commits made to master, like the diagram below.

git branch start

Once I've committed all my changes to new-login, I can type the following to merge new-login into master:

$ git checkout master
$ git merge new-login

I now have a branch that looks like this:

git branch post merge 1

I can also visualize it like this as well:

git branch post merge 2

They're both the same. Before I merged this, I tested my changes on the new-login branch and since master had nothing new in it, then I knew that master would work fine after the merge.

Glory merging

The examples shown in the Atlassian tutorial demonstrate what I'm calling glory merging, which is merging and assuming all will be well.

I can't wrong them for that, because they're just trying to describe the merge process, not all the precautions you should take when merging.

In a more likely scenario, master will have progressed and have new commits after I branched off from it. I'll commit my changes to the new-login branch and get the latest changes from master. If I merge right away, then I'm performing a glory merge. This is how the commands will look like:

$ git checkout master
$ git pull
$ git merge new-login

git glory merge

This is a glory merge, because I didn't even pull in the changes from the master branch into my new-login branch to see how it might affect my new-login branch. I assumed all would be well and went for the glory.

The following scenarios can result from doing this:

  1. The merge is fine and everyone is happy.
  2. My feature for some reason no longer works as expected, so the master branch has a bug in it now.
  3. There's a merge conflict and I can't merge until I resolve it.

Merging before the merge

A safe approach would be to merge the master branch into the new-login feature branch.

$ git checkout master
$ git pull
$ git checkout new-login
$ git merge master

git merge master into feature

I could then test it locally and finally merge it back into master.

$ git checkout master
$ git merge new-login

git merge feature back into master

new-login is now merged into master. The above diagram shows an example of fast-forwarding. If I wanted to continue to have master only have merge commits, then I could merge with the no-ff option. Assuming I am back at the point where I have already merged master into new-login, I could write the following commands to merge new-login back into master with the no-ff option.

$ git checkout master
$ git merge new-login --no-ff

git merge feature back into master no-ff

This gives me a new merge commit in the master branch as seen in the diagram above. By merging with the no-ff option, another merge commit is created. The decision on which way to merge comes down to my own personal preference. When working alone, I generally prefer to fast-forward whenever I can, because it makes the branch look cleaner, but I am starting to like seeing the merge commits to indicate the merging of a feature or work that wasn't trivial.

Aside from that, the point of these examples was to demonstrate that it's good practice to merge in the latest changes from a main line of development back into a feature branch. Once that's done I should always test locally before merging back into the main line of development. In this example, the main line of development is the master branch.

You can also follow the same principle of testing your latest work with rebasing. Rebasing can give you a cleaner history, depending on what your definition of clean is. I cover rebasing in another tutorial.

References

  1. A good article describing the merge process
  2. I used Pencil to create these diagrams
  3. I used Greenshot to take screenshots of my Pencil diagrams