【问题标题】:git revert remote to a particular commit with a merge doesn't work with -mgit revert remote to a specific commit with a merge 不适用于 -m
【发布时间】:2018-03-22 14:05:37
【问题描述】:

我正在尝试将我们的远程仓库恢复到之前的提交。树如下图:

我们的想法是,我们希望回到那个提交,而不需要从 master 分支中拉出的任何人都必须处理重置回提交 2dda031 时可能出现的问题。所以我改用git revert

我正在尝试使用 git revert --no-commit 2dda031..HEAD 来做到这一点

但是,我收到此错误:

error: commit d064f7c3b04a2bda30c43a32afac822c6af633c0 is a merge but no -m  option was given. 
fatal: revert failed

这是预期的,因为d064f7c 是一个合并(47d4161 也是如此)。所以按照here的建议,我愿意:

git revert --abort
git revert --no-commit -m 1 2dda031..HEAD

然后我收到错误消息:

error: mainline was specified but commit cb420e0 is not a merge.
fatal: revert failed

所以我只是觉得我在循环。有人可以告诉我恢复到该提交的正确方法(同时恢复历史记录)吗?

【问题讨论】:

    标签: git github git-revert


    【解决方案1】:

    编辑(下面的原始答案):让我首先将您的图形转换为文本,(我希望)没有拼写错误或其他严重错误。这就是您现在所拥有的,正如git log --graph --oneline 可能会显示的那样(尽管--graph --oneline 可能会选择稍微不同的提交顺序——图形查看器生成的蓝线和绿线可能按提交日期排序,而不考虑拓扑,而不是先按拓扑排序):

    * cb420e0  (master, ...) evert "Update README.md"
    * 7a16df4  Update README.md
    * 7564754  Update README.md
    * 214cd47  Update README.md
    * d064f7c  Merge pull request #6 from ...
    |\
    * | d936a24  Changing Run instructions
    * | 2cbd7c2  Minor edits for Google Drive link
    * | 1a3d871  Updated process documentation with google drive link
    | * 0594132  (TrustM..., ...) Added some comments to various scripts.
    | * 7e060c4  Updated the JSON dialogues and implemented the Trust mechanism
    |/
    * 4d7f49b  Configured script inputs and enabled mouse during pause screen.
    * 47d4161  Merge pull request #5 from ...
    |\
    | * e999b3d  (origin/Trying...) Adjusted ray cast length to be more realistic.
    | * 953e4c3  Fully functional dialogue system implemented.
    * | 1f33079  updated wiki to reflect marking of prototype
    | * 09e350b  Added in most of the Yarn framework
    | * 2dda031  fixed heirarchy of files
    | * bf667cc  Merge branch 'develop' of ...
    | |\
    | * |  79e068d  Character placement
    

    (我们看不到任何低于这一点的东西,尽管显然必须有更多的提交)。

    我猜你想要返回的提交状态是2dda031 fixed heirarchy of files

    现在,最棘手的部分是这个状态“存在于”曾经显然是一个分支,在“Merge pull request #5 from ...”提交下。如果在还原过程中的某个时刻,您要运行 git revert -m <some-number> 47d4161,您将告诉 Git 将 47d4161 与它的第一个父级 1ff3079 或第二个父级 d999b3d 进行比较。这些差异中的第一个显示了自合并基础以来每次提交的效果——无论提交是什么:我们无法从图中的这个片段中看到它;我们需要更多的图表来找到它,因为它在“屏幕”的底部 - 这两个中的一个,另一个差异显示了自合并基础到另一个的每个提交的效果。所以用-m 1 回复基本上会带走以下效果:

    * e999b3d  (origin/Trying...) Adjusted ray cast length to be more realistic.
    * 953e4c3  Fully functional dialogue system implemented.
    * 09e350b  Added in most of the Yarn framework
    * 2dda031  fixed heirarchy of files
    * bf667cc  Merge branch 'develop' of ...
    ...
    

    在使用-m 2的同时,基本上去掉了以下效果:

    * 1f33079  updated wiki to reflect marking of prototype
    ...
    

    (在这两种情况下,我们可能看不到更多的提交)。我很清楚你不想这样做,但我不确定。

    但是请注意,如果您只是提取提交 2dda031 的内容,您仍然会失去 1f33079 的效果以及它下面的任何其他提交,因为您得到origin/Trying... 之前的几次提交的状态。

    提交d064f7c 更简单,因为它只是从合并基础4d7f49b 合并d936a240594132。如果你想撤销提交1a3d871+2cbd7c2+d936a24 的效果,你可以git revert -m 1 d064f7c。如果你想撤销提交的影响0594132+7e060c4,你可以git revert -m 2 d064f7c。但是由于您(大概)想要撤消所有这些的效果,因此单独撤消它们会更简单,完全跳过合并。

    由于您可能还想通过214cd47 撤消cb420e0,您只需分别撤消这些操作。

    如果您想保留1f33079 和更早提交的效果,请不要恢复它们。如果你想撤销它们的效果,你可以git revert -m 2 47d4161,它会一次性撤销它们。我怀疑你想要那个,但和以前一样,这取决于你。

    请注意,任何还原都可以使用-n 运行(在索引和工作树中还原而不提交),但是一旦您开始了一系列-n 操作,您必须继续使用-n 并最终提交, 在您可以在没有 -n 的情况下开始新的还原之前。

    根据您想要的结果,也许最简单的方法是首先提取提交47d4161内容 — 无需单独提交即可从尽可能远的位置提取树状态-backouts——然后在每个额外的提交上使用git revert -n 来恢复。要提取这些内容,您可以使用git checkout <commit> -- .,但有轻微的风险,即不会删除此后新文件的索引和工作树条目,也可以使用git read-tree --reset -u <commit> 来避免风险(请参阅下面的注释,在原始答案中,也)。我会选择后者,给出:

    git read-tree --reset -u 47d4161
    git revert -n e999b3d
    git revert -n 953e4c3
    git revert -n 09e350b
    

    (当然,假设这会为您提供您想要的最终工作树和索引状态)。

    (下面一行的原始答案。)


    Git 的revert 不会将提交恢复(这使得命令命名不当:它使用了错误的动词)。它的作用是退出(即“恢复”)一个特定的提交,或者可能是一组提交。1你已经中途了有2dda031..HEAD,因为这个范围语法实际上意味着HEAD ^2dda031,即从HEAD 可到达的所有提交的集合,不包括(减去,使用集合减法)从2dda031 可到达的所有提交的集合。

    现在,由于合并,这里存在多个问题。第一个是排除2dda031 及其父级未能排除合并的另一端,因此您将恢复太多提交。第二个问题是,从某种意义上说,合并提交是一种提交,当它的单次提交更改时,“所有更改都由一个分支带来”。2 第三个是为了恢复合并提交,您必须指定要考虑的“边”,但为了恢复非合并,您不得指定任何“边”。

    其中一些问题的解决方案是完全避免恢复合并,而其他问题的解决方案是在适用的情况下恢复合并。但是还有另一种更简单的方法,具体取决于您的实际目标:如果真的要恢复 to 提交,那么执行此操作的 Git 动词实际上是 git checkout——但有一些陷阱。请参阅this answer 到一个相关的(甚至可能是重复的,取决于您的目标)问题。 git rm -r . 的原因是删除当前索引中不会被 git checkout <hash> -- . 步骤提取的所有文件。

    您可以使用一个快捷方式来代替git rm -r . && git checkout <hash> -- . 序列,它也不依赖于当前工作目录:您可以运行git read-tree --reset -u <hash>。这将丢弃索引的当前内容 (--reset) 并用指定​​提交的内容 (<hash> 参数) 替换它们,然后更新工作树以匹配,删除从索引中删除的所有文件并更新索引中更新的任何文件。

    请注意,在所有情况下,最终结果都在索引和工作树中,但尚未提交,因此您必须运行 git commit

    请注意,如果您的目标实际上不是将 还原为 特定提交,而是还原引入的一系列更改,那么使用 git revert -n 的方法是根据需要使用尽可能多的单独的git revert 命令:每个要撤消的更改集一个。其中一些可能是git reverts 的非合并,一些可能是git reverts 的合并。不过,请参阅脚注 2,并记住无论您使用哪种方法,即使您想保留它,您想要保留的更改最终也会成功(更改将消失)。


    1因此,其他一些 VCS 中的动词是“backout”。

    2这个描述在一个微妙但非常重要的方面是错误的:合并 combines 变化。合并的输入是两组更改:一组从合并基础提交到 --ours 提交,另一组从合并基础提交到 --theirs 提交。这两个变更集可能重叠。如果他们这样做了,并且在任何时候重叠“足够相似”,Git 只需要 一个副本 的更改。如果 Git 获取了某个更改 Δ 的副本,其中 Δ 出现在两个更改集中,并且您还原了 --theirs 更改集,Git 会退出 Δ 即使它在 --ours 更改集中 (如果您退出 --ours delta,同样的推理也适用:Git 不应该,但确实将其从作为 --theirs 引入的版本中删除。)

    【讨论】:

    • 感谢您全面而出色的回复:)。我的目标实际上是恢复一系列提交,正如你所说,我认为最好的方法可能只是逐渐恢复它们。但是当我尝试恢复合并提交时 - 我遇到了几个我无法解决的冲突。另一种方法是将另一个分支重命名为 master - 这是可以接受的做法吗?谢谢
    • 这是可以接受的做法吗? 什么是“可接受的”是你和其他人都同意的。在任何情况下,关于 git-reverts(常规提交 合并)值得注意的另一件事是它们添加了 new 提交,但不会更改任何现有历史记录。这意味着恢复合并不会改变合并(这是历史的一部分)影响 next 合并的方式。同时,我会尝试在这里画点别的东西……
    猜你喜欢
    • 2017-09-09
    • 1970-01-01
    • 2019-12-29
    • 2015-09-26
    • 2017-04-22
    • 1970-01-01
    • 2017-08-30
    • 2022-12-01
    • 2021-06-16
    相关资源
    最近更新 更多