In order:
They don't have to re-clone (although that will work), but they do have to do some extra work.
When you did the
rebase
you made copies of your old commits. Let's say commitB
was the one you "removed":A - B - C - D [old "febupdate", is what you pushed originally] \ C'- D' <-- HEAD=febupdate
Anyone who grabbed a copy of the first "push" has all three original commits,
B
C
andD
. Then when they go to grab the update they get the new commitsC'
andD'
. Let's say they added commitE
themselves. So they now have (before bringing in your update):A - B - C - D <-- origin/febupdate \ E <-- HEAD=febupdate
Once they bring in your update they get this:
A - B - C - D - E <-- HEAD=febupdate \ C'- D' <-- origin/febupdate
As far as git is concerned, when it look at this new state, it thinks they wrote commits
B
,C
,D
, andE
, and you wrote commitsC'
andD'
. Git offers them a chance to merge their four commits (B
throughE
) with your two.Even if they did not make a new commit
E
, git still offers them a chance to merge "their" three commits (B
throughD
) with your two. This will, in effect, "resurrect" commitB
.If they have no commits of their own (edit: and no unsaved work of course!), they can easily recover by simply forcing their
febupdate
to matchorigin/febupdate
:$ git checkout febupdate; git fetch; git reset --hard origin/febupdate
If they do have commits of their own (such as
E
), they need to bring those (and not any to-be-deleted ones) onto the new tip commitD'
, which they can do with eithergit cherry-pick
orgit rebase --onto
. The latter is a bit trickier to work1 but does everything in one fell swoop, as it were. The former (cherry-pick
) is easier to think about: they just renamefebupdate
tooops
, create a new local branchfebupdate
to trackorigin/febupdate
, thengit cherry-pick
each of their commits (the ones that are really theirs) fromoops
onto the newfebupdate
.The "proper" way is a matter of who's willing to do what kind of work and whether you and they are willing to allow the bad commit
B
to remain in the commit history. If everyone is willing to do all the above work, and you really don't wantB
to remain, that's the way to do it.If it's OK for
B
to remain, and you want to make it easy for people, you can usegit revert
. The jobgit revert
does is to add a new commit whose effect is simply "undo what was done in an old commit". Let's say that in commitB
you added a filebadfile
and removed a good line fromgoodfile
. The commandgit revert <sha-1-of-B>
will make a commit that removesbadfile
and puts the good line back intogoodfile
. Whatever happened inB
, revert "un-does" that and adds a new commit:A - B - C - D - R <-- HEAD=febupdate
Everyone expects new commits to be added, so this "just works" with whatever work-flow everyone already has.
Sort of, but if some people have already grabbed commits
C'
andD'
, whatever you do now will still cause someone some extra work (over and above their already-some-extra-work). In this case I'd probably just press on with making people do the recovery (as outlined in part (1) above).
1There is an upcoming feature in git 1.9/2.0 that should (at least in theory) make it less tricky to work. Also, as a special case, git pull --rebase
does the right thing in current git, but only if you have not already run git fetch
. (This is a little hard to describe.)