该git pull命令只是运行(在任何时候git fetch总是安全的1 ),然后是git merge2(不太安全)。因此,您需要从中恢复的是您的git merge. 但是这个:
现在,它不断地origin/lorem-ipsum从origin/master
... 建议您告诉 Git记住“上游”为masteris origin/lorem-ipsum, not origin/master。如果这是真的——你没有显示git pull你运行的实际情况,也没有显示git statusor的输出git branch -vv,所有这些在这里都是有用的线索——那么你还需要修复你的上游设置master。
在我们进入下一步之前,还有一点需要注意:
被拉取的文件也有冲突。
重要的是要记住 Git 不会拉取文件。Git 获取,然后合并,提交。提交有文件,合并一些提交可能会导致文件冲突,所以这看起来像是一个小问题,但它会影响你如何恢复这些东西。
当您确实遇到合并冲突时,您必须自己解决冲突的文件、git add结果,并进行最终git commit的合并提交。(当您没有遇到合并冲突时,Git 会为您添加并提交结果。)该git status命令非常有用,因为它会告诉您是否处于合并冲突的中间,以及许多其他有用的信息。经常使用它。
摆脱糟糕的合并
如果你正处于一个糟糕的合并之中,并且你希望整个事情都消失,那很容易:只需运行git merge --abort. 这会停止合并过程并将一切恢复到开始之前的状态。
如果您已经完成了合并,但它很糟糕并且您想摆脱它,那就更难了,因为您或 Git 通过进行新的提交来完成合并,而提交的全部意义在于它们是永久的并且不变。提交坚持“永远”,3和新的提交建立在以前的提交之上,所以很难摆脱“中间”的提交。摆脱一些“结束”要容易得多。
让我们画一些提交,包括合并,看看我们在这里的意思。请注意,每个提交都有一些难以理解的哈希 ID(1fc39a7或deadc0d类似的)。为了让事情更清楚,我们将为每个提交使用单个大写字母。另请注意,每个提交都有一个父提交,但具有两个父提交的合并除外。我们说每个提交“指向”它的父级(或者,对于合并,父级复数):
... <-E <-F <-G <-K <-- master (HEAD)
\ /
H <-I <-J <-- origin/lorem-ipsum
这K是我们的合并提交,同时指向G和J。 G是您的master. 这个名字master现在意味着(“指向”)这个 commit K。名称origin/lorem-ipsum指向 commit J。master我们还用标记HEAD,注意这是我们现在签出的分支。
要完全删除 K,我们可以使用git reset. 该git reset命令可能具有相当大的破坏性,因此在使用它之前,请运行git status以确保没有任何您关心的东西会丢失。(这是一个反复出现的主题:run git status。)在确保您在 上master之后,您现在不会丢失任何其他内容,这表明您确实想故意丢失master此合并提交:K
git reset --hard master~1
~1后缀告诉 Git 找到提交到哪些master点,然后后退一步。也就是按照从 出来的箭头K。如果有两个箭头——有,因为K是合并——Git 应该跟随第一个箭头,它总是指向在我们进行合并master之前的提交。所以 Git 按照箭头提交。G
(另一种方法是git log找到实际的哈希ID commit G,然后运行git reset --hard <hash-id>。虽然剪切和粘贴应该工作得很好,但输入起来更难。但~1我认为这个“后退1”更容易.)
什么git reset --hard是三件事:
更改分支以指向选定的提交。这意味着master现在命名 commit G,而不是K:
...--E--F--G <-- master (HEAD)
\
H--I--J <-- origin/lorem-ipsum
(这次我省略了内部箭头,因为它们画起来很痛苦,而且不会给我们太多。发生了K什么?它仍然在你的存储库中,但它现在在回收箱中,它在哪里很难找到,Git 通常不会再显示了。)
该reset --hard步骤还会重置 Git 的index。我认为,最好将 Git 的索引描述为“构建下一次提交的位置”。在正常情况下,您希望索引与当前提交匹配,直到您开始编辑和git add更改以进行新提交。所以这就是你想要的:让你的索引匹配 commit G。
该reset --hard步骤还会重置您的工作树,即您以正常方式显示所有文件的位置,以便您可以使用或处理它们。这会丢弃合并的版本K并将它们替换为来自的版本G,这也是您想要的。
现在好像合并从未发生过。
不过,您的上游设置可能仍然是origin/lorem-ipsum。
修复或更改您的上游
要更改当前分支的上游设置(仍然master),只需运行. 在这种情况下,您想再次将其设置为,因此:git branch --set-upstream-to=new-upstreamorigin/master
git branch --set-upstream-to=origin/master
现在您当前分支的 ( master's) 上游是origin/master,这git pull意味着“获取,然后与origin/master”合并 - 大概是您想要的。
我建议避免git pull
该git pull命令旨在方便。它是......但这种便利是一种陷阱。如果您知道自己在做git fetch后面的事情git merge,那么您就会知道寻找撤消合并的方法(并且对此有很多现有的 SO 答案)。
除此之外,git rebase无论如何使用它通常会更好。那么你应该运行git fetch后跟git rebase. 你可以为你git pull做这件事......但有时变基不是要走的路;有时合并更好。随着你对 Git 的了解越来越多,你会发现 merge-vs-rebase 的决定有时取决于你在 fetch 时得到什么。在这种情况下,如何在查看获取的内容之前提前决定是变基还是合并?
我认为最好先学习单独的步骤。然后,一旦您熟悉它们,您就可以决定只输入一个命令 ( git pull) 是否值得方便,而不是在出错时偶尔令人头疼。届时您还将知道默认情况下是要合并还是变基,并且可以设置git pull为这样做。
1有些方法可以git fetch手动运行,但并不完全安全,但很难做到。你不会意外得到这个。
2你可以直接作为它的第二步git pull使用,而不是作为它的第二步运行。我假设,并且您所展示的内容表明,您没有这样做。git rebasegit merge
3嗯,也就是说,提交是永久性的,直到你故意将它们扔掉。然后,在经过适当的等待期后,您可以将它们从垃圾中回收,最终它们会与其他垃圾一起被git gc垃圾收集器真正扔掉。