5

我在一个 git repo 工作,我们维护一个 master 和一个开发分支。在发布前在开发中进行更改并合并到主中。今天不小心把master合并到develop,结果推到develop。A git login develop 现在显示merge develop into master提交。我需要以某种方式解决这个问题。我使用git reflog. 恢复它的正确方法是什么?

由于我已经推送,所以我想避免重写历史。但是,我不确定我是否可以使用git revert. 我不相信“恢复错误的合并”操作方法和其他 SO 问题适用,因为我想要恢复的提交本身就是合并(https://www.kernel.org/pub/software/scm/git/docs /howto/revert-a-faulty-merge.txt)。

4

4 回答 4

10

You can use git revert to revert the merge. Just be aware that when you do that, you make a future "re-merge" more difficult. Read that message you linked closely—it says a lot about how to make the future "re-merge" work—but be aware that it's talking about merging develop into master, and you said you did the opposite, merged master into develop:

A -- B -- C -- D -- E          <-- master
      \          \
       F - G - H - M           <-- develop

In this case (having merged master into develop instead of the other way around) you can choose a number of different options, all with advantages and drawbacks...

The easiest method, which I prefer if it's an option

(0) Do nothing. If merging in C and D doesn't break the develop branch, just leave it that way. Later, someone will git checkout master and git merge --no-ff develop and get this (let's say one more commit I got added to develop first):

A -- B -- C -- D -- E -- M2    <-- master
      \          \      /
       F - G - H - M - I       <-- develop

Here merge finds what's been done in develop since it split off from master, which was at B. So it puts in F, G, and H, skips any parts of M that came off master (probably all of it), and finally puts in I and makes merge-commit M2 (because of commit E; but if E were not there, it would be because I used --no-ff too).

Some easy methods with obvious drawbacks

(1) Just "rewrite history": erase commit M from branch develop. Alert everyone else who uses the branch that this is about to happen: that commit M is going away and they should take whatever measures they must, to handle this.

(2) Stop using the old name develop, make a new branch name develop1 or whatever:

A -- B -- C -- D -- E          <-- master
     |           \
     |             M           <-- develop
      \          /
       F - G - H               <-- develop1

This is the same as option (1) except that the commit M is still there, along with the branch label on it, and you have a new/different branch label pointing to commit H. You still need to alert everyone, but it might make their jobs easier. You can delete develop later, when everyone is good with that.

The revert and its drawbacks

(3) Revert the merge. As in the linked article, let's use W to represent the new revert commit:

A -- B -- C -- D -- E          <-- master
      \          \
       F - G - H - M - W       <-- develop

What's in W? Whatever it takes to "erase the effect" of the merge. In this case, that means, changes that un-do whatever was done in C and D. So far so good, develop has just the changes from F, G, and H, again. The problem occurs later, if/when someone does:

git checkout master
git merge develop

The second command goes rooting around to find where master and develop were split, i.e., commit B, so it picks up all the changes since then. Which at this point include those in W, which will un-do C and D, not at all what you wanted. It can be fixed-up afterward, but whoever does the merge has to know about this "delete C and D" time-bomb waiting for them.

Note that this is the same merge as in the "do nothing" option (0). The issue here is the contents of W. In case (0), you want the contents of commit I, but here you don't want the contents of W.

One more option

This is arguably the worst option of all, given what's shown above, but I'll list it anyway:

(4) make an all-new branch with copies of the commits you want to keep, and name it develop. (Or name it something else, but then we're back to the develop1-like situation above, and you should probably just use that one.) Whether you keep the old-develop branch, or just abandon it (by not labeling it), is up to you, but I will draw it in:

       F' - G' - H'            <-- develop
      /
A -- B -- C -- D -- E          <-- master
      \          \
       F - G - H - M           <-- old-develop

This is described in the link you included. It's only really useful in more complex cases.

于 2013-10-01T02:35:17.433 回答
2

您可以通过将本地分支重置回最后一次好的提交来解决此问题,然后将其推送:

git checkout develop
git reset --hard lastgoodcommit
git push origin develop

请注意,您的上游存储库默认情况下可能不允许非快进合并。如果是这样,您将需要在上游修改该选项,执行上述推送,然后恢复原始设置。

于 2013-10-01T01:37:19.560 回答
0

是的,您可以恢复合并,但您需要指定哪个父级是主线。由于您将开发合并为大师,这应该可以解决问题:

git revert --mainline 1 HEAD

为了确保你有你想要的,你应该用 $lastgoodcommit 做一个差异,但是,$lastgoodcommit 应该是 HEAD^。这当然是,如果您没有在合并之上进行提交,如果您有,那么您需要将 HEAD 替换为合并提交。

最后,您还可以通过检查当前提交顶部的代码来手动执行还原:

git checkout $lastgoodcommit -- .

这 ' - 。' 是否有区别于正常的签出,其中 HEAD 切换到指定的提交,在此处的命令中,工作目录中的所有文件和暂存区域都将与 $lastgoodcommit 中的完全相同。如果您使用 $lastgoodcommit 进行比较,您应该再次看到没有任何变化。本质上是一个还原。

您也可以尝试“ git checkout --patch”来选择性地还原代码块。

于 2013-10-01T04:06:50.630 回答
0

我使用 GIT 扩展。有一个功能可以右键单击 UI 中的修订并恢复到它。

于 2013-10-01T01:34:41.867 回答