The distributed version control system called Git has changed the way developers think of code merging and branching, compared to the previous version control systems like CVS and SVN.
With Git, these operations became relatively easy and fast to do. This system allows developers to create new features isolated from the master branch and integrate them at a later point when the feature is ready for integration.
If you are new to Git, the following links should help get you started quickly:
In Git, there are two main ways to integrate changes from one branch into another:
We will look at merging with the -ff option and the –no-ff option. Note: there is no git –no-ff rebase command.
A Quick Demo Setup
Before we look at the two ways of Git merging, let’s first set up a working Git repository with the following commands:
echo “foo bar baz” > mycode/foo.txt
git init mycode
git add foo.txt
git commit -m “Commit message”
Let’s now create a new branch named “myfeature” and switch to it:
git checkout -b myfeature
Now we can make a change to the “foo.txt” file in the “myfeature” branch of our local repo:
echo -e “foo bar baz\nquux” > foo.txt
git commit -a -m ‘added myfeature’
Let’s assume our “myfeature” change is complete, and now we would like to integrate this feature into our “master” branch.
Our git graph would look like this:
As stated before about Git, we have two ways to go about this: either doing a merge or a rebase.
How to Use: git merge
The git merge command joins two or more branches together.
First, let’s switch back to the “master,” so that we can apply the merge on our main branch.
git checkout master
We can now do the merging, but let’s first discuss the two different ways to create a merge.
Often the current branch head is an ancestor of the named commit (“myfeature”). This is the most common case. In this scenario, a new commit is not needed to store the combined history. Instead, the git HEAD (along with the index) is updated to point at the named commit without creating an extra merge commit.
The default behavior of git merge is to do a a fast-forward merge when possible. This default behavior can be made explicit with the -ff option, or this behavior can be suppressed with the no-fast-forward merge (–no-ff) option. When merging an annotated tag, Git always creates a merge commit even if a fast-forward merge is possible.
When using –no-ff, someone reviewing the git history can clearly see the branch you checked out to work on. Also, note how the –no-ff merge has an extra merge commit at the end.
Below is an image that shows the difference between a –no-ff and -ff merge.
So you now have the option of using either:
git merge myfeature
After this command, our git graph would look like this:
A - B master
Or we could keep the branch history by doing:
git merge –no-ff myfeature
In this latter case, our git graph would now look like this:
A - C master
And if we execute the following git log command:
git log –graph –full-history –all –pretty=format:“%h%x09%d%x20%s”
This will show a similar git graph:
*5368727 (HEAD, master) Merge branch ‘myfeature’
| * 6267227 (myfeature) added myfeature
*ac54e38 Commit message
Which type of merge you prefer depends on how much of the branch information you want to store when you do a merge.
How to Use: git rebase
The second way of integrating changes from one branch into another is to do a git rebase.
When merging brings two branches together while preserving the graph of each commit history, rebasing unifies the branches by rewriting changes from the source branch so they appear as children of the destination branch.
The git rebase forward-ports local commits to the updated upstream head. Rebasing is the process of moving a branch to a new base commit.
This is what that looks like in a git graph. Assume the following history exists and the current branch is “myfeature”:
A — B — C myfeature
D — E — F — G master
And if we now do:
git rebase master
The result would be:
The “master” branch now contains the “myfeature” branch changes at commit “G” without creating a merge commit. So instead of joining the branches with a merge commit, rebasing integrates the “myfeature” branch by building on top of master. The result is a linear history that may be easier to understand for developers. The git log would now show the linear history of “D—E—F—G master”.
In case of conflict, git rebase will stop at the first problematic commit and leave conflict markers in the tree.
Merge or Rebase?
Both methods of code integration should be used when they are best suited. There are different opinions on when a certain method should be used.
Here are some links that describe situations when one method might be preferred over the other: