Git 恢复多个提交并合并回特定的提交,而无需强制推送

Git 恢复多个提交并合并回特定的提交,而无需强制推送,git,github,revert,Git,Github,Revert,问题是: 远程存储库中有大约50个提交,这些提交应该恢复为1个特定的提交,就像git reset-hard my specific commit 在远程应用程序中提交和合并 完全没有力量推动 我知道我可以做git revert-n我的特定提交..问题是它在第一次合并时停止。传递-m1可以解决这个问题,但它会在非合并时停止 git重置-硬和强制推送不是选项。我无法强制推送这个git主机 情况:我为什么需要这个 主版本中的错误更改,所以我们签出一个新分支,并希望从特定提交的旧状态构建一个新版本,以便

问题是:

远程存储库中有大约50个提交,这些提交应该恢复为1个特定的提交,就像git reset-hard my specific commit 在远程应用程序中提交和合并 完全没有力量推动 我知道我可以做git revert-n我的特定提交..问题是它在第一次合并时停止。传递-m1可以解决这个问题,但它会在非合并时停止

git重置-硬和强制推送不是选项。我无法强制推送这个git主机

情况:我为什么需要这个

主版本中的错误更改,所以我们签出一个新分支,并希望从特定提交的旧状态构建一个新版本,以便我们可以将该版本部署到prod。 单个提交很容易恢复,但批量恢复很耗时,充其量我希望一次恢复大量提交 我知道在这里有很多问题,但我还没有找到一个解决这个问题的方法和一个没有强制执行的合理解决方案。

您可以使用git checkout-p将工作树还原为给定的sha1,而不必更改当前分支的位置

git checkout -p "my specific commit"
回答每个问题-我没有找到如何将其强制为非交互式变体,尽管您可以使用Linux命令yes-它只是永远键入y:

yes | git checkout -p "my specific commit"
然后提交并推送

git commit -m'Reverting to my specific commit'
git push
结果是一个单一的承诺,一举推翻了糟糕的历史

编辑:警告:如果文件已从“我的特定提交到当前头”中删除,此命令将不会还原它们。因此,@torek和@Marcus提出的解决方案比这个更可取。

您可以使用git checkout-p将工作树还原为给定的sha1,而无需更改当前分支的位置

git checkout -p "my specific commit"
回答每个问题-我没有找到如何将其强制为非交互式变体,尽管您可以使用Linux命令yes-它只是永远键入y:

yes | git checkout -p "my specific commit"
然后提交并推送

git commit -m'Reverting to my specific commit'
git push
结果是一个单一的承诺,一举推翻了糟糕的历史

编辑:警告:如果文件已从“我的特定提交到当前头”中删除,此命令将不会还原它们。因此,@torek和@Marcus提出的解决方案比这个更可取。

TL;博士 使用git read tree-u,然后进行新的提交

长的 请记住,Git存储库本质上是提交的集合。每个提交都包含所有文件的完整快照,以及提交中的所有文件,但以这种方式表示,它听起来是重复的,再加上一些元数据:提交人的姓名和电子邮件地址,提交时间戳,解释提交原因的日志消息,等等。元数据的一个关键部分是此提交的父提交的哈希ID,但这一次我们根本不必关心这一部分

任何给定提交的真实名称都是其哈希ID。您可以通过执行以下操作将此提交临时粘贴到工作树中:

git checkout <hash-id>
此模式告诉git checkout:不要切换到另一个提交。不要以任何方式改变头部本身!但是一定要在Git数据库中查找与路径说明符匹配的文件。这些都是有益的。对于每个匹配的文件,将其从提交中取出,复制到我当前的索引中,然后复制到我当前的工作树中

如果在工作树的顶层执行此操作,则良好提交中的所有文件都将匹配路径说明符。。因此,如果commit中有README和main.py文件,那么这些文件将替换索引中的README和main.py,以及工作树中的README和main.py。你会准备好承诺的

这里的缺陷是这样的:如果您现在有第三个文件,比如说bug.txt,它不在您试图切换回的良好提交中,该怎么办?要解决这个问题,必须显式删除当前索引和工作树中未处于良好提交状态的任何文件

您可以手动删除任何此类文件。或者可能没有这样的文件,在这种情况下,问题仅仅是理论上的。但是有一种保证的治疗方法,如果没有问题,它是无害的。您可以从以下内容开始:

git rm -r .
移除所有的东西。这将删除bug.txt、main.py和自述文件。随后的git签出-。将好的main.py和README放回原处,bug.txt仍然被删除,这样您就可以用git提交结果了

不过,有一个较低级别的Git命令可以为您执行此操作,这就是Git-read-tree。这个命令不是真正的日常使用,但这也不是日常问题。在这种特殊情况下,使用:

git read-tree -u <good-commit-hash>
告诉管道命令:清除当前索引。从给定的散列ID获取文件,并将其放入我的索引中。无论在何处完全删除文件,也要从我的工作树中删除if。无论在哪里替换文件,也要在我的工作树中替换它。就文件及其内容而言,这与您得到的结果完全相同 使用git rm-r。;git checkout-,只是它更高效

在任何一种情况下,您现在都可以使用当前索引内容进行新的提交,当前索引内容现在与当前工作树内容相匹配,但未跟踪的文件除外,这些文件通常保持未跟踪状态

1 git read tree命令实际上是一个管道命令,用于生成新的花式前端命令,这些命令可以做一些花式的事情。例如,有一天,有人可能会编写一个git revert to命令,该命令只包含几个状态检查,然后是git read tree-u和git commit;博士 使用git read tree-u,然后进行新的提交

长的 请记住,Git存储库本质上是提交的集合。每个提交都包含所有文件的完整快照,以及提交中的所有文件,但以这种方式表示,它听起来是重复的,再加上一些元数据:提交人的姓名和电子邮件地址,提交时间戳,解释提交原因的日志消息,等等。元数据的一个关键部分是此提交的父提交的哈希ID,但这一次我们根本不必关心这一部分

任何给定提交的真实名称都是其哈希ID。您可以通过执行以下操作将此提交临时粘贴到工作树中:

git checkout <hash-id>
此模式告诉git checkout:不要切换到另一个提交。不要以任何方式改变头部本身!但是一定要在Git数据库中查找与路径说明符匹配的文件。这些都是有益的。对于每个匹配的文件,将其从提交中取出,复制到我当前的索引中,然后复制到我当前的工作树中

如果在工作树的顶层执行此操作,则良好提交中的所有文件都将匹配路径说明符。。因此,如果commit中有README和main.py文件,那么这些文件将替换索引中的README和main.py,以及工作树中的README和main.py。你会准备好承诺的

这里的缺陷是这样的:如果您现在有第三个文件,比如说bug.txt,它不在您试图切换回的良好提交中,该怎么办?要解决这个问题,必须显式删除当前索引和工作树中未处于良好提交状态的任何文件

您可以手动删除任何此类文件。或者可能没有这样的文件,在这种情况下,问题仅仅是理论上的。但是有一种保证的治疗方法,如果没有问题,它是无害的。您可以从以下内容开始:

git rm -r .
移除所有的东西。这将删除bug.txt、main.py和自述文件。随后的git签出-。将好的main.py和README放回原处,bug.txt仍然被删除,这样您就可以用git提交结果了

不过,有一个较低级别的Git命令可以为您执行此操作,这就是Git-read-tree。这个命令不是真正的日常使用,但这也不是日常问题。在这种特殊情况下,使用:

git read-tree -u <good-commit-hash>
告诉管道命令:清除当前索引。从给定的散列ID获取文件,并将其放入我的索引中。无论在何处完全删除文件,也要从我的工作树中删除if。无论在哪里替换文件,也要在我的工作树中替换它。就文件及其内容而言,这与使用git rm-r得到的结果完全相同。;git checkout-,只是它更高效

在任何一种情况下,您现在都可以使用当前索引内容进行新的提交,当前索引内容现在与当前工作树内容相匹配,但未跟踪的文件除外,这些文件通常保持未跟踪状态

1 git read tree命令实际上是一个管道命令,用于生成新的花式前端命令,这些命令可以做一些花式的事情。例如,有一天,可能有人会编写一个git revert to命令,该命令只包含几个状态检查,然后是git read tree-u和git commit

我会做你想做的

如果上游远程分支是您想要提交的位置,那么您可以简单地使用@{u}作为当前提交散列

简短:如果您想在存储库中执行任何类型的清理,请使用git reset。这是一种瑞士军刀

我会做你想做的

如果上游远程分支是您想要提交的位置,那么您可以简单地使用@{u}作为当前提交散列


简短:如果您想在存储库中执行任何类型的清理,请使用git reset。这是一种瑞士军刀。

更简单,也可以说更好,因为它删除了其他提交中没有的文件:git read tree-u,然后是git commit。@torek不确定您的意思:checkout-p还删除了在测试沙盒中添加的文件或取消了重命名。我的测试表明我的解决方案很有效,尽管我同意你的解决方案更直接,最终也更简单。关于Git有什么你不知道的吗?我看到你如此可靠地回答了这么多Git问题。感谢您对Git知识的贡献!有趣的是,我还没有看到git checkout-p提供给
删除整个文件。大概这意味着Git AdP和Git Realth-P是相同的,我应该测试……@ TroRK我会考虑Git CouthOut-P错误的任何其他行为。然而,GitReset-p似乎无法正确处理撤销重命名的问题。该死,我刚才说得太快了。重命名只处理了一半:新文件被删除,但旧文件没有恢复,git reset-p也有同样的问题。所以我收回这一切,你的解决方案是唯一正确的。我认为这是一个带有reset-p和checkout-p的bug。实现这三个命令的补丁模式的perl脚本必须解释git diff输出,重命名输出可能很棘手,因此,如果它正确处理它,我会感到惊讶,如果它不正确,我也不会感到惊讶:-更容易,也可以说更好,因为它删除了不在另一个提交中的文件:git read tree-u,后跟git commit。@torek不确定您的意思:checkout-p还删除了在测试沙盒中添加或取消重命名的文件。我的测试表明我的解决方案很有效,尽管我同意你的解决方案更直接,最终也更简单。关于Git有什么你不知道的吗?我看到你如此可靠地回答了这么多Git问题。感谢您对Git知识的贡献!有趣的是,我没有看到git checkout-p提供删除整个文件的功能。大概这意味着Git AdP和Git Realth-P是相同的,我应该测试……@ TroRK我会考虑Git CouthOut-P错误的任何其他行为。然而,GitReset-p似乎无法正确处理撤销重命名的问题。该死,我刚才说得太快了。重命名只处理了一半:新文件被删除,但旧文件没有恢复,git reset-p也有同样的问题。所以我收回这一切,你的解决方案是唯一正确的。我认为这是一个带有reset-p和checkout-p的bug。实现这三个命令的补丁模式的perl脚本必须解释git diff输出,重命名输出可能很棘手,因此,如果它正确处理它,我会感到惊讶,如果它不正确,我也不会感到惊讶:-这种方法也会起作用,对许多人来说,可能比复杂的git read tree-u版本更容易记住。我忘记了:当你完成的时候,你应该添加一个git clean-fd,因为从那以后添加的文件在使用这个方法后将不被跟踪。很好,很简单。我喜欢它。这种方法也会起作用,而且可能比复杂的git read tree-u版本更容易记住。我忘记了:当你完成的时候,你应该添加一个git clean-fd,因为从那以后添加的文件在使用这个方法后将不被跟踪。很好,很简单。我喜欢。