例如-我有3个提交 提交A-> B-> C,我的头目前在C处 现在我想创建另一个提交D,它将具有与A相同的代码,以便我可以将该提交推送到远程分支而不更改历史记录。
当人们在Git中说“我想还原”时,有时他们的意思是git revert
所做的事情,这更像是一个撤销操作,而有时他们的意思是你实际所做的事情,即从早期版本恢复源代码库。
举个例子,假设我们有一个提交只包含一个文件README
,并且进行了三次提交:
A <-B <-C <-- master (HEAD)
版本A的README
文件说:“我是一个README文件”,只有一行。
版本B的README
文件与之前相同,也说“我是一个README文件”,但新增了第二行:“这个文件共有五行。”
版本C的README
文件被更正了,它的第二行说:“这个文件共有两行。”
Git的git revert
命令可以撤销一个变更。现在,运行git revert <hash-of-B>
会尝试移除添加的一行。由于该行不再匹配,因此操作将失败(我们可以运行git revert --abort
来放弃操作)。类似地,运行git revert <hash-of-C>
将尝试撤销更正。这将成功,实际上回滚到了版本B
!
这个问题Undo a particular commit in Git that's been pushed to remote repos,全都是关于撤销的,虽然有时会导致恢复之前某个版本,但并不完全相同。根据你的问题,你想要的是:“让我生成一个新的提交D
,其源代码与提交A
相同”。你想要恢复到版本A
。
这个问题How to revert Git repository to a previous commit?,充满了使用git reset --hard
命令来执行该操作的答案,但这样做会删除历史记录。然而,被接受的答案中包含一个关键点,具体来说是:
该命令告诉Git从给定的提交git checkout 0d1d7fc32 .
0d1d7fc32
中提取所有文件,并且只提取当前目录(.
)中的文件。如果你的当前目录是工作树的顶部,那么它将递归包含子目录中的所有文件。但是它有一个问题,就是它确实提取了所有文件,但是它不会移除(从索引和工作树)任何您不需要的文件。为了说明这一点,让我们回到我们的三个提交存储库并添加第四个提交:
$ echo new file > newfile $ git add newfile $ git commit -m 'add new file'
现在我们有四个提交:
A <-B <-C <-D <-- master (HEAD)
如果提交
D
包含正确的两行README
,并且包含新文件newfile
。那么我们可以执行:
$ git checkout <hash-of-A> -- .
我们将使用提交
A
中的版本覆盖README
的索引和工作树版本。我们回到了单行的README
,但是在我们的索引和工作树中仍然有文件newfile
。为了解决这个问题,我们不应该只从提交中检出所有文件,而是应该先删除索引中存在的所有文件:
$ git rm -r -- .
然后从提交记录
A
安全地重新填充索引和工作树:$ git checkout <hash> -- .
(我试图在这里自动使用
--
,以防所需的路径名称类似于选项或分支名称等;即使我只想检出名为-f
的文件或目录,它也可以工作。)完成这两个步骤后,就可以安全地
git commit
提交结果了。次要: 一种快捷方式
由于Git实际上只是从索引中进行提交,因此您只需将所需的提交复制到索引中即可。
git read-tree
命令可以实现此功能。 您可以同时更新工作树,因此:$ git read-tree -u <hash>
使用替换而非删除和检查的方法就足够了。(你仍然需要像往常一样提交一个新的 commit。)
开始之前,请记得 git stash
或备份所有未提交的文件,因为这些命令执行后它们将会丢失。
git reset --hard <提交A>
git reset --soft <提交C>
git add . && git commit -m "提交D"
现在处于D状态,之前的提交A、B和C都已保留。
git reset --hard
命令的用户来说,我认为这是最好的方法。 - Devin Rhode
git revert [commit]
撤销提交,然后将其推送到新的提交中有什么问题吗?这样做不会将本地分支上的提交还原回A,然后您可以将此新的A推送到D上。 - L_ChurchD
拥有与A
相同的更改,还是要让D
撤销A
引入的更改? - Tim Biegeleisen