I understand the scenario presented in Pro Git about The Perils of Rebasing. The author basically tells you how to avoid duplicated commits:
Do not rebase commits that you have pushed to a public repository.
I am going to tell you my particular situation because I think it does not exactly fit the Pro Git scenario and I still end up with duplicated commits.
Let's say I have two remote branches with their local counterparts:
origin/master origin/dev
| |
master dev
All four branches contains the same commits and I am going to start development in dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4
After a couple of commits I push the changes to origin/dev
:
origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4
origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit
I have to go back to master
to make a quick fix:
origin/master : C1 C2 C3 C4 C7 # (2) git push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6
And back to dev
I rebase the changes to include the quick fix in my actual development:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master
If I display the history of commits with GitX/gitk I notice that origin/dev
now contains two identical commits C5'
and C6'
which are different to Git. Now if I push the changes to origin/dev
this is the result:
origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7
origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push
dev : C1 C2 C3 C4 C7 C5' C6'
Maybe I don't fully understand the explanation in Pro Git, so I would like to know two things:
C5
and C6
after C7
?You should not be using rebase here, a simple merge will suffice. The Pro Git book that you linked basically explains this exact situation. The inner workings might be slightly different, but here's how I visualize it:
C5
and C6
are temporarily pulled out of dev
C7
is applied to dev
C5
and C6
are played back on top of C7
, creating new diffs and therefore new commitsSo, in your dev
branch, C5
and C6
effectively no longer exist: they are now C5'
and C6'
. When you push to origin/dev
, git sees C5'
and C6'
as new commits and tacks them on to the end of the history. Indeed, if you look at the differences between C5
and C5'
in origin/dev
, you'll notice that though the content is the same, the line numbers are probably different -- which makes the hash of the commit different.
I'll restate the Pro Git rule: never rebase commits that have ever existed anywhere but your local repository. Use merge instead.