【问题标题】:Github - how to revert a pull from wrong branchGithub - 如何从错误的分支恢复拉取
【发布时间】:2017-10-10 23:14:17
【问题描述】:

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

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

现在,它不断从origin/lorem-ipsum 拉取而不是origin/master

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

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

请帮忙,我卡住了!

【问题讨论】:

  • 我不记得所有步骤,但git checkout <last valid commit id> 将还原存储库。

标签: git github git-shell


【解决方案1】:

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

现在,它不断从origin/lorem-ipsum 拉取而不是origin/master

... 建议您告诉 Git 记住 master 的“上游”是 origin/lorem-ipsum,而不是 origin/master。如果这是真的——你没有显示你实际运行的git pull,也没有显示git statusgit branch -vv的输出,所有这些都是有用的线索——那么你需要修复 master 的上游设置。

在我们进入下一步之前,还有一点需要注意:

被拉取的文件也有冲突。

请务必记住,Git 不会提取文件。 Git 获取,然后合并,提交。提交有文件,合并一些提交可能会导致文件冲突,所以这似乎是一个小问题,但它会影响您如何恢复这些内容。

当您确实遇到合并冲突时,必须自己解决冲突的文件,git add 结果,并执行最终的git commit 以进行合并犯罪。 (当您没有遇到合并冲突时,Git 会为您添加并提交结果。)git status 命令非常非常有用,因为它会告诉您是否处于冲突合并的中间,以及许多其他好的信息。经常使用它。

摆脱糟糕的合并

如果您正处于错误合并的中间,并且希望整个事情消失,这很容易:只需运行git merge --abort。这会停止合并过程并将一切恢复到开始之前的状态。

如果您已完成合并,但它很糟糕并且您想摆脱它,那就更难了,因为您或 Git 通过进行新的提交来完成合并,并且提交的全部意义在于它们是永久不变的。提交“永远”存在,3 和新提交建立在以前的提交之上,所以 真的 很难摆脱“中间”提交。摆脱一些“结束”要容易得多。

让我们画一些提交,包括合并,看看我们在这里的意思。请注意,每个提交都有一些难以理解的哈希 ID(1fc39a7deadc0d 或类似的)。为了让事情更清楚,我们将为每个提交使用单个大写字母。另请注意,每个提交都有一个 parent 提交,但具有 两个 父提交的合并除外。我们说每个提交“指向”它的父级(或者,对于合并,父级复数):

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

这里K 是我们的合并提交,指向GJG 是您的master 上的最后一次提交master 这个名字现在意味着(“指向”)这个提交K。名称origin/lorem-ipsum 指向提交J。我们还将master 标记为HEAD,注意这是我们现在签出的分支。

要完全删除 K,我们可以使用git resetgit 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 &lt;hash-id&gt;。虽然剪切和粘贴应该可以正常工作,但输入起来更难。但是我认为,~1 的“后退 1”更容易。)

git reset --hard 做了三件事:

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

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

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

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

  3. 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 真正扔掉, 在适当的等待期之后,您可以将它们从垃圾中回收。

【讨论】:

    猜你喜欢
    • 2021-09-23
    • 2010-12-15
    • 2022-11-17
    • 1970-01-01
    • 2016-11-27
    • 1970-01-01
    • 2018-08-29
    • 2020-10-01
    • 2019-05-07
    相关资源
    最近更新 更多