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