What to do about so many empty merge commits?
I think it has to do with a branch "merging" up to master by using git merge
/pull
instead of using git rebase
.
If you make a change in a branch from a particular commit ID on master
, and then master
moves ahead, and you use git merge
to update that branch, git
gratuitously creates "merge commits" which cover the changes made to automatically merge the new changes since your change was made on master.
In contrast, a git rebase
(assuming you have properly setup remote tracking with git branch --set-upstream-to=...
) takes your commits (really the diffs those commits represent) and re-applies it to the latest on the tracking branch adjusting your commits to work on top of that rebase.
For example, let's say we have master with commits A, B, and C:
$ git branch --set-upstream-to=upstream/master master
$ git remote update
$ git pull
$ git log --oneline --decorate
1 C (HEAD, master, upstream/master) Change 3
2 B Change 2
3 A Change 1
And you create a branch for a new change:
$ git checkout -b work upstream/master
$ git commit -m "Change 4"
$ git log --oneline --decorate
1 D (HEAD, work) Change 4
2 C (master, upstream/master) Change 3
3 B Change 2
4 A Change 1
But now somebody else makes a commit to master before you get a change to push yours, and you use git remote update
to reflect those changes locally:
$ git remote update
$ git log --oneline --decorate
1 D (HEAD, work) Change 4
2 C Change 3
3 B Change 2
4 A Change 1
Your changes are still intact, and notice that your work is still commit D on top of commit C, but that commit C no longer has the upstream/master
label. If you have a local master
branch which has its upstream tracking set to upstream/master
, then after you use git pull
to bring it up-to-date, you'd likely see:
$ git checkout master
$ git pull
$ git log --oneline --decorate
1 E (HEAD, master, upstream/master) Change 5
2 C Change 3
3 B Change 2
4 A Change 1
If you were to then use git merge
to incorporate those changes into your work
branch, you'd likely see:
$ git checkout work
$ git merge upstream/master
$ git log --oneline --decorate
1 F (HEAD, work) Merge remote-tracking branch 'upstream/master'
2 E (master, upstream/master) Change 5
3 D Change 4
4 C Change 3
5 B Change 2
6 A Change 1
Because D
("Change 4") was your work based on C
, but F
contains the changes made by the merge performed on top of E
.
If you were to perform a git rebase
instead of git merge
, then you'd likely see:
$ git checkout work
$ git rebase
$ git log --oneline --decorate
1 D (HEAD, work) Change 4
2 E (master, upstream/master) Change 5
3 C Change 3
4 B Change 2
5 A Change 1
Does that make sense?