如何在项目提交历史中找回已删除的文件?

1874
从前,我的项目中有一个文件,现在我想找回它。
问题是:我不知道我什么时候删除了它,也不知道它的路径在哪里。
当这个文件存在时,我该如何定位它的提交记录呢?

24
这里的回答对我比“重复问题”的回答更有用。 - Felipe Alvarez
17
同意,无论是否有重复内容,它们在谷歌搜索中都没有出现,而这一个出现了。希望我们能停止浪费时间追寻重复的问题,只有时间和谷歌的算法才能确定哪个问题最好。 - Tim Boland
哪个重复了@FelipeAlvarez?望向地平线,却没有发现一个..也许曾经有过一个重复。 - Timo
11个回答

2299

如果您不知道确切的路径,可以使用

git log --all --full-history -- "**/thefile.*"

如果您知道文件的路径,可以这样做:

git log --all --full-history -- <path-to-file>

这应该显示涉及该文件的所有分支中提交的列表。然后,您可以找到所需的文件版本,并显示它与...

git show <SHA> -- <path-to-file>

或者使用以下命令将其恢复到您的工作副本中:

git checkout <SHA>^ -- <path-to-file>

请注意插入符号 (^),它从被标识的提交前开始检出,因为在 <SHA> 提交时文件已被删除,我们需要查看先前的提交以获取已删除文件的内容。


74
如果您不知道精确的路径,只知道文件名怎么办? - priestc
22
当你在一个分支上,而该文件从未存在过时,git log -- <path> 将没有输出。你应该始终使用 git log --all -- <path>,以确保不会错过其他分支上发生的更改。如果你有多个分支并且容易忘记路径和分支(就像我一样),那么 git log -- <path> 命令可能非常危险,如果你与其他开发人员一起工作也很危险。 - hobs
4
@Amber,考虑在你的git log答案中添加--all参数(感谢Philip),这样人们就不会错过其他分支上的修改和文件。这将为像我这样健忘的人节省很多烦恼。 - hobs
5
使用 **/thefile.* 时,通常最好加上引号,例如 '**/thefile.*',以保护 glob 中的 * 不被 shell 解析。(我不熟悉 Windows shell 是否会解析 *,但如果在 bash 中从当前工作目录中意外匹配到,那可能会出现问题。) - torek
3
确保将目录更改为git项目的根目录。当在子目录中使用命令时,我遇到了相对路径的问题。 - jorgenfb
显示剩余19条评论

501

获取已删除文件的列表,并复制已删除文件的完整路径。

git log --diff-filter=D --summary | grep delete

执行下一个命令以查找该提交的提交 ID 并复制提交 ID

git log --all -- FILEPATH

显示已删除文件的差异

git show COMMIT_ID -- FILE_PATH

请记住,您可以使用 > 将输出写入文件,例如:

git show COMMIT_ID -- FILE_PATH > deleted.diff

2
虽然我在第一步的帮助下找到了路径,但第二步却出现了错误:“未知的修订版本或路径不在工作树中”。 - JacobF
7
若要查看提交哈希和删除的文件,可以使用以下命令:git log --diff-filter=D --summary | grep -E 'delete|^commit\s+\S+' - Chris Middleton
2
第二步没有返回任何内容。有什么想法为什么会发生这种情况?我的文件名是正确的。 - Denys Kniazhev-Support Ukraine
2
要将这三个组合成一个函数,将其添加到您的.bashrc或.zshrc文件中:git-grep-latest(){ result_path=$(git log --diff-filter=D --summary | grep $1 | head -1 | awk '{print $4;}'); latest_commit=$(git log --all -- $result_path | head -1 | awk '{print $2;}'); git show $latest_commit -- $result_path; }现在,您只需执行以下命令:git-grep-latest some_text - randomor
1
@TylerJones,你可以使用Linux的管道将任何东西喂给任何东西-谷歌“Linux管道”..你会喜欢它的。 - John Hunt
显示剩余2条评论

109
假设你想恢复一个名为MyFile的文件,但对其路径(或扩展名)不确定:
0.(预备工作)通过进入git根目录来避免混淆

一个复杂的项目可能有多个具有相似或相同文件名的目录。

> cd <project-root>

1. 找到完整路径
> git log --diff-filter=D --summary | grep delete | grep MyFile

`delete mode 100644 full/path/to/MyFile.js`

full/path/to/MyFile.js 是你正在寻找的路径和文件。

2. 确定所有影响该文件的提交记录
> git log --oneline --follow -- full/path/to/MyFile.js

`bd8374c Some helpful commit message`

`ba8d20e Another prior commit message affecting that file`

`cfea812 The first message for a commit in which that file appeared.`
3. 结账文件

如果您选择第一个列出的提交(最后一个按时间顺序排列,这里是bd8374c),则无法找到该文件,因为它在该提交中被删除了。

> git checkout bd8374c -- full/path/to/MyFile.js

`error: pathspec 'full/path/to/MyFile.js' did not match any file(s) known to git.`

只需选择前一个(追加插入符)提交:
> git checkout bd8374c^ -- full/path/to/MyFile.js

11
这比被接受的答案更清晰易懂。 - pouyan021
2
在Windows控制台(cmd)中的第二步骤请使用find而不是grep: git log --diff-filter=D --summary | find "delete" | find "MyFile"在第三步骤中,请注意引号包围哈希值:git checkout "bd8374c^" -- full/path/to/MyFile.js - user5542121
1
我同意@pouyan021的看法。这个答案让我很快找到了删除该文件的提交。 - Yinci
1
根据@user5542121的建议,如果您是Windows用户并且使用PowerShell而不是cmd,则将“find”替换为“Select-String”。因此,git log --diff-filter=D --summary | Select-String "delete" | Select-String "MyFile" - Silverfish

41

无法编辑已接受的响应,因此在此添加答案,

要在 git 中恢复文件,请使用以下命令(请注意 SHA 后面紧跟着 '^' 符号)

git checkout <SHA>^ -- /path/to/file

我不明白为什么你想要“^”。文件已经在该SHA提交中了...你为什么还想要回退到另一个提交呢? - Tony K.
22
它在具有该sha的提交记录中标记为“已删除”,这意味着它仍然不存在。你必须转到之前的提交记录才能真正恢复它。 - tandrewnichols
6
这句话的意思是,你使用的提交 SHA 错误了,你需要获取你所需文件版本的提交。可能你想要的文件版本已经被删除了,因此需要确认提交 SHA 是否正确。 - Amber
8
@Amber,你想要的提交很可能是在被删除之前最近的提交,因此这就是答案。 - Sam Holder
感谢您的提示。请注意,在Windows中需要使用双引号。 - Alex R
1
@AlexR: <SHA>~1应该可以不用加引号就能起到相同的作用。 - CodeManX

37

@Amber 给出了正确的答案!再补充一点,如果您不知道文件的确切路径,您可以使用通配符!这对我有用。

git log --all -- **/thefile.*

7
嗯,我不确定是否应该将现有答案复制到得票最高的答案中:/ 这个答案本身也很有用;点赞可能已经足够了? - Clément
3
如果文件位于项目根目录下(在 Cygwin 上测试过),这将无法找到该文件。 - wortwart

25

以下是一个简单的命令,开发人员或 Git 用户可以传递从存储库根目录中删除的文件名称并获取其历史记录:

git log --diff-filter=D --summary | grep filename | awk '{print $4; exit}' | xargs git log --all -- 

如果有人能改进这个命令,请务必进行修改。


1
太棒了,谢谢!看起来我的文件根本不存在,但这是一个单独且更加棘手的问题... - user657199
请确保你在存储库根目录下运行此命令,如果你的文件似乎“丢失”。 - samaspin

19

尝试使用其中一个查看器,例如gitk,这样您就可以浏览历史记录以查找半个被遗忘的文件。(如果需要所有分支,请使用 gitk --all )


4
“--all”选项对于你的回答和被接受的答案都非常重要。 - hobs
4
浏览历史对于大多数项目来说需要花费很长时间。 - mikemaccana

13

如果您希望查看所有已删除文件的大小

以及相关的SHA

git log --all --stat --diff-filter=D --oneline

添加-p|--patch选项可以查看内容

git log --all --stat --diff-filter=D -p

要缩小到任何文件,您有两个简单的选项,可以使用路径规范或管道到 grep 并搜索文件名。

使用 grep:

git log --all --stat --diff-filter=D --oneline | grep foo

使用路径规范:
git log --all --stat --diff-filter=D --oneline -- '*foo*'

路径规范与-p|--patch结合使用时,如果您想查看内容,则可以很好地发挥作用。
git log --all --stat --diff-filter=D --oneline --patch -- '*foo*'

如果您知道文件的位置,您可能也会喜欢这个。

git log --all --full-history -- someFileName

这是我找到的最有效的定位删除特定文件提交的方法。非常感谢。 - MadHatter

7

简介:

  1. 步骤1

在已删除文件的历史记录中搜索文件完整路径 git log --diff-filter=D --summary | grep filename

  1. 步骤2

从被删除之前的提交中恢复文件

restore () {
  filepath="$@"
  last_commit=$(git log --all --full-history -- $filepath | grep commit | head -1 | awk '{print $2; exit}')
  echo "Restoring file from commit before $last_commit"
  git checkout $last_commit^ -- $filepath
}

restore my/file_path

2

这是我的解决方案:

git log --all --full-history --oneline -- <RELATIVE_FILE_PATH>
git checkout <COMMIT_SHA>^ -- <RELATIVE_FILE_PATH>

1
请阅读[回答]并[编辑]您的问题,以包含一个解释,说明这段代码为什么实际上可以解决手头的问题。请记住,您不仅要解决问题,还要教育提问者和任何未来阅读此帖子的读者。 - Adriaan

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