We have all heard that one should never rebase published work, that it’s dangerous, etc. However, I have not seen any recipes posted for how to deal with the situation in case a rebase is published.
Now, do note that this is only really feasible if the repository is only cloned by a known (and preferably small) group of people, so that whoever pushes the rebase or reset can notify everyone else that they will need to pay attention next time they fetch(!).
One obvious solution that I have seen will work if you have no local commits on foo
and it gets rebased:
git fetch
git checkout foo
git reset --hard origin/foo
This will simply throw away the local state of foo
in favour of its history as per the remote repository.
But how does one deal with the situation if one has committed substantial local changes on that branch?
Starting with git 1.9/2.0 Q1 2014, you won't have to mark your previous branch origin before rebasing it on the rewritten upstream branch, as described in Aristotle Pagaltzis's answer:
See commit 07d406b and commit d96855f :
That is why the
git merge-base
command has a new option:For example, if the history looked like where:
Git 2.1 (Q3 2014) will add make this feature more robust to this: see commit 1e0dacd by John Keeping (
johnkeeping
)correctly handle the scenario where we have the following topology:
where:
B'
is a fixed-up version ofB
that is not patch-identical withB
;C*
andD*
are patch-identical toC
andD
respectively and conflict textually if applied in the wrong order;E
depends textually onD
.The correct result of
git rebase master dev
is thatB
is identified as the fork-point ofdev
andmaster
, so thatC
,D
,E
are the commits that need to be replayed ontomaster
; butC
andD
are patch-identical withC*
andD*
and so can be dropped, so that the end result is:If the fork-point is not identified, then picking
B
onto a branch containingB'
results in a conflict and if the patch-identical commits are not correctly identified then pickingC
onto a branch containingD
(or equivalentlyD*
) results in a conflict.I'd say the recovering from upstream rebase section of the git-rebase man page covers pretty much all of this.
It's really no different from recovering from your own rebase - you move one branch, and rebase all branches which had it in their history onto its new position.
Getting back in synch after a pushed rebase is really not that complicated in most cases.
Ie. first you set up a bookmark for where the remote branch originally was, then you use that to replay your local commits from that point onward onto rebased remote branch.
Rebasing is like violence: if it doesn’t solve your problem, you just need more of it. ☺
You can do this without the bookmark of course, if you look up the pre-rebase
origin/foo
commit ID, and use that.This is also how you deal with the situation where you forgot to make a bookmark before fetching. Nothing is lost – you just need to check the reflog for the remote branch:
This will print the commit ID that
origin/foo
pointed to before the most recent fetch that changed its history.You can then simply