恢复多个提交并合并回特定提交而无需强制推送
Reverting multiple commits and merges back to a specific commit without force pushing
问题:
- 具有约 50 次提交的远程存储库应还原为 1 次特定提交(如 git reset --hard "my specific commit")
- 在远程提交和合并
- 完全没有力推
我知道我可以 git revert -n "my specific commit"..HEAD 问题是它在第一次合并时停止。通过 -m 1 会解决这个问题,但它会在非合并时停止。
git 重置 --hard 和强制推送是 NOT 一个选项。我无法强制推送此 git 主机。
情况:(我为什么需要这个)
- master 中的错误更改,因此我们签出一个新分支并希望从旧状态(来自 "specific commit")构建一个新版本,以便我们可以将该构建部署到 prod。
- 单个提交很容易还原,但批量还原很耗时,充其量我想一次还原大量提交
我知道这里有很多关于这个的问题,但我还没有找到一个有这个确切问题的人,并且没有强制推动的合理解决方案。
您可以使用 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 提出的解决方案比这个更可取。
TL;DR
使用 git read-tree -u <hash>
,然后进行新的提交。
长
请记住,Git 存储库本质上是提交的集合。每个提交都包含所有文件的完整快照——好吧,是该提交中的所有文件,但这样表达听起来像是同义反复——加上一些 元数据: 名称和电子邮件地址提交人的姓名、提交时间的时间戳、解释为什么他们提交的日志消息,等等。 (元数据的关键部分之一是此提交的 parent 提交的哈希 ID,但这一次我们根本不必关心这部分!)
任何给定提交的真实名称是其哈希 ID。您可以通过以下方式暂时将此提交粘贴到您的工作树中:
git checkout <hash-id>
但当然这只会让你得到一个 "detached HEAD",当你通过 git checkout master
重新附加你的 HEAD 时,你又回到了损坏的源代码快照。
因此,每次提交都代表所有文件的快照。你有一个快照,在某个时候,你喜欢,你想回到它——但实际上并没有直接 到 它 git checkout <hash>
。这意味着您想要的是进行 new 提交,其快照与现有提交相同。有一些很好的提交和一些现有的哈希 ID,并且 git checkout <hash>
是......好的,但是你想要一个 new 提交和 new 哈希 ID,在当前分支的(新)尖端与好的哈希 ID 相匹配。这个新提交应该将当前提交作为其父提交,就像 any 新提交将当前提交作为其父提交一样。
中概述的方法将起作用:它将选定提交中的内容与当前工作树中的内容进行比较,并询问您是否要调整工作树以匹配选定的提交,对于每个文件中的每个 diff-hunk。回答 "a" 表示 "take all for this file",但这让您需要输入很多答案,每个不同的文件一个。
使用不同的 git checkout
模式更简单但有缺陷:
git checkout <good-commit-hash> -- .
此模式告诉 git checkout
:不要将 切换到 另一个提交。不要以任何方式改变 HEAD
本身!但是 do 搜索 Git 数据库以查找与良好提交中的路径说明符 .
匹配的文件。对于每个匹配的文件,将其从该提交中取出,将其复制到我当前的索引,然后将其复制到我当前的工作树。
如果您在工作树的顶层执行此操作,则良好提交中的 所有 文件将匹配路径说明符 .
。因此,如果您在提交 <hash>
中有文件 README
和 main.py
,它们将替换索引中的 README
和 main.py
,以及 README
和 main.py
在你的工作树中。您将准备好提交。
这里的缺陷是:如果你现在有一个 third 文件,比如说 bug.txt
,那么 isn 't in the good commit you're trying to switch back to? 要解决这个问题,你必须明确地 删除 当前文件中的任何文件索引和工作树 not 在好的提交中。
您可以手动删除任何此类文件。或者可能没有任何此类文件,在这种情况下,问题只是理论上的。但是有一个保证的治疗方法,如果没有问题就是无害的。您可以从以下开始:
git rm -r .
删除所有内容。这将删除 bug.txt
and main.py
and README
。随后的 git checkout <good-commit-hash> -- .
放回 好的 main.py
和 README
,并且 bug.txt
仍然被删除,因此您可以 git commit
结果。
不过,有一个较低级别的 Git 命令可以为您执行此操作,那就是 git read-tree
。此命令并非真正适合日常使用,1 但这也不是日常问题。在这种特殊情况下,使用:
git read-tree -u <good-commit-hash>
告诉管道命令:清除我当前的索引。从给定的哈希 ID 中获取文件并将它们放入我的索引中。无论你在哪里完全删除了一个文件,也从我的工作树中删除 if 。无论你在哪里替换了一个文件,也要在我的工作树中替换它。 就文件及其内容而言,这与你使用 git rm -r .; git checkout <good-commit-hash> -- .
获得的结果完全相同,只是它更多高效。
在任何一种情况下,您现在都可以使用当前索引内容进行新提交,该索引内容现在与当前工作树内容匹配(未跟踪文件除外,它们像往常一样保持未跟踪状态)。
1git read-tree
命令实际上是一个 管道命令,用于制作新的花哨的前端命令东西,嗯,花哨的。例如,某天某人可能会写一个 git revert-to
命令,它只包含一些状态检查,然后是 git read-tree -u <hash>
和 git commit
.
git reset --hard good_commit_hash
git reset --soft current_commit_hash
git commit
会做你想做的。
(如果上游(远程)分支是您要提交的地方,您可以简单地使用“@{u}”作为 "current_commit_hash"。)
简而言之:如果您想在您的存储库中进行任何类型的清理,请使用 git 重置。有点像瑞士军刀。
问题:
- 具有约 50 次提交的远程存储库应还原为 1 次特定提交(如 git reset --hard "my specific commit")
- 在远程提交和合并
- 完全没有力推
我知道我可以 git revert -n "my specific commit"..HEAD 问题是它在第一次合并时停止。通过 -m 1 会解决这个问题,但它会在非合并时停止。
git 重置 --hard 和强制推送是 NOT 一个选项。我无法强制推送此 git 主机。
情况:(我为什么需要这个)
- master 中的错误更改,因此我们签出一个新分支并希望从旧状态(来自 "specific commit")构建一个新版本,以便我们可以将该构建部署到 prod。
- 单个提交很容易还原,但批量还原很耗时,充其量我想一次还原大量提交
我知道这里有很多关于这个的问题,但我还没有找到一个有这个确切问题的人,并且没有强制推动的合理解决方案。
您可以使用 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 提出的解决方案比这个更可取。
TL;DR
使用 git read-tree -u <hash>
,然后进行新的提交。
长
请记住,Git 存储库本质上是提交的集合。每个提交都包含所有文件的完整快照——好吧,是该提交中的所有文件,但这样表达听起来像是同义反复——加上一些 元数据: 名称和电子邮件地址提交人的姓名、提交时间的时间戳、解释为什么他们提交的日志消息,等等。 (元数据的关键部分之一是此提交的 parent 提交的哈希 ID,但这一次我们根本不必关心这部分!)
任何给定提交的真实名称是其哈希 ID。您可以通过以下方式暂时将此提交粘贴到您的工作树中:
git checkout <hash-id>
但当然这只会让你得到一个 "detached HEAD",当你通过 git checkout master
重新附加你的 HEAD 时,你又回到了损坏的源代码快照。
因此,每次提交都代表所有文件的快照。你有一个快照,在某个时候,你喜欢,你想回到它——但实际上并没有直接 到 它 git checkout <hash>
。这意味着您想要的是进行 new 提交,其快照与现有提交相同。有一些很好的提交和一些现有的哈希 ID,并且 git checkout <hash>
是......好的,但是你想要一个 new 提交和 new 哈希 ID,在当前分支的(新)尖端与好的哈希 ID 相匹配。这个新提交应该将当前提交作为其父提交,就像 any 新提交将当前提交作为其父提交一样。
使用不同的 git checkout
模式更简单但有缺陷:
git checkout <good-commit-hash> -- .
此模式告诉 git checkout
:不要将 切换到 另一个提交。不要以任何方式改变 HEAD
本身!但是 do 搜索 Git 数据库以查找与良好提交中的路径说明符 .
匹配的文件。对于每个匹配的文件,将其从该提交中取出,将其复制到我当前的索引,然后将其复制到我当前的工作树。
如果您在工作树的顶层执行此操作,则良好提交中的 所有 文件将匹配路径说明符 .
。因此,如果您在提交 <hash>
中有文件 README
和 main.py
,它们将替换索引中的 README
和 main.py
,以及 README
和 main.py
在你的工作树中。您将准备好提交。
这里的缺陷是:如果你现在有一个 third 文件,比如说 bug.txt
,那么 isn 't in the good commit you're trying to switch back to? 要解决这个问题,你必须明确地 删除 当前文件中的任何文件索引和工作树 not 在好的提交中。
您可以手动删除任何此类文件。或者可能没有任何此类文件,在这种情况下,问题只是理论上的。但是有一个保证的治疗方法,如果没有问题就是无害的。您可以从以下开始:
git rm -r .
删除所有内容。这将删除 bug.txt
and main.py
and README
。随后的 git checkout <good-commit-hash> -- .
放回 好的 main.py
和 README
,并且 bug.txt
仍然被删除,因此您可以 git commit
结果。
不过,有一个较低级别的 Git 命令可以为您执行此操作,那就是 git read-tree
。此命令并非真正适合日常使用,1 但这也不是日常问题。在这种特殊情况下,使用:
git read-tree -u <good-commit-hash>
告诉管道命令:清除我当前的索引。从给定的哈希 ID 中获取文件并将它们放入我的索引中。无论你在哪里完全删除了一个文件,也从我的工作树中删除 if 。无论你在哪里替换了一个文件,也要在我的工作树中替换它。 就文件及其内容而言,这与你使用 git rm -r .; git checkout <good-commit-hash> -- .
获得的结果完全相同,只是它更多高效。
在任何一种情况下,您现在都可以使用当前索引内容进行新提交,该索引内容现在与当前工作树内容匹配(未跟踪文件除外,它们像往常一样保持未跟踪状态)。
1git read-tree
命令实际上是一个 管道命令,用于制作新的花哨的前端命令东西,嗯,花哨的。例如,某天某人可能会写一个 git revert-to
命令,它只包含一些状态检查,然后是 git read-tree -u <hash>
和 git commit
.
git reset --hard good_commit_hash
git reset --soft current_commit_hash
git commit
会做你想做的。
(如果上游(远程)分支是您要提交的地方,您可以简单地使用“@{u}”作为 "current_commit_hash"。)
简而言之:如果您想在您的存储库中进行任何类型的清理,请使用 git 重置。有点像瑞士军刀。