该git pull
命令只是运行(在任何时候git fetch
总是安全的1 ),然后是git merge
2(不太安全)。因此,您需要从中恢复的是您的git merge
. 但是这个:
现在,它不断地origin/lorem-ipsum
从origin/master
... 建议您告诉 Git记住“上游”为master
is origin/lorem-ipsum
, not origin/master
。如果这是真的——你没有显示git pull
你运行的实际情况,也没有显示git status
or的输出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-upstream
origin/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 rebase
git merge
3嗯,也就是说,提交是永久性的,直到你故意将它们扔掉。然后,在经过适当的等待期后,您可以将它们从垃圾中回收,最终它们会与其他垃圾一起被git gc
垃圾收集器真正扔掉。