HOWTO · Git

在 Git 中把一个文件恢复到以前的提交状态

了解如何使用 git restore 或 git checkout 从以前的 Git 提交中还原受版本控制的文件、检查结果,并决定是否将其暂存并提交。

本页内容

要将一个已跟踪文件恢复为较早提交中的版本,请先找到包含所需版本的提交,然后运行 git restore --source <commit> -- <file-path>。此命令会更新工作树中的文件,不会更改提交历史;默认情况下,恢复后的版本不会被暂存。操作前请先检查或保存有价值的本地修改,因为恢复操作会替换工作树中的文件内容。如果希望将恢复结果记录在新提交中,之后再暂存并提交即可。

查找包含所需版本的提交

使用 git log --oneline -- <file-path> 查看修改过该文件的提交,并找出包含所需版本的提交。例如,对于仓库根目录下的 README.md,运行 git log --oneline -- README.md。-- 表示命令选项到此结束;当路径可能被误认为选项时,这样写很有用。对 git log 来说,路径可以相对于当前目录;也可以使用 Git 的 :(top) 路径语法指定相对于仓库根目录的路径,例如 :(top)README.md。

阅读提交摘要;必要时,使用 git show <commit>:<file-path> 检查候选提交中的文件内容。在这种写法中,冒号后的路径始终相对于仓库根目录,与当前目录无关。例如,如果你在 docs 子目录中运行 git log --oneline -- README.md 来查找对 docs/README.md 的修改,请用 git show <commit>:docs/README.md 检查该版本;如果文件是仓库根目录下的 README.md,无论你在仓库根目录还是子目录,都使用 git show <commit>:README.md。在恢复命令中使用日志显示的提交标识,可以是缩写,也可以是完整标识。即使之后的提交又修改了同一文件,较早的提交仍可能包含所需版本;应根据你想恢复的文件内容来选择,而不要只选最新的一条记录。

从之前的提交恢复文件

首先检查或保存目标文件中有价值的修改,然后用 git restore --source <commit> -- <file-path> 恢复选定版本。本文示例中的命令是 git restore --source 9cbe84d -- README.md。请将提交标识和路径替换为你仓库中的值。此命令会把该提交记录的文件版本复制到工作树中。它不会创建提交,也不会移动当前分支;默认情况下,恢复后的内容不会被暂存。

这些命令片段展示的是 Git 操作,并未附带声称实际运行所得的输出;操作效果取决于仓库历史以及当前暂存区和工作树的状态。恢复后,使用 git diff -- <file-path> 检查工作树中的改动。如果该路径还有已暂存的改动,请用 git diff --cached -- <file-path> 检查暂存版本,以确认下一次提交会包含哪些内容。

如何在 git restore 和 git checkout 之间选择

要恢复某个指定提交中的文件,git restore --source <commit> -- <file-path> 会明确指定来源提交,并更新工作树;默认不会暂存恢复结果。另一种写法是路径形式的 git checkout <commit> -- <file-path>。这种写法会同时更新工作树中的文件和暂存区,因此恢复后的版本会立即被暂存。准备提交时,这一区别很重要:运行 git checkout 后应检查已暂存的改动;运行 git restore 后,则只在确实打算提交时再自行暂存该文件。

这两种写法都会替换目标文件当前在工作树中的内容。运行任一命令前,都应先检查或保存本地修改,尤其是尚未提交到其他位置的修改。恢复文件本身不会创建提交;它只是改变后续工作的文件状态。直到你之后创建提交,分支历史才会发生变化。

暂存并提交恢复后的文件

运行 git restore 后,先检查改动;如果要将其纳入提交,使用 git add -- <file-path> 暂存文件。然后运行 git diff --cached -- <file-path> 确认暂存的版本,并创建一条说明恢复原因的提交信息。如果暂时不想记录这次恢复,可以让改动保留在工作树中。

运行路径形式的 git checkout <commit> -- <file-path> 后,恢复的版本已经位于暂存区。提交前用 git diff --cached -- <file-path> 检查它。接下来可以继续编辑文件、暂存这些编辑,或在保留工作树内容的同时取消该路径的暂存,具体取决于你要进行的操作。是否提交是独立于恢复单个文件的选择;恢复操作不会自动发布或分享改动。

理解 git revert 和 git reset 对文件的作用

git revert 与恢复文件解决的是不同问题。git revert <commit> 会创建一个新提交,用来反转较早提交引入的改动;它用于撤销某个提交,同时保留现有历史。它并不是从较早提交复制单个文件的命令。要恢复一个文件的旧版本,请使用上文的指定来源提交恢复流程,然后再决定是否提交恢复结果。

路径形式的 git reset HEAD -- <file-path> 会取消该路径的暂存:它会让暂存区与 HEAD 一致,同时保留工作树中的文件内容。它不会从指定提交恢复文件内容。若要丢弃工作树中的编辑并让文件回到当前 HEAD 中的版本,git restore -- <file-path> 默认会从 HEAD 恢复;这也会替换工作树中的内容,因此请先检查或保存修改。较旧的路径形式 git checkout -- <file-path> 也有类似的丢弃效果,但不会选择较早的提交。如果目标版本来自某个特定提交,请使用明确指定 --source <commit> 的写法。

如需了解更多信息,请参阅 Git restore 文档、Git checkout 文档,以及 Pro Git 中关于撤销操作的章节。