使用git撤销一次提交的部分更改

183

我想在git中还原特定的提交。不幸的是,我们的组织仍然使用CVS作为标准,因此当我提交回CVS时,多个git提交被合并成一个提交。在这种情况下,我希望能够单独挑选原始的git提交,但这是不可能的。

是否有一种类似于git add --patch的方法,允许我有选择性地编辑差异,并决定要还原哪些提交的部分?


更多解决方案请点击这里,但重点关注于将部分还原限制在特定文件中。 - ntc2
6个回答

277

使用--no-commit-n)选项来执行git revert,然后取消暂存更改,接着使用git add --patch

$ git revert -n $bad_commit    # Revert the commit, but don't commit the changes
$ git reset HEAD .             # Unstage the changes
$ git add --patch .            # Add whatever changes you want
$ git commit                   # Commit those changes

注意:您使用 git add --patch 添加的文件是要回滚的文件,而不是要保留的文件。

15
也许值得添加最后所需的命令,针对那些不太熟悉 Git 的人:在提交之后,运行 git reset --hard 命令来丢弃你不想恢复的其他更改。 - tremby
20
git reset --hard对于新手来说是危险的,因为它可能会丢失想要保留的编辑内容。相反,应该熟悉 git status 命令,并使用 git checkout -- FILE.. 命令更安全地还原修改。 - Tino
3
git 真是一个明显的疏漏,git revert 应该只需要添加一个 --patch 参数就好了。 - Kaz
@Kaz:git revert 用于撤销整个提交。您可以使用 git checkout -p 交互式地选择要撤销的位。 - mipadi
1
我还想补充一点(也许很明显),那就是首先保存你的工作。要么先进行commit,或者stash,然后再尝试revert - Felipe Alvarez
显示剩余2条评论

52

我已经成功地使用了以下方法。

首先还原全部的提交(将其放入索引中),但不要提交。

git revert -n <sha1>  # -n is short for --no-commit

然后交互式地从索引中删除已还原的良好更改

git reset -p          # -p is short for --patch  

然后提交坏更改的反向差异

git commit -m "Partially revert <sha1>..."

最后,被重置命令撤销的“好”的更改(即未暂存的更改)仍然在工作树中。它们需要被清除。如果工作树中没有其他未提交的更改,则可以通过以下方式完成:

git reset --hard

5
这是否不是一个更优的替代方案,相较于常见答案(使用reset HEAD .),因为它不需要清理工作目录的最终操作? - Steven Lu
2
这个答案更优越,因为 reset -preset HEAD 加上 add -p 更短。但是它仍然需要清理,因为已重置的“好”块在提交后仍然存在于工作目录中。 - chtenb
这个答案并不是最优的,因为交互式地删除你想要的更改通常会令人困惑和容易出错,特别是如果其中任何一个需要进行编辑。 - Kaz

8

解决方案:

git revert --no-commit <commit hash>
git reset -p        # every time choose 'y' if you want keep the change, otherwise choose 'n'
git commit -m "Revert ..."
git checkout -- .   # Don't forget to use it.

1
如果您能说明它与已接受的解决方案有何不同,这将对人们有所帮助。 - CharlesB
3
为什么结账流程很重要,这个解决方案与user1338062的方案有何不同? - martin

8

另一个选择(如果您当前的文件版本与您要还原的版本之间的差距不太大)是进行交互式检出。查看git日志并获取要撤消的提交的哈希或其之前的提交。您的命令将变为以下两种之一:

git checkout -p commit-hash git checkout -p HEAD~1
git checkout --patch HASH_OF_COMMIT_TO_REVERT^ -- file/you/want/to/fix.ext

(其中^表示父提交)或:
git checkout --patch HASH_PRECEDING_COMMIT_TO_REVERT -- file/you/want/to/fix.ext

您可以选择还原补丁中的某些部分或块,方法是在打补丁时选择s(拆分)选项。

这确实会更改您工作树中的文件,但不会创建提交,所以如果您真的出了岔子,可以使用git reset --hard -- file/you/want/to/fix.ext重新开始。


6

个人而言,我更喜欢这个版本,它重复使用自动生成的提交信息,并让用户有机会在最终提交之前编辑并添加单词“部分”。

# generate a revert commit
# note the hash printed to console on success
git revert --no-edit <hash to revert>

# undo that commit, but not its changes to the working tree
# (reset index to commit-before-last; that is, one graph entry up from HEAD)
git reset HEAD~1

# interactively add reversions
git add -p

# commit with pre-filled message
git commit -c <hash from revert commit, printed to console after first command>

# reset the rest of the current directory's working tree to match git
# this will reapply the excluded parts of the reversion to the working tree
# you may need to change the paths to be checked out
# be careful not to accidentally overwrite unsaved work
git checkout -- .

1

您可以使用 git-revert -n 命令,然后使用 add --patch 命令选择需要的 hunks。


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