Understanding Git merge & Git rebase
In this blog, we will try to understand the most important concept of Git such as git merge and git-rebase.
Today’s Motivation
“Progress is impossible without change and those who cannot change their minds cannot change anything.”
Let’s get started… 🙂
What is the use of git merge?
Isolating features into different branches is a crucial practice for any serious developer. By separating each feature, bugfix or working experiment you will avoid a lot of problems and keep your development branches clean.
At some point, a piece of code will reach a state where you’ll want to integrate it with the rest of the project. This is where the git merge command comes in.
Preparing to Merge
Let’s assume you want to merge branch hotfix
into yourmaster branch
.
Before you start, how to make sure that you are ready to merge changes?
- Check if your local repository is up to date with the latest changes from your remote server with a
git fetch
. - Once the fetch is completed
git checkout master
. - Ensure the master branch has the latest updates by executing
git pull
. - Checkout to the master branch that should receive the changes, in our case that is master.
Let’s have a practical demonstration.
for e.g: Let’s create another branch called hotfix and do some modifications in the hotfix branch. Firstly let’s have a look at our master branch.
This is what our master branch looks like with 3 commits. Let’s view our commit history 👇.
Let’s switch to the master branch.
Now, add one more file named “four.txt” and commit to the hotfix branch. Then, push the changes to the remote server.
We successfully pushed all the changes to our remote repository. 👇
Let’s have a view of our remote repository as well.
Once the preparations are completed, you can start the merge with git merge hotfix
command and view the commit history.
You might be thinking of what is fast-forward which was shown in the image above. Let’s try to understand deeply.
Fast Forward Merge
A fast-forward merge can occur when there is a linear path between branches that you want to merge. If the master branch has not diverged, instead of creating a new commit, it will just point master to the latest commit of the hotfix branch. All commits from the hotfix branch are now available in the master branch.
As you see in the above image, you can see the difference in the before merge and after merge diagram.
In before merge:
The master branch is pointed to the hash key c4b7
commit which was the last commit performed in the master branch. When we created a new branch hotfix
and switch to it, the hotfix
branch will also be pointed to the hash key c4b7
commit where the master branch is also pointing because of the branch hotfix
is just created and that’s why it will point to the same commit where the master is pointing. As soon as, you make modifications in the hotfix
branch in our case, we added “four.txt” file then the hotfix will point to the hash key 2115
.
In after merge:
In after merge scenario, we see that both master
and hotfix
branch pointing to the same hash key 2115
commit because we made modifications in the hotfix branch and the hash key 2115
is pointing to the hash key c4bd
commit means hash key 2115
is the child of the c4bd
hash key. It has child-parent relation and it’s in a linear line that’s why git automatically does “fast-forwarding”. If we also make changes in the master branch after making changes in the hotfix branch then git will create another merge commit.
Let’s view our commit history after the merge.
However, a fast-forward merge is not possible if the branches have diverged. In this case, you want to use a Three-way merge.
Three-way Merge
When there is not a linear path to the target branch, Git has no choice but to combine them via a three-way merge. This merge uses an extra commit to tie together the two branches.
In before merge:
In the before merge diagram, we created “fifth.txt” file and commit it on the master branch and HEAD is pointed to the 4210
hash commit because it is the last commit on the branch master
. The hotfix branch commit hash key 836f
is the child of c4b7
hash key. i.e the commits are not linear. So, git will do a three-way merge commit by creating a new merge commit.
In after merge:
In the after merge diagram, we merged both the master
and hotfix
branch and git created a new merge commit block which having a hash key 7999
. The parent of 7999
commit is 4210
and 836f
hash key. Once you merge the hotfix branch then you can delete it to make your master branch looks clean. That’s why the only master is pointing to the 7999
commit.
When you merge the branch using the command git merge <branch-name>
. This command will open a vi editor to write the commit message. To write the message press i
key on the keyboard. 👇
After writing the commit message close the editor by pressing esc
key and write :wq
to quit.
Let’s have a look at our commit history.
How to Deal With Merge Conflicts
A merge conflict occurs when two branches you’re trying to merge both changed the same part of the same file, Git won’t be able to figure out which version to use. When such a situation occurs, it stops right before the merge commit so that you can resolve the conflicts manually. Let’s see a practical demonstration of how the merge conflict occurs.
As you see in the image above, the merge conflict has occurred. But how? Let’s find out, Firstly, you created a new file named “first.txt” in the master branch and modify it with some content as “Hello, World” and commit it and the hash key for this commit is ce3d
. Now, you created another branch named ‘conflict-example’ and again modify the first.txt file with some content as “Hello, world, Welcome to programming world” and commit it and the hash key for this commit is 71ff
. Now, you again switched to the master
branch from the conflict-example
branch and again modify the first.txt file and commit it with some content as “Hello, world, Welcome to GitHub”. Now, you think that changes are over let's merge both the branches and here you will get the merge error because you change the same line in both the commits and git is unable to decide which commit to merge that’s why git throw merge conflict error.
To resolve the merge error open your “first.txt” file here you see the conflict.
When the conflicted line is encountered, Git will edit the content of the affected files with visual indicators that mark both sides of the conflicting content. These visual markers are:
<<<<<<<
: Conflict marker, the conflict starts after this line.=======
: Divides your changes from the changes in the other branch.>>>>>>>
: End of the conflicting lines.
- Decide if you want to keep only your
conflict-example
ormaster
branch changes, or write completely new code. Delete the conflict markers before merging your changes. In our case, we only keep themaster
branch changes. Which is shown in the image below:
2. When you ready to merge, all you have to do is run the command git add
on the conflicted files to tell Git they’re resolved.
3. Commit your changes with the command git commit
to generate a merge commit.
Let’s have a look at our commit history.
Hope this helped you get a better understanding of how to merge your branches and deal with conflicts.
A better workflow with Git rebase
The goal of both merging and rebasing is to take commits from a feature branch and put them onto another branch. As you studied about Merging in the above section let’s revise one more in a short time.
Let’s say, I have a graph that looks like this. As you can see, I split off my feature branch at commit 2, and have done a bit of work.
If I run a merge, git will stuff all my changes from my feature branch into one large merge commit that contains ALL of my feature branch changes. It will then place this special merge commit onto the master branch. When this happens, the tree will show your feature branch, as well as the master branch. Going further, if you imagine working on a team with other developers, your git tree can become complex: displaying everybody else’s branches and merges.
Rebasing
Now let’s take a look at how rebase would handle this same situation. Instead of doing a git merge, I’ll do a git-rebase
. What rebase will do is take all of the commits on your feature branch and move them on top of the master commits. Behind the scenes, git is actually blowing away the feature branch commits and duplicating them as new commits on top of the master branch. What you get with this approach is a nice clean tree with all your commits laid out nicely in a row, like a timeline. Easy to trace.
The Golden Rule of Rebasing
Once you understand what rebasing is, the most important thing to learn is when not to do it. The golden rule of git rebase is to never use it on public branches.
For example, think about what would happen if you rebased master onto your feature branch:
The rebase moves all of the commits in master onto the tip of the feature. The problem is that this only happened in your repository. All of the other developers are still working with the original master. Since rebasing results in brand new commits, Git will think that your master branch’s history has diverged from everybody else’s.
The only way to synchronize the two master branches is to merge them back together, resulting in an extra merge commit and two sets of commits that contain the same changes (the original ones, and the ones from your rebased branch). Needless to say, this is a very confusing situation.
So, before you run git rebase, always ask yourself, “Is anyone else looking at this branch?” If the answer is yes, take your hands off the keyboard and start thinking about a non-destructive way to make your changes. Otherwise, you’re safe to re-write history as much as you like.
Let’s have a practical demo:
I have created the “first.txt” and “second.txt” file in the master branch and commit it.
Let’s view our commit history on the master branch until now.
Next, I’ll check out a new branch named “feature” so I can write and commit code to this branch — keeping my work separated from the master branch. As I’m developing my feature, I’ll make a few commits.
Let’s view our commit history on the feature branch until now.
Note: While I’m developing its likely that my fellow developers will have shipped some of their changes to remote master. That’s ok, we can deal with that later.
Let’s say your fellow developers create a new file named “third.txt” in the master branch and pushed it to the remote repo. Now, our code is not synced with the latest updates which were done in the remote repo. So to sync, we use the command git pull
.
Let’s view again our master commit history.
As you see, the third file was added.
Now that I’m done developing my feature, I want to merge the changes onto the master branch. To do this, I’ll check out my feature branch and rebase against my local master. This will re-anchor my branch against the latest changes. Additionally, at this point, Git will let me know if I have any conflicts and I can take care of them on my branch.
The gitk is the git repository browser. This is what happens when running the command git rebase master
. The feature replay on top of the master branch.
Note that my feature branch doesn’t have any conflicts, I can switch back to my master branch and place my changes onto master.
Let’s see a visual representation using the gitk command.
As you see, after running the command git rebase feature
the master is pointing where the feature is pointed. i.e all the commits are in a single line and rewrite the commit history.
Now, I should be able to push my changes up to remote master without issues using the command git push
.
I hope this blog has been useful.
If you really enjoyed reading this blog, give me a clap 👏 and share it :-)
Thank you.