【问题标题】:How can I get a file back after committing its deletion?提交删除后如何取回文件?
【发布时间】:2021-10-14 21:33:30
【问题描述】:

我已经删除了几个文件并在 Git 中的一个特性分支中提交了这个更改,但事实证明毕竟需要其中一个。由于该文件不存在,因此我无法轻松查看其历史记录并查看我执行了哪个提交等。

我知道文件的名称。我怎样才能只取回这一个文件?我确实有它的副本,但似乎从父分支恢复它或回滚删除对于版本历史记录会更好。

这是一个包含数十个提交的功能分支,它只是我想要返回的一个提交中的一个文件。我在几周前(以及许多次提交)进行了更改,直到今天才注意到这个错误。

(在网上看,我似乎发现很多人在询问如何在变基等之后恢复删除,但这是直截了当的“哎呀,我提交了文件删除”)

【问题讨论】:

  • 尝试git bisect 查找删除文件的提交。
  • 从未听说过@LasseV.Karlsen,谢谢

标签: git


【解决方案1】:
  • 使用git log --diff-filter=D --summary | grep delete获取完整文件路径
  • 使用git log --all --full-history -- "filepath"找到你上次提交的文件
  • 回到文件确实存在的提交并开始一个新的分支。
  • 将您当前的工作合并到该分支。
  • 将分支合并回您的工作分支。

看到这个答案https://stackoverflow.com/a/37622157/12361414

【讨论】:

  • 我不知道它是哪个提交,这是问题的一部分......我这几周前做了,今天才意识到它是必需的
  • 也许你可以使用git log --all --full-history -- "**/thefile.*"找到提交哈希
  • @FlorianEndrich,您还可以从提交中仅签出单个文件...
【解决方案2】:

每个提交都有一个每个文件的完整副本——或者更准确地说,它拥有的每个文件,但是这样说,听起来是多余的。这意味着如果您签出一些提交 C 并开始工作,您要做的一件事就是删除一些文件 path/to/file 并最终运行 git commit 以进行新的提交 @ 987654325@,commit D 拥有它拥有的所有文件,这意味着它省略 path/to/file。但是 path/to/file 仍保留在提交 C 中,因为一旦提交,任何地方的任何权力都无法更改提交。

这反过来意味着,如果文件 path/to/file 没有在提交 C 与一些较早的提交 BA ,您可以从这三个提交中的任何一个中获取文件path/to/file。所有三个提交中的文件只是一个副本(事实上,Git de-duplicated 它,尽管文件出现在所有三个提交中,但它只存储一次)。这背后的机制非常聪明和优雅,与你的目的完全无关。 ? 对您而言,重要的是您可以从任何具有您想要的副本的提交中获取文件。

记住这一点,如果最容易找到专门提交C,而不是提交AB,我们可以这样做——找到提交C。如果最容易找到提交 BA,并且您确定这两个提交中的副本同样好,请考虑使用提交 BA 来取回文件。

Florian Endrich showed in his answergit log --diff-filter=D --summary 的第一个命令是一种查找我在上面调用D 的提交的方法——您关心的文件在其中被删除,从而找到文件的完整路径名。如果您已经知道文件的路径,则只需使用 next 命令会更简单:

git log -n 1 -- path/to/file

这是一个简化版本:我们只需要获取提交D 的原始哈希ID。一旦我们有了那个,将提交命名为C 就很简单了,因为提交C 是提交D父提交。这是一个示例,稍微修剪一下,将一个 @ 更改为一个空格,以(也许)减少一点垃圾邮件:

git log -n 1 -- compat/gmtime.c
commit 84b0115f0dc9483dbc7f064b46afaddc4d94db92
Author: Carlo Marcelo Arenas Belón <carenas gmail.com>
Date:   Thu May 14 12:18:54 2020 -0700

    compat: remove gmtime
    
    ccd469450a (date.c: switch to reentrant {gm,local}time_r, 2019-11-28)
    removes the only gmtime() call we had and moves to gmtime_r() which
    doesn't have the same portability problems. ...

这个对 Git 存储库的特定提交会删除文件 compat/gmtime.c。因此,该文件存在于此提交的 parent 提交中,其形式为删除前的形式。提交84b0115f0dc9483dbc7f064b46afaddc4d94db92 的父提交是什么? Git 可以告诉我们:

$ git rev-parse 84b0115f0dc9483dbc7f064b46afaddc4d94db92^
7397ca33730626f682845f8691b39c305535611e

(注意rev-parse 参数末尾的插入符号^;如果你的命令行解释器喜欢啃胡萝卜,呃,插入符号,你可以使用~)。

因此84b0115f0d... 的父级是7397ca3373...,这意味着如果我们想要返回compat/gmtime.c,我们可以简单地让Git 查看或提取该文件的版本,因为它出现在提交7397ca3373...

$ git show 7397ca3373:compat/gmtime.c
#include "../git-compat-util.h"
#undef gmtime
#undef gmtime_r
[snipped]

我们实际上也不需要找到 C 的 hash ID,因为我们可以将 ^ 后缀添加到 D 的 hash ID 以表示“提交 C” .所以git show 84b0115f0d^:compat/gmtime.c 也可以。

鉴于您想要文件返回,您想要使用的命令是 Git 2.23 中的新 git restore

git restore --source=<commit> --staged --worktree -- path/to/file

其中&lt;commit&gt; 部分是C 的正确哈希ID,或D 的哈希ID 后跟插入符号^ 或波浪号~ 字符,或其他。

如果您有一个易于使用、易于回忆的名称,用于提交AB先于 提交C,但是里面有正确的文件,你也可以在这里使用 that

git restore --source=dev --staged --worktree -- path/to/file

如果分支dev 上最后一次提交中的文件副本是一个好的副本。

如果你没有 Git 2.23 并且无法升级,你可以使用:

git checkout <commit> -- <path>

--source=... --staged --worktree 代替git restore。事实上,如果你确实有 Git 2.23 或更高版本,你可以仍然使用这种git checkout,它仍然有效。

总结

  1. 找到具有良好文件副本的提交。 任何 提交都可以:提交只保存文件的完整快照,因此 任何 包含正确快照的提交都是该文件副本的良好来源。

  2. 使用git restoregit checkout 从该提交中提取文件。使用--staged --worktree 告诉git restore 将文件解压缩到暂存区您的工作树,以便文件准备就绪。使用 git checkout 也总是这样做:例如,使用 git restore 您可以选择 not 以使文件已暂存,因此 git restore 更灵活,但代价是需要更多输入.

【讨论】:

    猜你喜欢
    • 2013-02-13
    • 2011-01-29
    • 2012-08-11
    • 2016-07-19
    • 2023-04-04
    • 2021-09-23
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多