HOWTO · Git
如何在 Git 中撤销 rm
了解如何恢复用 git rm 删除的文件,无论删除仍在暂存区还是已经提交。选择安全的 Git 命令,从索引或某个提交中恢复目标版本。
本页内容
要撤销 git rm,先确认删除仍在暂存区还是已经提交。提交之前,从 HEAD 恢复该路径;提交之后,选择包含目标版本的提交,并从该提交恢复路径。Git 可以恢复已提交的版本,但无法重建从未提交过的文件内容。
了解 git rm 更改了什么
git rm <path> 会从工作树中删除受跟踪的文件,并将其删除暂存到索引中。因此,该删除已准备好包含在下一次提交中。命令 rm 不是 git remove 的别名;rm 是 shell 命令,而 git rm 还会更新 Git 的索引。
当你希望 Git 停止跟踪某个文件、同时保留其工作树副本时,使用 git rm --cached <path>。这会将从索引中的删除暂存,并把文件留在磁盘上。常用于文件应保留在本地、但不再属于仓库的情况。
在提交前恢复删除
如果删除已暂存,且你需要最后提交的版本,使用 git restore --source=HEAD --staged --worktree -- <path> 从 HEAD 将路径同时恢复到索引和工作树。使用前先确认你的 Git 安装提供 git restore。如果不提供,git checkout HEAD -- <path> 会从 HEAD 将该路径恢复到索引和工作树。
这些命令恢复的是已提交的版本,而不是只存在于工作树中的编辑。先检查该路径的任何暂存或本地更改,因为同时恢复两个位置会以所选版本替换该路径。若要恢复多个路径,在 -- 之后逐一列出每个路径。
这个自包含示例会创建一个临时仓库、提交一条笔记、将其删除暂存,然后恢复已提交的版本。最后一行是恢复后的文件内容。
set -eu
repo_dir="$(mktemp -d)"
trap 'rm -rf "$repo_dir"' EXIT
cd "$repo_dir"
git init -q
git config user.name "Example User"
git config user.email "example@example.invalid"
printf 'A tracked note.\n' > notes.txt
git add notes.txt
git commit -qm "Add note"
git rm -q notes.txt
if git restore --source=HEAD --staged --worktree -- notes.txt 2>/dev/null; then
:
else
git checkout HEAD -- notes.txt
fi
cat notes.txt
预期标准输出:
A tracked note.
在提交后恢复删除
在提交记录了删除之后,选择包含目标文件版本的提交。然后使用 git restore --source=<commit> --staged --worktree -- <path> 只恢复该路径。如果 git restore 无法使用,使用 git checkout <commit> -- <path>。将 <commit> 和 <path> 替换为所选提交和相对于仓库的路径。
恢复单个路径并不会撤销删除它的提交;它会创建一个已暂存的文件恢复,你可以查看并提交。不要假设 HEAD~1 是正确的来源:可能存在更晚的提交,而紧邻的前一个提交也可能不包含你需要的版本。
用 Git 历史查找较早版本
使用 git log --all -- <path> 检查更改过该路径的提交,并找出包含目标版本的提交。git reflog 记录近期的分支顶端位置,可帮助在分支移动(例如重置)后定位提交。它不会单独存储文件内容;你选择的提交必须包含你需要的文件版本。
找到候选提交后,先确认它包含该路径,然后使用上述命令从候选提交恢复该路径。Git 历史只在内容已提交时才有帮助。如果文件从未提交,请改查编辑器备份、系统备份或其他副本。
选择安全的恢复方法
根据仓库状态选择来源:对于未提交的暂存删除,从 HEAD 恢复;对于已提交的删除,从某个特定的较早提交恢复。在提交恢复的文件之前,查看暂存的差异和工作树状态,特别是当同一路径上还有其他工作时。
避免将 git reset --hard 作为恢复单个文件的常规方法。它会重置索引和工作树,可能丢弃无关的未提交更改。从所选提交恢复单个路径更有针对性。对于多个文件,将每个路径提供给恢复命令,并在提交前查看产生的更改。