git revert 回到某个提交而不更改历史记录并创建一个新的提交,如 git revert

Posted

技术标签:

【中文标题】git revert 回到某个提交而不更改历史记录并创建一个新的提交,如 git revert【英文标题】:git revert back to certain commit without changing history and creating a new commit like git revert 【发布时间】:2018-08-29 12:03:53 【问题描述】:

有没有办法在不更改远程历史记录的情况下恢复到某个提交,基本上就像 git revert 一样在新提交中撤消该提交的所有更改?

例如 - 我有 3 个提交 提交 A -> B -> C,我的头目前在 C 我现在想创建另一个提交 D,它的代码与 A 相同,这样我就可以将该提交推送到远程分支而不更改历史记录。

【问题讨论】:

git revert [commit] 然后将其推入新提交有什么问题?不会将提交还原回本地分支上的 A,那么您可以将这个新的 A 推送到 D 您希望提交D 具有与A 相同的 更改,还是希望D 恢复A 引入的更改? 我希望 D 具有与 A 相同的代码。我不想还原 A 引入的更改。 @L_Church git revert A 将恢复 A 引入的更改,但 B & C 引入的更改不受影响,我想完全移至 A 处的代码,即撤消 A 之后所做的所有更改。 【参考方案1】:
    git reset --hard <Commit A> git reset --soft <Commit C> git add . && git commit -m "Commit D"

现在在 D 并且之前的提交 A、B 和 C 都被保留了

【讨论】:

我不知道为什么我投了反对票,我想可能是因为它看起来有点棘手。但这是实现它的唯一简单方法。请自己做一些实验,看看它是否有效,然后再投反对票.. 我尝试按照接受的答案中的说明进行操作,但我要么做错了什么,要么做错了什么 - 命令 git read-tree 没有做我想要的。您的解决方案确实 有效,但这是一个糟糕的 hack。 (这真的应该可以在不移动分支指针的情况下完成。)【参考方案2】:

小心“revert”这个词

当人们在 Git 中说“我想恢复”时,他们有时是指 git revert 所做的事情,这更像是一个退出操作,有时是指你所做的事情,即 从早期版本恢复源库。

为了说明,假设我们有一个只有一个文件README 和三个提交的提交:

A <-B <-C   <-- master (HEAD)

修订版 A 中 README 的版本显示“我是一个 README 文件”,并且只有一行长。

修订版 B 中的 README 版本显示“我是自述文件”。和以前一样,但添加了第二行,“这个文件有五行长。”

修订版 C 中的 README 版本已更正,因为它的第二行显示“此文件有两行长。”

Git 的git revert 可以撤消更改,因此现在运行git revert &lt;hash-of-B&gt; 将尝试删除添加的行。这将失败,因为该行不再匹配(我们可以运行git revert --abort 放弃)。同样,运行git revert &lt;hash-of-C&gt; 将尝试撤消更正。这将成功,有效地将修订版B

这个问题,Undo a particular commit in Git that's been pushed to remote repos,完全是关于回退类型的还原。虽然这有时会导致恢复到那种恢复,但它并不相同。根据您的问题,您想要的更多:“让我成为一个新的提交D,它与提交A 具有相同的源代码”。你想恢复到版本A

Git 没有用户命令恢复到,但是很简单

How to revert Git repository to a previous commit? 这个问题充满了关于使用 git reset --hard 的答案,它可以完成这项工作,但它是通过删除历史来实现的。不过,accepted answer 包含其中一个键,特别是:

git checkout 0d1d7fc32 .

此命令告诉 Git 从给定的提交 0d1d7fc32 中提取该快照和当前目录 (.) 中的所有文件。如果您的当前目录是工作树的顶部,则将从所有目录中提取文件,因为. 递归地包含子目录文件。

这样做的一个问题是,是的,它会提取所有文件,但它不会删除(从索引和工作树中)您拥有但没有的任何文件'不想要。为了说明,让我们回到我们的三提交存储库并添加第四个提交:

$ echo new file > newfile
$ git add newfile
$ git commit -m 'add new file'

现在我们有四个提交:

A <-B <-C <-D   <-- master (HEAD)

commit D 有正确的两行 README新文件 newfile

如果我们这样做:

$ git checkout <hash-of-A> -- .

我们将用来自提交A 的版本覆盖README 的索引和工作树版本。我们将回到单线README。但是我们的索引和工作树中仍然会有文件newfile

要解决这个问题,我们应该从删除索引中的所有文件开始,而不是从提交中签出所有文件:

$ git rm -r -- .

然后从提交A重新填充索引和工作树是安全的:

$ git checkout <hash> -- .

(我尝试在此处自动使用--,以防我想要的路径名类似于选项或分支名称或类似名称;即使我只想检查名为@ 的文件或目录,它也能正常工作例如 987654357@)。

完成这两个步骤后,就可以安全地git commit 结果了。

次要:快捷方式

由于 Git 实际上只是从索引中进行提交,您所要做的就是将所需的提交复制到索引中。 git read-tree 命令执行此操作。你可以让它同时更新工作树,所以:

$ git read-tree -u <hash>

足以代替删除和结帐。 (您仍然必须像往常一样进行新的提交。)

【讨论】:

很好的解释,谢谢, git read-tree -u --reset 解决了我的问题。

以上是关于git revert 回到某个提交而不更改历史记录并创建一个新的提交,如 git revert的主要内容,如果未能解决你的问题,请参考以下文章

git revert

git操作技巧之Revert(Intellij Idea环境下)

在 master 上清理 git 历史记录的正确方法

如何“取消还原”已还原的 Git 提交?

git revert和git reset的区别

git revert和git reset的区别