题 在Git存储库中查找并恢复已删除的文件


说我在Git存储库中。我删除了一个文件并提交了更改。我继续工作,并做了一些更多的提交。然后,我发现我需要恢复该文件。

我知道我可以使用 git checkout HEAD^ foo.bar,但我真的不知道该文件何时被删除。

  1. 找到删除给定文件名的提交的最快方法是什么?
  2. 将该文件恢复到我的工作副本的最简单方法是什么?

我希望我不必手动浏览我的日志,检查整个项目的给定SHA,然后手动将该文件复制到我的原始项目结帐中。


2386
2018-06-04 22:40


起源


$ git checkout deletedFile,没有人明确说过这个?!回答未来googlers的标题...... - hhh
请注意,之前的评论回答标题中的问题,而不是正文中的问题 - 包括查找 什么时候 该文件已被删除。 - avdgaag
要查找提交,文件已删除: git log --diff-filter=D -- path/to/file - titaniumdecoy
有关: 你如何丢弃git中的非分段更改?。
@hhh git checkout deletedFile 将取消删除 deletedFile 如果它被删除但删除 还没有上演或承诺。这不是这里要求的问题;这个问题是关于如何恢复删除多次提交之前提交的文件。 - Mark Amery


答案:


查找影响给定路径的最后一次提交。由于该文件不在HEAD提交中,因此该提交必须已将其删除。

git rev-list -n 1 HEAD -- <file_path>

然后使用插入符号检查提交前的版本(^)符号:

git checkout <deleting_commit>^ -- <file_path>

或者在一个命令中,如果 $file 是有问题的文件。

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

如果您使用zsh并启用了EXTENDED_GLOB选项,则插入符号将不起作用。您可以使用 ~1 代替。

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"

2725
2017-07-11 07:12



棘手的一点是使用^后缀检查提交BEFORE。谢谢。 - Christian Oudard
最后的^是什么? - ranman
@Ranman:这意味着“第一个父母”。 - CB Bailey
从Windows命令行我收到一个错误。 error: pathspec <filename> did not match any file(s) known to git.。解决方案是使用git bash。 - donturner
@zoras zsh在'^'上有自己的扩展我相信,但你可以使用'~1'的替代语法: git checkout <deleting-commit>~1 -- <file-path>  ~X允许你在指定的commit之前指定X提交,所以~1是之前的提交,~2是之前的两次提交,等等 - Nils Luxton


  1. 使用 git log --diff-filter=D --summary 获取已删除文件和删除文件的所有提交;
  2. 使用 git checkout $commit~1 filename 恢复已删除的文件。

哪里 $commit 是您在步骤1中找到的提交的值,例如 e4cf499627


724
2018-06-04 23:10



好奇,~1指的是什么? - tommy chheng
@tommy - 代字号规范将为您提供命名提交的第n个孙子。看到 book.git-scm.com/4_git_treeishes.html 更多细节 。 - Robert Munteanu
这是迄今为止最简单直观的方法。 git log -- *PartOfMyFileName*。谢谢你 $commit~1 - bgs
该 git checkout $commit~1 filename 语法适用于单个文件,也适用于整个目录。即:从sha 12345恢复./images中的所有已删除图像: git checkout 12345~1 images。谢谢你的回答! - noinput
@Alexar $commit~1 表示您应该添加提交的名称。就像是 1d0c9ef6eb4e39488490543570c31c2ff594426c 哪里 $commit 是。 - Eugene


要还原文件夹中的所有已删除文件,请输入以下命令。

git ls-files -d | xargs git checkout --

302
2017-12-02 06:11



文件传递到哪里?我看不出变化。 - William Grand
这可能是最简单的方法。它变态多么困难 git 甚至做了最简单的任务。 - jww
什么意思 - ? - Tebe
git checkout - [file]将还原[file]中的更改。管道将用已删除文件的名称替换[file]。 - Manu
该 ls-files 子命令很方便,但似乎不适用于已删除的文件 git rm 即上演,更不用说承诺,这是OP所要求的。 - MarkHu


我来到这个问题寻找恢复我刚删除的文件,但我还没有提交更改。如果您发现自己处于这种情况,您需要做的就是以下内容:

git checkout HEAD -- path/to/file.ext


100
2018-04-10 00:03



谢谢。这个对我有用。恢复我尚未提交更改的文件。 - Hua Zhang
这是最简单也是最好的。 - SmallChess


如果你疯了,请使用 git-bisect。这是做什么的:

git bisect start
git bisect bad
git bisect good <some commit where you know the file existed>

现在是时候运行自动化测试了。 shell命令 '[ -e foo.bar ]' 如果是,将返回0 foo.bar 存在,否则为1。 “运行”命令 git-bisect 将使用二进制搜索自动查找测试失败的第一个提交。它从给定范围的中间开始(从好到坏),并根据指定测试的结果将其减半。

git bisect run '[ -e foo.bar ]'

现在你正处于删除它的提交中。从这里,您可以跳回到未来并使用 git-revert 撤消更改,

git bisect reset
git revert <the offending commit>

或者您可以返回一个提交并手动检查损坏:

git checkout HEAD^
cp foo.bar /tmp
git bisect reset
cp /tmp/foo.bar .

82
2018-06-04 22:46



你能详细说说吗? git bisect run '[ -e foo.bar ]'? - avdgaag
如果无法自动检查,您也可以手动使用好的和坏的。请参见bisect手册页。 - Josh Lee
这不是最简单的解决方案,但它令人印象深刻。谢谢你的写作。 - avdgaag
@avdgaag git bisect run 告诉Git通过在命令必须返回的单词'run'之后运行命令来自动化二分 0 为一个 good 版本(见 git help bisect 详情)。该 '[ -e foo.bar ]' 是用于测试文件的标准表达式 foo.bar 确实存在(实现通常在文件中 /usr/bin/[ 这通常是硬连接的 /usr/bin/test并且单个的quation标记用于将all作为单个命令行参数。 - Mikko Rantalainen


我最喜欢的别名,基于 bonyiii回答 (upvoted),我自己的答案“将参数传递给Git别名命令“:

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

我丢失了一个文件,错误删除了一些提交之前?
快:

git restore my_deleted_file

危机避免了。


罗伯特戴利 建议 在评论中 以下别名:

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

jegan 增加 在评论中

为了从命令行设置别名,我使用了以下命令:

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 

61
2018-02-17 15:33



这将恢复整个提交,而不仅仅是请求的文件。 - Daniel Bang
这是我的别名,非常有效: restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1" - void.pointer
@RobertDailey看起来很棒!我已将您的别名包含在答案中以获得更多可见性。 - VonC
git:'restore'不是git命令。 - resultsway
为了从命令行设置别名,我使用了以下命令: git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" - jegan


如果您知道文件名,这是使用基本命令的简单方法:

列出该文件的所有提交。

git log -- path/to/file

最后一次提交(最顶层)是删除文件的提交。所以你需要恢复倒数第二次提交。

git checkout {second to last commit} -- path/to/file

42
2018-02-27 01:50



是。这对我来说是正确的解决方案!简单明了 - Fabrizio Bertoglio
刚刚使用此解决方案,并没有提交删除。我能够使用最新的提交ID恢复文件。 - Adam
+10为 second to last commit 澄清! - colminator
倒数第二次提交(先前提交删除)是否包含已删除文件的最新版本?倒数第二个(前一次提交删除之前的提交)可能已经过时了。 - Suncat2000
这是我见过的第一个解决方案,这个解决方案很简单,下次我不必回到这里找到它。也许。 - Eloff