将git重置到之前的提交,然后推送。

3
尝试通过git reset回滚到以前的提交(例如123abc)时:
git reset --hard 123abc 
git commit -a -m "revert to 123abc"

我无法推送它(在推动之前需要先拉动,而拉动会让我前进)。 我写了以下几行代码:

for i in `git diff --name-only 123abc`; do git checkout 123abc $i; done
git commit -a -m "revert to 123abc"

现在这个版本可以正常运行

 git diff --name-only 123abc

为空

我在想这是一个黑客攻击还是git的方式。如果不是,如何正确地完成它?


1
如果你正在重置,就不需要再提交了。 - evolutionxbox
@evolutionxbox 但是我无法推送。我希望Origin知道我想要重置的事实。 - jimifiki
通过重置,您正在更改提交树。远程服务器可能拒绝推送。您可以强制推送,删除远程分支并重新推送,或者使用还原而不是重置。 - evolutionxbox
3个回答

6
当尝试恢复到先前的提交(例如123abc)时,我添加了介词“to”的强调,如“revert to”所示,这是至关重要的。您链接问题中(如何将Git存储库还原到以前的提交?)已接受答案指出了这一点:
“这在很大程度上取决于您对‘还原’的理解。”
在单个提交上运行git revert可能不足够。如果需要,可以参见那里有关还原多个提交的答案。
运行git reset --hard可能足够,但会引入您遇到的问题。这里需要了解以下内容:
  1. 分支名 简单地让 Git 找到 特定的提交。我们说分支名 指向 这个提交。使用 git reset,可以更改某个分支名指向的特定提交。然而,在 git reset 完成之前可能会发生其他事情,这取决于运行的 git reset 的类型:第一步是只更改 你自己的一个分支名。因为你正在考虑(或已经使用)--hard,所以你的仓库中还有一些重要的变化,但目前来说不那么重要。

  2. 每个 Git 仓库都有其自己的分支名。移动其中一个 你的 分支名对任何其他仓库没有影响。

  3. 你需要做的是让 其他 Git 仓库 更改其分支名之一。这就是你遇到这个问题的地方:

    我不能推送这个(我需要先拉取,而拉取会让我往前走)。

    git push 命令是如何要求或者使用 --force 命令—要求其他 Git 仓库更改、创建或删除其中的一些名称(分支名、标签名和其他这样的名称)的方式。但是,每个 Git 仓库都被设置为 轻松接受新的传入提交,同时抵制建议放弃任何现有提交的建议

当您使用git reset将您的一个分支名称“向后移动”,以便恢复(不是像添加一个撤销提交那样)一些先前的提交时,您正在有意地丢弃一些现有的提交。由于您控制自己的Git存储库,因此可以在自己的存储库中执行此操作。由于git reset命令旨在执行此类丢弃操作,1因此它会毫不犹豫地这样做。但是git push不是,并且他们的 Git 会抱怨。
只要您使用git pull他们的存储库就会让您放回您从自己的存储库中剪切出来的所有提交。毕竟,Git旨在尽可能轻松地添加提交!这使您处于目前的情况。
现在您有选择:
  • 强制其他Git仓库放弃一些提交。这需要足够的权限。这也意味着,使用该Git仓库的所有其他人需要采取行动来处理任何他们所克隆的副本,因为他们的副本将热情地恢复您试图修剪的所有提交。所以这对那些其他人有点不好。

  • 或者,使用git revert或其他命令向您的仓库添加一些提交,最终将文件还原,但不会删除任何旧的提交。新的提交只是添加到旧的提交中。旧的提交仍然存在,供任何想了解它们的人(例如git log)或使用它们(例如git switch --detach hash-id)。

添加新提交是Git的设计目的,因此除非有非常强烈的理由放弃旧提交,否则后者是正确的选择。

正如您所链接的问题所指出的那样,如果使用未发布的提交(即只有您拥有的提交,在您自己的私人仓库中),所有这些都会变得更加容易,因为这些提交根本不在任何其他Git仓库中。如果从您自己的分支中删除这些提交,没有人会知道。他们将无法反对git push从他们的分支中删除这些提交,因为这些提交不在他们的分支上。(再次强调,每个分支名称对于每个Git仓库是本地的。一个仓库可以向另一个仓库显示存储在其分支名称中的提交哈希ID,但每个仓库都负责在其自己的分支名称中保留其自己的哈希ID。提交被共享;分支名称不会。)
由于您正在查看的提交是已发布的,因此您无法使用此快捷方式。

1这使得git reset成为一种非常高效的工具,就像某种喷火烤肉架、工业钢切割激光器之类的东西。这种过度强大是Git 2.23现在拥有git restore的原因之一:你以前需要使用git reset才能完成的一些事情,现在可以用相对温和的git restore来完成。如果要求,这两个命令都会丢弃正在进行的工作,但git restore更像手锯或螺栓切割器之类的东西。


这是一篇非常好的、详细的解释。我特别喜欢对git reset与火焰喷射链锯或工业级钢切割激光的比较。然而,你没有提到用于强制删除远程提交的git push命令。是git push --force吗? - Ethan Groat
@EthanGroat:是的,git push --force(这是原始的蛮力推送,没有任何双重检查方法),或者git push --force-with-lease(可以在推送过程中插入双重检查),或者新潮的--force-if-includes(类似于--force-with-lease,在后台获取更新时引入,配合新的git maintenance套件使用)。 - torek
@torek 在我执行 reset --hardpush --force 后,仓库的用户需要采取哪些行动? - steffen
@steffen:他们必须检查自己的Git仓库。你告诉了一些第三方仓库(例如GitHub)“丢弃一堆提交,因为我不喜欢它们。”其他用户可能拥有这些提交。他们的Git软件现在会认为这些提交是新的,由他们创建,并应该添加到GitHub仓库中。如果这是错误的,他们必须采取行动来确保不会发生这种情况。他们应该采取什么行动取决于他们对这些提交的处理方式(是要保留它们还是丢弃它们)。 - torek
Git的设计初衷是用来添加提交记录,而不是删除它们。因此,每当你删除一些提交记录时,你必须确保没有其他人已经拥有这些提交记录并会重新添加它们。Git软件会很乐意重新接收被丢弃的提交记录,因为“添加提交记录”对于一个Git仓库来说是一件自然的事情。“删除提交记录”才是需要使用强制操作的地方。 - torek

1
这是一个黑客技巧。使用git revert <commit hash>,但要注意,您正在撤消<commit hash>应用的更改,因此要还原到以前的提交,请使用git revert HEAD。这将创建另一个提交,该提交正在撤消上一个提交引起的更改。请查看此答案:https://dev59.com/v2Eh5IYBdhLWcg3wNxAn#22683231。以上是如何将撤消本身也放入历史记录的正确方法。如果您是仓库中唯一的工作人员,您还可以强制推送删除了最新提交的分支:https://dev59.com/v2Eh5IYBdhLWcg3wNxAn#31937298

什么是黑客? - evolutionxbox
@evolutionxbox: Hack = 滥用系统以达到自己的目标的一种非预期方式。 - Roman Pavelka
抱歉,我明白这个词的意思。我想知道你在上面的问题中认为什么是“hack”。没有任何东西被以意外的方式使用。 - evolutionxbox
1
嗯,OP创造了自己的git revert方法。语言学并不是一门精确的科学,但我认为这是一种不太正规的撤销提交的方式... - Roman Pavelka
2
重置是一种有效的方法。只是OP不知道如何完成任务。https://dev59.com/im855IYBdhLWcg3w1oLa - evolutionxbox

0

你应该尝试使用 git revert <commit hash>。这将撤销所选的提交,然后你可以推送更改。


我无法推送:我被告知我落后了,必须先拉取再推送。这使我远离我想要推送的提交。 - jimifiki
你尝试过执行git pull,然后再执行我上面描述的git revert吗? - Joshua Zeltser
当然,我陷入了一个无限循环:还原、拉取、还原、拉取……问题是,在还原后我无法推送向后,一旦拉取,我就被移动到分支的顶端。 - jimifiki
也许尝试执行git pull,然后执行git reset --hard <提交哈希值> - Joshua Zeltser
我会尽力并告诉你 - jimifiki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接