It looks like the first merge was a fast-forward, and the second one was a three-way merge.
Explanation
Git has two versions of merge: fast-forward and three-way. (There are other versions, but that is not what happened here.) The default behavior is to do a fast-forward merge when possible, and otherwise do a three-way merge.
A fast-forward merge (you can force this behavior with the option --ff-only
, which will cause the merge to fail when fast-forward is impossible) can take place when the commit that is being merged has the current position of the branch in its history. For example:
A - B - C - D <-master
E - F - G <- branch-a
Excecuting git merge
(with default settings) will result in
A - B - C - D - E - F - G <- branch-a <-master
You will also not get a chance to edit the merge commit because there is none. However, once this happens, your other branch will diverge from master (not just be ahead):
A - B - C - D - E - F - G <-master
E1 - E2 <- branch-b
Therefore, Git cannot just move the pointer of master from G
to E2
because that will get rid of the changes that were made in F
and G
. Instead a three-way merge happens, which create a commit that has two parents, and also has a commit message. Now, master can be moved to this commit. (Notice that in this situation, master and branch-b do NOT point to the same commit.
A - B - C - D - E - F - G - H <-master
/
E1 - E2 <- branch-b
If you want to have a linear history then you need to use rebase, but be forewarned that if anybody else has seen your branch commits this may lead to issues that are beyond the scope of this answer. Using rebase will involve two steps, rebasing and then fast-forward merge. So, instead of merging you first execute the following while on branch-b, git rebase master
. This creates new commits that are copies of the old commits, i.e., the same change-set, author information and message, but new committer information and parent history. (I call the commits E1' and E2' in the illustration to indicate that they are just copies.) The old commits will exist until they are garbage collected, but will not be reachable unless you look at the reflog.)
A - B - C - D - E - F - G <-master
E1 - E2
E1' - E2' <- branch-b
Executing git checkout master; git merge --ff-only branch-b
will now fast-forward your changes into master, thereby giving you a linear history.
A - B - C - D - E - F - G - E1' -E2' <-master <- branch-b