Re: How to fix “Your branch and 'origin/master' have diverged” after editing a commit that came before a pull?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]