* How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
@ 2010-11-20 0:36 Yang Zhang
2010-11-20 1:59 ` Yang Zhang
2010-11-20 4:36 ` Jeff King
0 siblings, 2 replies; 8+ messages in thread
From: Yang Zhang @ 2010-11-20 0:36 UTC (permalink / raw)
To: git
In the following scenario:
1. Make commits A, B, C
2. Pull, getting commits D, E
3. Make more commits F, G, H, ...
4. Realize that you need to tweak B
5. Tweak B using git rebase -i and git commit --amend
Now git status says:
Your branch and 'origin/master' have diverged.
How should I fix this? Thanks.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
2010-11-20 0:36 How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull? Yang Zhang
@ 2010-11-20 1:59 ` Yang Zhang
2010-11-20 4:36 ` Jeff King
1 sibling, 0 replies; 8+ messages in thread
From: Yang Zhang @ 2010-11-20 1:59 UTC (permalink / raw)
To: git
Clarification:
Simply pulling again does not fix the problem; it just reapplies the
commits D, E on top of what I already have (despite the D, E already
being there in the history), which also has the side-effect of
producing superfluous conflicts.
On Fri, Nov 19, 2010 at 4:36 PM, Yang Zhang <yanghatespam@gmail.com> wrote:
> In the following scenario:
>
> 1. Make commits A, B, C
> 2. Pull, getting commits D, E
> 3. Make more commits F, G, H, ...
> 4. Realize that you need to tweak B
> 5. Tweak B using git rebase -i and git commit --amend
>
> Now git status says:
>
> Your branch and 'origin/master' have diverged.
>
> How should I fix this? Thanks.
>
--
Yang Zhang
http://yz.mit.edu/
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
2010-11-20 0:36 How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull? Yang Zhang
2010-11-20 1:59 ` Yang Zhang
@ 2010-11-20 4:36 ` Jeff King
2010-11-20 20:12 ` Yang Zhang
2010-11-21 14:45 ` Gavin Guo
1 sibling, 2 replies; 8+ messages in thread
From: Jeff King @ 2010-11-20 4:36 UTC (permalink / raw)
To: Yang Zhang; +Cc: git
On Fri, Nov 19, 2010 at 04:36:46PM -0800, Yang Zhang wrote:
> In the following scenario:
>
> 1. Make commits A, B, C
> 2. Pull, getting commits D, E
> 3. Make more commits F, G, H, ...
> 4. Realize that you need to tweak B
> 5. Tweak B using git rebase -i and git commit --amend
>
> Now git status says:
>
> Your branch and 'origin/master' have diverged.
>
> How should I fix this? Thanks.
When you change a commit, you also change every commit after it (since
each points to its predecessor by a parent pointer). So you create an
alternate history. IOW, you had this:
A--B--C
and pulled to get this:
A--B--C--D--E
or maybe this:
A--B--C--Merge
/
D--E
depending on how those other commits relate to what you have. Then you
made more commits, like (let's look at the non-merge case):
A--B--C--D--E--F--G--H
Then you tweaked B. The rebase replayed every commit after that, but
each one is not exactly the same as the other. So now you have:
B--C--D--E--F--G--H
/
A--B'--C'--D'--E'--F'--G'--H'
where the top branch of history is what you used to have (and is
accessible via the reflog as branch@{1}). But your actual branch is at
H'. So if you "git pull" again, as you tried, it will try to merge D and
E from upstream. So:
D--E
\
A--B'--C'--D'--E'--F'--G'--H'--Merge
which is definitely not what you want. The problem is that you have
rewritten upstream's commits, because you rebased across a set of
commits that contained things you had pulled.
To fix it, what you want to do is recreate the history on top of B' as
it happened on top of B. So first you go back to C', the last commit
just before the commits from upstream that were rewritten. (you will
have pick its sha1 out of the log):
git checkout -b temp B'
You should then have:
A--B'--C'
on a temporary branch. Now re-pull from upstream (you could also
manually rebase those commits, but this is probably simpler, especially
if there actually was a merge):
git pull remote_name branch_name
Note that you need to explicitly mention where you pulled from, since
the temp branch will not be configured to pull in the same way (if you
don't have any special config set up, it should be "git pull origin
master").
And now you have:
A--B'--C'--D--E
at which point we can rebase the last bit of your branch on top:
git rebase --onto temp F'^ branch_name
where "branch_name" is the name of the branch where this mess happened
(presumably "master"), and F' is the first commit that is worth saving
after you pulled from upstream. And that gives you:
A--B'--C'--D--E--F''--G''--H''
where F'' corresponds to the original F, but actually has a different
commit id (because of different parentage) than F or F'.
At that point your original branch should be in the state you want. You
can delete the temp branch with "git branch -D temp".
So that's the most general way to do it. It's a little convoluted
because of the way rebase works (you can't say "rebase those commits on
top of me", but rather have to say "rebase me on top of these commits",
which leads us to use the temporary branch).
Depending on the relationship of F, G, and H to D and E, it could be
much simpler to just re-order history. So from your your broken state,
which remember is:
A--B'--C'--D'--E'--F'--G'--H'
do a "git rebase -i", and just _delete_ all of the commits you pulled
from upstream (D and E in this case). Now you have:
A--B'--C'--F''--G''--H''
and now repeat your pull to get:
A--B'--C'--F''--G''--H''--D--E
Which is very simple and straightforward. But it relies on it being
acceptable for F, G, and H to come before D and E in the history.
Whew, that turned out long. Hopefully it helps, and did not just confuse
you more. :)
-Peff
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
2010-11-20 4:36 ` Jeff King
@ 2010-11-20 20:12 ` Yang Zhang
2010-11-21 14:45 ` Gavin Guo
1 sibling, 0 replies; 8+ messages in thread
From: Yang Zhang @ 2010-11-20 20:12 UTC (permalink / raw)
To: Jeff King; +Cc: git
On Fri, Nov 19, 2010 at 8:36 PM, Jeff King <peff@peff.net> wrote:
> Whew, that turned out long. Hopefully it helps, and did not just confuse
> you more. :)
That was a *fantastic* explanation. Thank you!
--
Yang Zhang
http://yz.mit.edu/
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
2010-11-20 4:36 ` Jeff King
2010-11-20 20:12 ` Yang Zhang
@ 2010-11-21 14:45 ` Gavin Guo
2010-11-21 16:30 ` Jeff King
1 sibling, 1 reply; 8+ messages in thread
From: Gavin Guo @ 2010-11-21 14:45 UTC (permalink / raw)
To: Jeff King; +Cc: Yang Zhang, git
> To fix it, what you want to do is recreate the history on top of B' as
> it happened on top of B. So first you go back to C', the last commit
> just before the commits from upstream that were rewritten. (you will
> have pick its sha1 out of the log):
>
> git checkout -b temp B'
I think you mean git checkout -b temp C'
> You should then have:
>
> A--B'--C'
>
> on a temporary branch. Now re-pull from upstream (you could also
> manually rebase those commits, but this is probably simpler, especially
> if there actually was a merge):
>
> git pull remote_name branch_name
>
> Note that you need to explicitly mention where you pulled from, since
> the temp branch will not be configured to pull in the same way (if you
> don't have any special config set up, it should be "git pull origin
> master").
>
> And now you have:
>
> A--B'--C'--D--E
>
> at which point we can rebase the last bit of your branch on top:
>
> git rebase --onto temp F'^ branch_name
>
> where "branch_name" is the name of the branch where this mess happened
> (presumably "master"), and F' is the first commit that is worth saving
> after you pulled from upstream. And that gives you:
>
> A--B'--C'--D--E--F''--G''--H''
>
> where F'' corresponds to the original F, but actually has a different
> commit id (because of different parentage) than F or F'.
>
> At that point your original branch should be in the state you want. You
> can delete the temp branch with "git branch -D temp".
I'm sorry that I can't understand "your original branch should be in
the state you want" ?
You only create a temp branch, and rebase some commits on it, right ??
What does that related to original branch ??
> So that's the most general way to do it. It's a little convoluted
> because of the way rebase works (you can't say "rebase those commits on
> top of me", but rather have to say "rebase me on top of these commits",
> which leads us to use the temporary branch).
Gavin Guo
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
2010-11-21 14:45 ` Gavin Guo
@ 2010-11-21 16:30 ` Jeff King
2010-11-21 18:51 ` Yang Zhang
0 siblings, 1 reply; 8+ messages in thread
From: Jeff King @ 2010-11-21 16:30 UTC (permalink / raw)
To: Gavin Guo; +Cc: Yang Zhang, git
On Sun, Nov 21, 2010 at 10:45:13PM +0800, Gavin Guo wrote:
> > To fix it, what you want to do is recreate the history on top of B' as
> > it happened on top of B. So first you go back to C', the last commit
> > just before the commits from upstream that were rewritten. (you will
> > have pick its sha1 out of the log):
> >
> > git checkout -b temp B'
>
> I think you mean git checkout -b temp C'
Yes, sorry. You should base your temp branch on C'.
> > git rebase --onto temp F'^ branch_name
> [...]
> > At that point your original branch should be in the state you want. You
> > can delete the temp branch with "git branch -D temp".
>
> I'm sorry that I can't understand "your original branch should be in
> the state you want" ?
> You only create a temp branch, and rebase some commits on it, right ??
> What does that related to original branch ??
The three-argument form of rebase above will switch to branch_name (your
original branch), consider F'^ as the upstream, and rebase
F'^..branch_name on top of the commits in "temp".
-Peff
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
2010-11-21 16:30 ` Jeff King
@ 2010-11-21 18:51 ` Yang Zhang
2010-11-22 5:38 ` Jeff King
0 siblings, 1 reply; 8+ messages in thread
From: Yang Zhang @ 2010-11-21 18:51 UTC (permalink / raw)
To: Jeff King; +Cc: Gavin Guo, git
On Sun, Nov 21, 2010 at 8:30 AM, Jeff King <peff@peff.net> wrote:
> On Sun, Nov 21, 2010 at 10:45:13PM +0800, Gavin Guo wrote:
>
>> > To fix it, what you want to do is recreate the history on top of B' as
>> > it happened on top of B. So first you go back to C', the last commit
>> > just before the commits from upstream that were rewritten. (you will
>> > have pick its sha1 out of the log):
>> >
>> > git checkout -b temp B'
>>
>> I think you mean git checkout -b temp C'
>
> Yes, sorry. You should base your temp branch on C'.
>
>> > git rebase --onto temp F'^ branch_name
>> [...]
>> > At that point your original branch should be in the state you want. You
>> > can delete the temp branch with "git branch -D temp".
>>
>> I'm sorry that I can't understand "your original branch should be in
>> the state you want" ?
>> You only create a temp branch, and rebase some commits on it, right ??
>> What does that related to original branch ??
>
> The three-argument form of rebase above will switch to branch_name (your
> original branch), consider F'^ as the upstream, and rebase
> F'^..branch_name on top of the commits in "temp".
>
> -Peff
>
Actually, I missed this detail earlier, and now like Gavin I'm
confused. *temp* is "in the state that you want," not original_branch,
right? temp shouldn't be deleted just yet; master should be updated to
point to this....
--
Yang Zhang
http://yz.mit.edu/
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?
2010-11-21 18:51 ` Yang Zhang
@ 2010-11-22 5:38 ` Jeff King
0 siblings, 0 replies; 8+ messages in thread
From: Jeff King @ 2010-11-22 5:38 UTC (permalink / raw)
To: Yang Zhang; +Cc: Gavin Guo, git
On Sun, Nov 21, 2010 at 10:51:51AM -0800, Yang Zhang wrote:
> >> > git rebase --onto temp F'^ branch_name
> >> [...]
> >> > At that point your original branch should be in the state you want. You
> >> > can delete the temp branch with "git branch -D temp".
> >>
> >> I'm sorry that I can't understand "your original branch should be in
> >> the state you want" ?
> >> You only create a temp branch, and rebase some commits on it, right ??
> >> What does that related to original branch ??
> >
> > The three-argument form of rebase above will switch to branch_name (your
> > original branch), consider F'^ as the upstream, and rebase
> > F'^..branch_name on top of the commits in "temp".
> >
> > -Peff
> >
>
> Actually, I missed this detail earlier, and now like Gavin I'm
> confused. *temp* is "in the state that you want," not original_branch,
> right? temp shouldn't be deleted just yet; master should be updated to
> point to this....
No, the first thing rebase will do is switch back to your
original_branch, and then it will rebase the extra commits (the
rebased versions of things that happened after your initial pull), on
top of the new partial history in temp.
Yeah, the arguments to rebase are weird. In a simpler world you would
do:
# assume we're on master, the broken branch; mark the point with a tag
git tag broken
# go back to just before the broken rewritten commits
git reset --hard C'
# now re-do the merge
git pull origin master
# and now grab the other commits from our broken state
git cherry-pick F'^..broken
except that cherry-pick doesn't actually walk the commit range as you
want it to. I think you can do:
git cherry-pick F' G' H'
these days, so that is another option.
Anyway, just for fun I put together a script which graphically shows
your situation at each step. You can run it all at once, but it is
probably more instructive to cut and paste into a terminal, reading all
of the comments.
-Peff
-- >8 --
#!/bin/sh
# clean up any previous invocations
rm -rf parent child
# short helper function for making our commits
commit() {
echo $1 >$1 && git add $1 && git commit -m $1 && git tag $1
}
# short helper to show state
show() {
# or gitk "$@" if you prefer
git log --oneline --graph --decorate "$@"
}
# make a parent and child with some shared base
mkdir parent && (cd parent && git init && commit base)
git clone parent child
# now child and parent diverge. child has a-b-c,
# parent has d-e
(cd child && commit A && commit B && commit C) &&
(cd parent && commit D && commit E) &&
# now let's recreate the problem situation. Everything
# now happens in the child.
cd child
# First we pull the parent's commits into the child
git pull origin master
# And build on top of it
commit F && commit G && commit H
# And then we "rebase -i", rewriting B
GIT_EDITOR='perl -pi -e "s/pick (.* B)/edit \$1/"' git rebase -i base
echo changes >>B && git commit --amend -a -m B
git rebase --continue
# Now we have the broken state, because we rewrote parent's commits during our
# rebase. We also failed to preserve merges, so the new history appears linear.
# You can see the repeated commits easily by looking at the history graph of
# our new state versus our old.
#
# Let's also go ahead and tag the original history and each of the new commits
# so we can recognize and refer to them. Here H-new corresponds to H' in my
# other explanation, and so on.
git branch original-history master@{1}
git tag H-new HEAD
git tag G-new HEAD~1
git tag F-new HEAD~2
git tag E-new HEAD~3
git tag D-new HEAD~4
git tag C-new HEAD~5
git tag B-new HEAD~6
show --all
# So now let's look at the solution. First we make a temporary branch from the
# rewritten commit just prior to the ones from upstream (in this case, C').
git checkout -b temp C-new
show temp
# Now re-pull from upstream, recreating the merge on top of your rewritten
# commits.
git pull origin master
show temp
# And now rebase the rewritten versions of all of the commits that came after
# the merge. We know F-new is the first such rewritten commit, so its parent
# (F-new^) becomes the upstream. We are rebasing onto the state we have in
# temp, and we are rebasing the branch master (and the end result will go on
# master).
git rebase --onto temp F-new^ master
show master
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2010-11-22 5:38 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-20 0:36 How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull? Yang Zhang
2010-11-20 1:59 ` Yang Zhang
2010-11-20 4:36 ` Jeff King
2010-11-20 20:12 ` Yang Zhang
2010-11-21 14:45 ` Gavin Guo
2010-11-21 16:30 ` Jeff King
2010-11-21 18:51 ` Yang Zhang
2010-11-22 5:38 ` Jeff King
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).