从git中还原特定的提交

66

我有一个包含大量提交和文件的git树。现在,我想撤销仅涉及某个文件的特定提交。为了解释清楚:

> git init
Initialized empty Git repository in /home/psankar/specific/.git/
> echo "File a" > a
> git add a ; git commit -m "File a"
[master (root-commit) 5267c21] File a
 1 file changed, 1 insertion(+)
 create mode 100644 a
> echo "File b" > b
> git add b; git commit -m "File b"
[master 7b560ae] File b
 1 file changed, 1 insertion(+)
 create mode 100644 b
> echo "File c" > c
> git add c; git commit -m "File c"
[master fd6c132] File c
 1 file changed, 1 insertion(+)
 create mode 100644 c
> echo "b and c modified" > b ; cp b c
> git commit -a -m "b and c modified"
[master 1d8b062] b and c modified
 2 files changed, 2 insertions(+), 2 deletions(-)
> echo "a modified" > a
> git commit -a -m "a modified"
[master 5b7e0cd] a modified
 1 file changed, 1 insertion(+), 1 deletion(-)
> echo "c modified" > c
> git commit -a -m "c modified"
[master b49eb8e] c modified
 1 file changed, 1 insertion(+), 1 deletion(-)
> git log --pretty=oneline c
> git log --pretty=oneline c | cat
b49eb8e03af331bddf90342af7d076f831282bc9 c modified
1d8b062748f23d5b75a77f120930af6610b8ff98 b and c modified
fd6c13282ae887598d39bcd894c050878c53ccf1 File c

现在我只想还原两个提交 b49eb81d8b06 而不还原对 a 文件的更改。也就是说,仅还原文件中的提交(而不回滚其他不同文件中可能有数千个的中间提交)。这是否可行?


use git rebase -i <commithash> - cnd
git show --stat -p COMMITID 命令将为您提供快速的行数统计信息和一个补丁,显示该提交所做的确切更改。有了这些信息,您可以决定是否要 git revert COMMITID。使用 -n 选项可以让您在提交之前进行评估,甚至可以执行 git revert --abort - stackunderflow
2个回答

122
您可以使用带有 --no-commit 选项的 git revert。以您的示例为例:
$ git revert --no-commit b49eb8e 1d8b062
# Files that were modified in those 2 commits will be changed in your working directory
# If any of those 2 commits had changed the file 'a' then you could discard the revert for it:
$ git checkout a
$ git commit -a -m "Revert commits b49eb8e and 1d8b062"

--no-commit选项不会进行自动提交,它允许您编辑并添加自己的提交消息。

请注意,与使用git reset不同,使用git revert将所有已还原的提交仍将存在于提交历史记录中。


6
对于 --no-commit 开关的加一并提到需要按照 相反的顺序 进行 revert 的操作。 - eckes
1
在我提问之前,我已经在我的机器上尝试过相同的操作,但是没有成功。现在我在一个新的 git 设置中(版本为1.8)尝试了一下,它可以正常工作。很可能是我之前使用的旧版 git(1.6)存在一个 bug。谢谢。 - Sankar

15

这里有两种情况:

  1. 当你已经将git树推送到某个地方,而且你不想更改历史记录时。在这种情况下,您需要一个新的提交来表达您在还原先前提交中所做的更改。您应该使用 @mamapitufo 的答案。

  2. 如果您从未推送更改所在的分支,并且可以更改历史记录。在这种情况下,您可以完全删除不需要的提交。这将使历史记录整洁,这意味着您不会向同事或公众推送错误的转折点。

在第二种情况下,您应该执行git rebase -i。找到位于您想要更改的任何历史记录之前的提交。这可能是提交的哈希值,也可能是分支或标记的名称。例如,您可以执行以下操作:

git rebase -i 23def8231

如果您从分支origin/dev_branch开始,并在名为dev_branch的分支上进行了包括要删除的部分的工作,则可以执行以下操作:

git push origin +dev_branch
git rebase -i origin/dev_branch

现在,您将进入一个编辑窗口,在该窗口中,您可以看到要进行rebase的所有提交记录列表。这可能是vim——如果您不通常在终端中编辑文件,则可能已设置为默认值。如果是这种情况,您可能需要一个在vim中快速开始的指南和开放的心态。

现在,最简单的方法是删除提交记录。您可以通过删除行或在行的开头添加#表示注释来完成此操作。(文件中已经有一些注释来向您解释事情。忽略或删除它们没有任何影响。)

完成后,请保存文件并退出编辑器。重新设置如下:git回到您命名的提交记录。它会遍历您保存的列表,并重放该列表中的每个提交。然后,它将该过程的结果作为您最初所在分支的新版本。

必须记住的重要事项:

  • 如果您迷失了方向或删除了太多行,请通过删除文件中的每个提交行并保存来取消rebase。rebase流程将以完全未更改的状态结束。
  • 可能会创建冲突。例如,如果您删除了编辑文件的提交,并保留了稍后编辑相同位置的提交,那么稍后的提交将无法正确应用,您必须手动编辑或使用合并工具来获得所需版本。

您还可以在git rebase -i中进行许多其他操作。例如,更改提交的顺序,将多个提交压缩成一个,添加额外的更改,或更改提交的消息。它非常有用。典型用例是在将更改推回其他人将查看您的更改的地方之前清理本地分支。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接