恢复多个提交并合并回特定的提交,而无需强行推动

时间:2019-02-14 20:43:34

标签: git github revert

问题:

  • 具有约50个提交的远程存储库,应将其还原为1个特定的提交(例如git reset --hard“我的特定提交”)
  • 在远程提交并合并
  • 根本没有力气

我知道我可以做git revert -n“我的特定提交”。.HEAD的问题是它在第一次合并时就停止了。传递-m 1可以解决该问题,但是它会在非合并时停止。

git reset-强行推入是 NOT 的一种选择。我不能强行推动此git主机。

情况:(我为什么需要这个)

  • master中的错误更改,因此我们签出一个新分支,并希望从旧状态(通过“特定提交”)构建一个新版本,以便我们可以将该版本部署到产品中。
  • 单个提交很容易还原,但是批量还原很耗时,充其量我想一次还原很多提交

我知道这里有很多问题,但是我还没有找到一个确切的问题并且没有强制解决方案的合理解决方案。

3 个答案:

答案 0 :(得分:2)

TL; DR

使用git read-tree -u <hash>,然后进行新的提交。

请记住,Git存储库本质上是提交的集合。每个提交都拥有所有文件的完整快照,包括该提交中的所有文件,但表达方式听起来像是重言式的,还有一些元数据:该人的姓名和电子邮件地址进行提交的人,提交时间的时间戳记,记录他们解释为什么的原因的日志消息,等等。 (元数据的关键部分之一是该提交的 parent 提交的哈希ID,但是有一次我们根本不必担心这部分!)

任何给定提交的真实名称是其哈希ID。您可以通过执行以下操作将该提交暂时停留在您的工作树中:

git checkout <hash-id>

但是,当然,这只是为您提供了一个“分离的HEAD”,当您通过执行git checkout master重新连接HEAD时,您将返回损坏的源代码快照。

因此,每个提交代表所有文件的快照。在某个时刻,您有一个想要的快照,并且想要返回到快照,但是实际上并没有直接使用git checkout <hash>。这意味着您想要做一个 new 提交,其快照与现有提交相同。使用一些现有的哈希ID可以很好地提交,git checkout <hash>是...好,但是您想要使用 new 哈希ID的 new 提交,否则在当前分支的(新)末端匹配好者。此新提交应将当前提交作为其父提交,就像 any 新提交将其当前提交作为其父提交一样。

joanis' answer中概述的方法将起作用:它将选定提交中的内容与当前工作树中的内容进行比较,并询问您是否要调整工作树以匹配选定的提交,用于每个文件中的每个diff-hunk。回答“ a”表示“将所有内容都带给这个文件”,但这使您的工作充满了很多答案,每个文件都有一个答案。

使用不同的git checkout模式更简单但有缺陷:

git checkout <good-commit-hash> -- .

此模式告诉git checkout请勿将切换到。请勿以任何方式更改HEAD本身!但是要做,请在Git数据库中搜索雪貂,以查找与提交良好的路径说明.相匹配的文件。对于每个匹配的文件,将其从提交中删除,将其复制到我的当前索引,然后将其复制到我的当前工作树。

如果在工作树的顶层执行此操作,则良好提交中的所有文件将与路径说明符.相匹配。因此,如果在提交README中有文件main.py<hash>,则这些文件将替换索引中的READMEmain.py,以及READMEmain.py在您的工作树中。您将准备提交。

这里的缺点是:如果您现在有一个第三个文件,例如bug.txt,而不是 要解决此问题,您必须明确删除当前索引和工作树中不是 表现良好。

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

git rm -r .

删除所有内容。这将删除bug.txt main.py README。随后的git checkout <good-commit-hash> -- .将商品main.pyREADME退回,并且bug.txt仍被删除,以便您准备git commit结果。

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

git read-tree -u <good-commit-hash>

告诉管道命令:清除当前索引。从给定的哈希ID中获取文件,然后将其放入我的索引中。无论您将文件完全删除到什么地方,也要从我的工作树中删除。无论您在哪里替换文件,都应在我的工作树中替换它。就文件及其内容而言,与git rm -r .; git checkout <good-commit-hash> -- .获得的结果完全相同,只是效率更高。

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


1 git read-tree命令实际上是一个 plumbing命令,用于制作新的高级前端命令,这些命令可以完成某些工作。例如,某人某天可能会写一个git revert-to命令,其中仅包含一些状态检查,然后是git read-tree -u <hash>git commit

答案 1 :(得分:2)

git reset --hard good_commit_hash
git reset --soft current_commit_hash
git commit

会做你想要的。

(如果上游(远程)分支是您要提交的位置,则可以简单地将“ @ {u}”用作“ current_commit_hash”。)

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

答案 2 :(得分:0)

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

git checkout -p "my specific commit"

为每个问题回答“ a”-尽管您可以使用Linux命令yes,但我没有找到如何将其强制转换为非交互式变体的方法-它只是永久键入“ y”:

yes | git checkout -p "my specific commit"

然后提交并推送

git commit -m'Reverting to my specific commit'
git push

结果是一次提交,一次就消除了不良历史。

编辑:警告:如果将文件从my specific commit删除到当前的HEAD,则此命令将不会恢复它们。因此,@ torek和@Marcus提出的解决方案比该解决方案更合适。

相关问题