Git 焚决!一个绝招助你找回丢失的代码文件!

众所周知,代码只要提交到 git 仓库就有记录。哪怕是被覆盖或者删除了,都可以从仓库进行还原

但是如果代码没有提交,却不小心被搞丢了,还有办法找回吗?

答案是有的,我已经踩过坑并整理到文章中了

希望大家永远不会遇到这种糟心事

一旦碰上了,那滋味简直...不堪回首

你也不想代码被不小心搞丢后从头再写一遍吧?

赶紧收藏转发,保不齐哪一天就能用上呢

也可以提前学习,养成良好习惯,避免文件找不回来,有备无患!

1. 准备工作

我在项目中新增了一个"歌词本"文件夹,存了 50 首爱听的歌

因为不是所有的情况都能找回丢失的文件,只能找回已暂存(git add)过的文件

所以下面我将模拟 4 种场景进行测试,测试哪一种场景才能找回丢失的文件

2. 场景测试

2.0 测试说明

接下来我会在 4 种不同的场景下分别运行这个 git bash 命令

bash 复制代码
git fsck --lost-found

注意:命令皆在项目根目录(与.git 文件夹同级)下执行

预期:如果根目录(与.git 文件夹同级)下生成了 lost-found 文件夹即说明文件恢复成功

2.1 场景-1:未暂存 -> 运行 git fsck --lost-found 命令

现在是未暂存状态

运行命令

bash 复制代码
git fsck --lost-found

命令执行成功,但是却没有生成 lost-found 文件夹

2.2 场景-2:未暂存 -> 删除文件 -> 运行 git fsck --lost-found 命令

为了节省文章空间,这里就省略截图了

重新运行命令

也没有生成 lost-found 文件夹

2.3 场景-3:已暂存 -> 运行 git fsck --lost-found 命令

现在我把"歌词本"文件夹暂存(git add .)

再次运行命令

还是没有生成 lost-found 文件夹

2.4 场景-4:已暂存 -> 删除文件 -> 运行 git fsck --lost-found 命令

节省文章空间,截图就省略了

重新运行命令

也没有生成 lost-found 文件夹

2.5 场景-5:已暂存 -> 删除文件 -> 再次暂存 -> 运行 git fsck --lost-found 命令

然后我把"歌词本"文件夹整个给丢弃了,再次暂存(git add .)

最后运行命令

bash 复制代码
git fsck --lost-found

这次成功了!!!

.git 文件夹下面生成了 lost-found 文件夹,打开后里面有 other 文件夹, other 里面就是曾经暂存过的所有文件列表

可以看到正好 50 个项目

这些文件是以其对象的 SHA 哈希值命名的,但是文件内容就是原始内容(如果是文本文件,就可以直接打开查看)

用 vscode 打开整个 lost-found 文件夹,可以看到,里面全部都是刚才被我故意丢弃的歌词文件

在实际项目中,我们还可以再结合 vscode 的时间线功能,就可以把丢失的代码文件恢复的八九不离十了;

随堂笔记:不是 git add 之后,文件就会到.git/lost-found/other 目录中,而是当内容删除后才存在!!

3. 找回前提:文件 add 到暂存区过!

现在来解释,为啥只有 场景-5:已暂存 -> 删除文件 -> 再次暂存 -> 运行 git fsck --lost-found 命令 才能找回丢失文件的原因

执行 git add . 命令的时候,Git 会把 歌词本里面的 50 个.txt 文件内容存成 50 个 blob 对象,写入 .git/objects/ 文件夹

但是只有"悬挂"(dangling)-无引用,但存在的对象能通过 fsck 恢复到 .git/lost-found/ 目录中

未被 git 记录的文件,和 git 中正常存在(有引用关系)的文件是不会出现在 .git/objects/ 目录中,这就是为什么只有添加到暂存区后主动删除后才能生成到 .git/lost-found/ 文件夹的原因

只要你把文件暂存到 git 中,git 就会记住它,哪怕你之后 git reset,git rebase,cherry-pick,或者提交拉取覆盖,切换分支甚至删除文件。只要这个 blob 还没被 git gc 清理,它就能被 git fsck --lost-found 找出来

git fsck --lost-found 本质是恢复 Git 管理过的数据,对于没有被 git add 过的文件, Git 从来就不知道这个文件存在过,自然也就找不到了

4. git fsck --lost-found 命令解释

git fsck --lost-found 是 Git 内置命令,用于检查 Git 仓库的对象数据库(object database),并将任何"悬挂"(dangling)或"丢失"的对象恢复到 .git/lost-found/ 目录中。这是一个诊断和恢复工具,常用于修复损坏的仓库或找回意外丢失的数据。

  • git fsck:这是核心命令,用于验证 Git 仓库中对象的完整性。它会检查所有对象(如提交、树、blob 和标签)的连通性、有效性和 SHA-1 校验和。如果发现问题(如损坏的对象或无法访问的引用),它会报告错误。

  • --lost-found 选项:这个选项扩展了 fsck 的行为,不仅检查问题,还会将那些"悬挂"的对象(即存在于对象数据库中,但没有被任何引用如分支、标签或 HEAD 指向的对象)复制到 .git/lost-found/ 目录下。具体来说:

    悬挂的提交(commits)会放在 .git/lost-found/commit/ 目录中,每个文件名为其 SHA-1 值,是一个包含提交元数据的文本文件。

    其他悬挂对象(如树、blob 或标签)会放在 .git/lost-found/other/ 目录中,同样以 SHA-1 命名。

这些悬挂对象通常是因为操作如 git rebase、git reset 或手动删除引用而产生的。它们不是仓库损坏的标志,而是 Git 的正常行为(Git 默认保留这些对象一段时间,以防需要恢复)。

5. 通过关键词快速搜索

我想在一大堆文件中找到想要的文件怎么办?

直接 vscode 打开整个 .git/lost-found/other 目录,然后全局搜索关键词即可

我本来还研究了半天的搜索命令,最后发现还不如在 vscode 中全局搜索方便😓

6. 搜索进阶!查找最近修改的 15 个文件

不过新的问题也随之出现,文件太多怎么办?如果我 .git/lost-found/other 文件夹下面有几千个文件,我想找回最近被误删的文件,难道要挨个打开查看文件内容吗?

好问题,我也给你想到了解决方法。继续往下看。

前面的图片中显示 .git/lost-found/other 文件夹很干净,只有被我手动丢弃的 50 个歌词文件,是因为我之前清理了 git 的缓存,其实在我清理缓存之前 .git/lost-found/other 文件夹是存在两千多个文件的,如下图所示

要想在这两千多个文件中找到我丢失的那 50 个歌词文件,工作量可想而知!

毕竟不是所有的文件我都记得住,通过关键词查找回来

别急,我有好办法!

我现在新增 15 个不同格式的文件进行测试

然后把文件暂存后删除,测试能否精确找回这 15 个文件

注意!需要重新运行 git fsck --lost-found 命令才能更新.git/lost-found/other 文件夹哦!否则会搜出来旧的数据

还是在项目根目录(与.git 文件夹同级)下运行命令

bash 复制代码
#!/bin/bash
mkdir -p recent_lost
> temp_times.txt  # 先创建空文件,避免后续错误

for lost_file in .git/lost-found/other/*; do
    if [ ! -f "$lost_file" ]; then  # 如果没有文件,跳过(处理空目录)
        continue
    fi
    sha=$(basename "$lost_file")
    obj_dir=".git/objects/${sha:0:2}"
    obj_file="$obj_dir/${sha:2}"
    if [ -f "$obj_file" ]; then
        mtime=$(stat -c %Y "$obj_file" 2>/dev/null)  # 获取 Unix 时间戳,忽略错误
        if [ -n "$mtime" ]; then
            echo "$mtime $sha" >> temp_times.txt
        fi
    fi
done

if [ -s temp_times.txt ]; then  # 只在文件非空时排序
    sort -nr temp_times.txt | head -n 15 | while read mtime sha; do
        cp ".git/lost-found/other/$sha" "recent_lost/$sha"
        echo "Copied recent object: $sha (mtime: $(date -d @$mtime))"
    done
else
    echo "No recent objects found or no mtime available. This may happen if objects are packed (not loose) or no matching files in .git/objects/."
fi

rm -f temp_times.txt  # 用 -f 忽略如果文件不存在

成功提取出最近修改的 15 个文件,并将其存入自动创建的 recent_lost/ 文件夹中

用 vscode 打开项目,会发现除了 js,html,vue,txt 等文本文件打开后可以直观的看见内容外,其他的格式根本看不了一点,那咋搞?别急,请看下文-判断文件类型

解释:脚本遍历 other/ 中的 SHA,查找对应 .git/objects/ 中的文件,获取 mtime,按时间降序复制前 N 个到新目录 recent_lost/。这样你只需检查少数文件。

tip:如果想调整找回的数量,自己把 15 这个数字更改即可

7. 如何判断文件类型?

运行下面这个循环脚本能批量检查文件类型

bash 复制代码
for file in recent_lost/*; do
    file_type=$(file "$file")
    echo "$file: $file_type"
done

如图所示,html,json,js,excel,jpg,mp4,pdf,dwg 等格式都能正确判断出来

然后挨个复制哈希值,在 recent_lost 文件搜索文件:按组合键 chrl + p -> 粘贴哈希值

在 vscode 中找到这个 excel 的哈希值对应的文件,然后鼠标右键,在文件资源管理器中显示

然后手动更改文件后缀为对应的文件类型,即可还原内容啦!

其他的格式我就不挨个还原啦,步骤都跟还原 excel 一样的

8. git 瘦身

  • 强制垃圾回收并立即 prune:

    bash 复制代码
    git gc --aggressive --prune=now

    运行这个命令后,删除 lost-found 文件夹,然后新增文件暂存删除暂存后重新 git fsck --lost-found,就会发现,以前的被清理干净了,只剩下了刚才新增删除的文件

  • 压缩 .git/objects 文件夹

    bash 复制代码
    git repack -a -d -f --depth=50 --window=50

    命令的作用是删除冗余的旧 pack 文件和松散对象,释放空间;命令只影响存储格式,不修改仓库历史、提交或文件内容。SHA 值不变,仓库功能完整。

    可是我运行后发现并没啥用,估计是我这个仓库有好几年的历史了,长期累积导致体积变大,压无可压了

  • 重新 clone 项目

    如果你不嫌麻烦的话,重新 git clone git 仓库也是可以的,不过我已经实践了一遍,重新拉取代码跟运行 git gc --aggressive --prune=now 命令并无区别,.git/objects 文件夹依然是同样的大小

    记得提前将 .git/config 文件备份,clone 后再覆盖哦

总结

  • 如果没有暂存过,就无法通过 git 命令找回。不过可以试试编辑器的历史记录功能,这里用 vscode 举例

    操作步骤:同时按住 ctrl + shift + p 调用快捷键 -> 输入并打开 本地历史记录:查找要还原的条目 -> 就可以看见本地历史文件列表了

  • 如果是用 Socurcetree 提交代码的时候,报错然后丢失了文件,或许去贮藏菜单下面可以找到代码(我今天就被 Socurcetree 给坑了,报错后自己给我丢到贮藏去了)

  • 只能还原丢失的文件内容,无法还原文件名,不过我想能找回内容也够用了

  • 养成良好习惯,随时 git add . 呀!

相关推荐
Electrolux几秒前
[wllama]纯前端实现大语言模型调用:在浏览器里跑 AI 是什么体验。以调用腾讯 HY-MT1.5 混元翻译模型为例
前端·aigc·ai编程
sanra1234 分钟前
前端定位相关技巧
前端·vue
起名时在学Aiifox7 分钟前
从零实现前端数据格式化工具:以船员经验数据展示为例
前端·vue.js·typescript·es6
ASEpochs18 分钟前
Vsocde中‘sh’不是内部或外部命令,也不是可运行的程序或批量处理文件--已解决
git·vscode·bash
oMcLin25 分钟前
如何在Manjaro Linux上配置并优化Caddy Web服务器,确保高并发流量下的稳定性与安全性?
linux·服务器·前端
码途潇潇35 分钟前
JavaScript 中 ==、===、Object.is 以及 null、undefined、undeclared 的区别
前端·javascript
之恒君38 分钟前
Node.js 模块加载 - 4 - CJS 和 ESM 互操作避坑清单
前端·node.js
coding消烦员1 小时前
在 Windows 内网搭建 Git 仓库:共享普通仓库 vs 中心 bare 仓库
windows·git
be or not to be1 小时前
CSS 背景(background)系列属性
前端·css·css3
前端snow1 小时前
在手机端做个滚动效果
前端