Let me describe my situation:
Mr Blond and Mr Orange are working on branch A that branches out of the master branch on commit M1. Branch A has 2 commits: A1 and A2.
M1
\
\
A1 - A2
Meanwhile, Mr Orange committed and pushed 2 more commits on the master branch, M2 and M3.
M1 - M2 - M3
\
\
A1 - A2
Mr Blond pulls from the remote, and after a while decides to rebase onto the master branch:
M1 - M2 - M3
\ \
\ \
A1 - A2 A1` - A2`
Now A1` and A2` are the rebased commits that exist locally at Mr blond's, and A1 and A2 exist remotely. Mr Blond pushes his commits, using -f to force his changes and "rewrite" history. Now the remote repository looks like this:
M1 - M2 - M3
\
\
A1` - A2`
But Mr. Orange worked on the A branch as well. His local repository still looks like this:
M1 - M2 - M3
\
\
A1 - A2
What does Mr. Orange need to do in order to be synchronized with A branch in the remote repository?
A normal pull won't work. Will pull -f force the changes from the remote locally? I know that deleting the local version of A and bringing it again from the remote repository will do the trick but that doesn't seem to be a good way to achieve that.
I develop concurrently on two virtual machines, for configuration purposes. As a result, I frequently rebase on one machine and need the changes to appear on the other without difficulty.
Suppose my branch is named
feature/my-feature-branch
. After finishing the rebase on the first VM, I do a git fetch on the second. The following message appears:Well, don't do a git pull, because then you end up with a meaningless merge commit after quite a bit of fussing.
Instead, run
git rebase -i origin/feature/my-feature-branch
Once the text editor pops up, delete all commits, and replace it with the following (this makes it so the rebase completes without any commits being kept).
exec echo test
If you do have commits that need to be kept, then those can be applied here. In either case, the rebase will complete, and now both machines are in sync again, as evidenced by:
If Mr. Orange doesn't mind losing his changes, he can fetch from the server, then
git checkout A2
to get onto his localA2
branch, then (presuming the remote is named "origin")git reset --hard origin/A2
to reset hisA2
to where the remote'sA2
is.If he is concerned with losing changes, he can merge the server's changes in to resolve them (from his own
A2
branch, and presuming again that the remote is named "origin") withgit merge origin/A2
. This will make a new commit that's on top of both his and the remote'sA2
branches with the changes from both merged together. Then this can be pushed back to the remote.My recommendation (or, "what I would do if I were Mr Orange") is, start with
git fetch
. Now I'll have this in my repo, which is what Mr Blond had after his rebase and just before he ran "git push -f".The one important difference is, I'll have my local label
A
pointing to rev A2, and the remote labelremotes/origin/A
pointing to A2' (Mr Blond had it the other way around, local labelA
pointing to A2' andremotes/origin/A
pointing to A2).If I've been working on my copy of the branch named "A" I'll have this instead:
(with my local label pointing to A3 rather than A2; or A4 or A5, etc, depending on how many changes I have applied.) Now all I have to do is rebase my A3 (and A4 if needed, etc) onto A2'. One obvious direct way:
and then drop revs A1 and A2 entirely, since the modified ones are in new_A as A1' and A2'. Or:
(the
git am -3 -k
method is described in thegit-format-patch
manual page).These do require figuring out what I have that Mr Blond didn't before he did his
rebase
, i.e., identifying A1, A2, A3, etc.If the second approach is successful I end up with:
where my branch name
new_A
points to A3' (my existingA
branch still points to the old A3). If I use the first approach and it succeeds, I end up with the same thing, it's just that my existing branch nameA
will now point to A3' (and I have no name for the old branch with A1-A2-A3, even though it's still in my repo; finding it requires going through reflogs or similar).(If my A3 needs modification to become A3', both the interactive rebase and the "git am" method will require work by me, of course.)
Of course it's also possible to just
git merge
(as in the answer by Gary Fixler), but that will create a merge commit ("M" with no number, below) and keep revs A1 and A2 visible, giving:If you want to preserve the original A1 and A2, this is a good thing; if you want to get rid of them, it's a bad thing. So "what to do" depends on "what you want the result to be".
Edit to add: I like the format-patch method better as it leaves my old A branch name around while I make sure everything is good. Assuming it all works and is good, here's the last few steps:
and then, if old_A can be abandoned entirely:
or, equivalently, start with the branch delete, then rename new_A to A.
(Edit: see also
git rebase --onto
documentation, for the goal of rebasing A3, etc., onto the new_A branch.)