恢复多个提交并在没有强制推送的情况下合并回特定提交

问题描述 投票:1回答:3

问题:

  • 具有~50次提交的远程存储库应该恢复为1次特定提交(如git reset --hard“我的特定提交”)
  • 在远程提交和合并
  • 没有任何力量推动

我知道我可以做git revert -n“我的具体提交”.. HEAD的问题是它在第一次合并时停止。传递-m 1会解决这个问题,但随后会在非合并时停止。

git reset - hard和force push不是一个选项。我无法强行推动这个git主机。

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

  • master中的更改很糟糕,因此我们签出了一个新分支,并希望从旧状态(从“特定提交”)构建新构建,以便我们可以将该构建部署到prod。
  • 单个提交很容易恢复,但批量恢复是耗时的,充其量我想要立即恢复大量提交

我知道这里有很多问题,但是我没有找到一个有这个确切问题和没有用力推动的合理解决方案。

git github revert
3个回答
2
投票

TL;DR

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

Long

请记住,Git存储库本质上是一组提交。每个提交都包含所有文件的完整和完整快照 - 以及该提交中的所有文件,但以这种方式表达它听起来是同义的 - 加上一些元数据:提交者的姓名和电子邮件地址,时间戳当他们成功的时候,他们会解释他们为什么会这样做的日志信息,等等。 (元数据的关键部分之一是此提交的父提交的哈希ID,但是一次我们根本不必关心这部分!)

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

git checkout <hash-id>

但是当然这只会让你成为一个“独立的HEAD”,当你通过执行git checkout master重新连接你的HEAD时,你又回到了破碎的源代码快照。

因此,每个提交代表所有文件的快照。你有一个快照,在某些时候,你喜欢,你想回到它 - 但没有真正直接与git checkout <hash>。这意味着您想要的是创建一个新提交,其快照与现有提交相同。有一些好的提交有一些现有的哈希ID,并且git checkout <hash>是......好的,但是你想要一个新的提交,其新的哈希ID,否则匹配好的哈希ID,在当前分支的(新)提示。这个新提交应该具有当前提交作为其父提交,就像任何新提交将当前提交作为其父提交一样。

joanis' answer中概述的方法将起作用:它将所选提交中的内容与当前工作树中的内容进行比较,并询问您是否要调整工作树以匹配所选提交,对于每个差异块中的每个文件。回答“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.txtmain.pyREADME。随后的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命令实际上是一个管道命令,用于制作新的花哨的前端命令,做一些事情,好吧,幻想。例如,有一天,有人可能会写一个git revert-to命令,它只包含一些状态检查,然后是git read-tree -u <hash>git commit


2
投票
git reset --hard good_commit_hash
git reset --soft current_commit_hash
git commit

会做你想做的。

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

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


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提出的解决方案比这个更好。

© www.soinside.com 2019 - 2024. All rights reserved.