0

我不小心从另一个分支拉了一个,这改变了HEAD说法:

HEAD is now at 7c0208906 Merge remote-tracking branch 'refs/remotes/origin/lorem-ipsum'

现在,它不断地origin/lorem-ipsumorigin/master

被拉出的文件也有冲突。现在已经有几天了,其他人在父存储库中对主分支进行了新的更改,我落后了。

如何将存储库的状态恢复到错误拉取之前的状态,以及如何简单地将存储库状态转换HEAD为以前的状态?

请帮忙,我卡住了!

4

1 回答 1

1

git pull命令只是运行(在任何时候git fetch总是安全的1 ),然后是git merge2(不太安全)。因此,您需要从中恢复的是您的git merge. 但是这个:

现在,它不断地origin/lorem-ipsumorigin/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(1fc39a7deadc0d类似的)。为了让事情更清楚,我们将为每个提交使用单个大写字母。另请注意,每个提交都有一个父提交,但具有两个父提交的合并除外。我们说每个提交“指向”它的父级(或者,对于合并,父级复数):

... <-E <-F <-G <-K   <-- master (HEAD)
       \         /
        H <-I <-J   <-- origin/lorem-ipsum

K是我们的合并提交,同时指向GJG是您master. 这个名字master现在意味着(“指向”)这个 commit K。名称origin/lorem-ipsum指向 commit Jmaster我们还用标记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是三件事:

  1. 更改分支以指向选定的提交。这意味着master现在命名 commit G,而不是K

    ...--E--F--G   <-- master (HEAD)
          \
           H--I--J   <-- origin/lorem-ipsum
    

    (这次我省略了内部箭头,因为它们画起来很痛苦,而且不会给我们太多。发生了K什么?它仍然在你的存储库中,但它现在在回收箱中,它在哪里很难找到,Git 通常不会再显示了。)

  2. reset --hard步骤还会重置 Git 的index。我认为,最好将 Git 的索引描述为“构建下一次提交的位置”。在正常情况下,您希望索引与当前提交匹配,直到您开始编辑和git add更改以进行新提交。所以这就是你想要的:让你的索引匹配 commit G

  3. 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垃圾收集器真正扔掉。

于 2017-05-11T20:45:16.020 回答