简体   繁体   中英

In git, editing a historical pre-pull commit after the pull causes divergence from origin

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?

Note : I did not push; this has nothing to do with pushing.

Note : Simply pulling again does not fix the problem; it just re-merges 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.

To elaborate, one answer below suggests that the history now is:

- A - B' - C' - F' - G' - H' (master)
\
 D - E (origin/master)

This isn't quite right - it's actually:

- A - B' - C' - D(?) - E(?) - F' - G' - H' (master)
\
 D - E (origin/master)

Hence, my problem. I'd like it to be:

- A - B' - C' - F' - G' - H' (master)
\         /
 D - E (origin/master)

I'm guessing that you didn't use the -p option to git rebase, which means that the pull you did, merging commits D and E, was wiped away. If that's all that's happened, this is simple: just pull again. If this is what happened, your history will look like this:

- A - B' - C' - F' - G' - H' (master)
\
 D - E (origin/master)

There's your divergence!

If you did preserve the merge, then quite possibly this is just because something new was pushed to origin in the meantime:

- A - B' - C' - X' - F' - G' - H' (master)
\              /
 D - E -------- - Z (origin/master)

and so the solution, again, would be to pull.

If, however, you pushed something containing the original commit B, then you shouldn't have rebased. This will always cause divergence, and it's not a fun kind. Your history would look like this:

    B' - C' - F' - G' - H' (master)
   /
- A - B - C - X - F - G - H (origin/master)
\            /
 D - E ------

It's a fundamental fact of git's design that the SHA1 of a commit depends on the SHA1 of its parent, and therefore on the entire ancestry chain. This is why after the rebases, I've put a "prime" on everything. Even if you haven't modified C's content, the commit is modified. (The other possibility, that you preserved the merge, and pushed, should ve inferrable as a combination of the last two pictures.)

Note: I won't accept my own answer for a while because I'm genuinely curious about other (better) approaches.

The way I got out of this was hardly elegant:

# stash F, G, H - can't simply reorder them before D, E since they depend a lot on D, E
for i in {1..3}; do git reset HEAD^; git stash; done
# wipe out the weird non-merge D and E commits
git reset --hard HEAD~2
# pull to merge D, E again
git pull
# restore F, G, H
for i in {1..3}; do git stash pop; git commit -a; done

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM