Merge vs Rebase: Part 2 - What is a merge?

Merge vs Rebase: Part 2 - What is a merge?

Table of Contents:

In part 1 we left off with a little demo repository. We had a feature branch called feature1 that was ready to be merged back into master.

At this point we can choose to merge feature1 into master or we can choose to rebase. We'll cover rebasing in part 3. For now let's see what happens if we do a merge. Merging branches together is pretty straight-forward. We first need to checkout the branch we want to merge into. Since we want to merge feature1 into master we need to checkout master.

I checked out the master branch and then merged feature1 into it. Let's go over what exactly happened and why the graph generated by Source Tree looks the way it does.

Remember from part 1 how Commit 3 and Commit 4 ended up having the same previous commit? Commit 2 is the common ancestor for both of those commits because Commit 3 was made on another branch and Commit 4 was made to the master branch, which didn't have knowledge of Commit 3. In our feature1 branch we added a couple more commits. Commit 5 directly references Commit 3 because Commit 4 is only available on the master branch. Commmit 6 references Commit 5.

When we merged feature1 into master, it didn't magically move those commits over to the master branch somehow. It actually created a brand new commit that contains all of the changes from all of the commits on the feature1 branch. The commit that says Merge branch 'feature1' looks like this:

If you have been paying attention to the commit diffs in the screenshots then you've seen the silly lines I've been adding to index.txt. You may have noticed that those lines were all added piece by piece in separate commits to the feature1 branch. However, here you can see all of those changes in a single diff.

All Git did was smash all the diffs from all the commits to feature1 together into a single commit. This new commit has done something that we haven't discussed yet. If you look at the graph you'll notice that it has two ancestors. It has lines coming from Commit 4 and Commit 6. Why is this? Commits can store references to more than one previous commit. I'm only just now bringing this up because I didn't want to cause confusion earlier.

When a commit is being created it can store references to a single previous commit hash, multiple commit hashes, or none. Usually only the very first commit to your repository has no previous commit references and merge commits are usually the only commits that store more than one previous commit reference.

If you remember from part 1, branches are really just pointers to specific commits.

You may notice that feature1 still points to Commit 6 while master now points to the new merge commit. This is simply because we merged feature1 into master. If we were to checkout feature1 and merge master into it then all Git would do is another fast-forward merge that would bring the feature1 pointer up to point at the same commit.

If we now delete our feature1 branch entirely you might expect that pink branch line to disappear, but you'd be wrong.

Remember, Source Tree and any other Git GUIs generate the graph by walking over your commits and connecting them together using the referenced commit hashes. Branches are just pointers that point at specific commits. When you pull from a remote repository all Git does is:

  1. Download any commits that your local machine doesn't have.
  2. Merge the missing commits into your local repository, either via a merge commit, or via a fast-forward merge if you haven't made any changes since your last pull.
  3. Move your local branch pointer up to the latest commit.

If you've ever been confused about the master and origin/master pointers, now you know what those are. origin/master is showing you what commit your origin remote's master branch is pointing to. If I add a remote repository named origin to our demo repository and then make a commit to my local repository, the history would like like this:

You can see that master is pointing to the latest commit while origin/master is pointing at the previous merge commit. Source Tree is even letting us know that we have 1 commit to push to the remote repository. If we do a push then Git will upload the missing commit and update your remote branch pointer to show that origin/master is now pointing at the same commit as your local master branch.

Hopefully you now have a better understanding of how merging works in Git. Jump on over to part 3 and let's dive into rebasing and see how it compares to merging.