当你敲完 git commit 命令后,究竟发生了什么?

作者:Maxence Poutord
来源:dev.to
翻译:大道至简

如今大多数项目都使用 Git 作为版本控制系统,这意味着大多数项目都有一个.git文件夹。但是,你有没有尝试过打开它?

我试过一次……然后在一分钟内就关掉了!

长期以来,我都是把 Git 当做“黑盒”来用的。

“这是GIT。它通过一个漂亮的分布式图论模型跟踪项目中的协作工作。”
“哇,好酷!怎么用的呢?”
“母鸡。只需记住这些 shell 命令并输入它们来同步。如果出现错误,那就把代码保存到其他地方,删掉项目,再下载一份新的。”

直到一年前。我对这种知其然、不知其所以然的做法感到厌倦。最后我找到了机会,开始学习它。我看了《Pro Git》这本书,并进行了大量实验。我发现它并不像看起来那么复杂!

因此,如果你:

  • 跟上面那张图中的人一样

  • 想了解.git 文件夹里有什么

  • 曾经 "在 git 里弄丢过代码"……

这篇文章就是为你写的!

1Git 数据模型

为了便于理解,我会从一个基础项目开始(有2个文件: index.js and README.md)。

  1. git init

  2. echo "console.log('hi')" >> index.js

  3. echo "# Cool project" >> README.md

  4. git add . && git commit -m "First commit"

现在我们来看看.git 文件夹里的内容:

$ tree .git/objects

.git/objects
├── 2d
│   └── 1a41ebd2d32cb426b6d32e61e063f330aa1af8
├── 66
│   └── 87898e2a0fe2da282efab6b5f7f38b7f788f56
├── c2
│   └── 43f24fb294ebc954b0a7ee6020589245f78315
├── c3
│   └── 0500babf488d06033e8d039cbf64be3edbd089
├── info
└── pack

6 个目录,4 个文件

Git 创建了4个文件。为了避免一个文件夹包含太多文件,git 会自动截取前两个字符作为文件夹名。要检索一个 git 对象,必须将文件夹名+文件名拼接起来。

由于这些文件不具备可读性,你可以用命令git cat-file <sha-1> -p 查看里面的内容(或者用 -t 参数查看类型)。顺便提一下,只能用前 8 个字符。

这些文件是这样互相链接的:

Git 对象模型有 4 种不同类型:

  • commit:包含提交人、日期、消息还有目录树

  • tree:引用其他 tree 和(或)blob

  • blob:存储文件数据

  • tag:存储某个提交的引用(本文未涉及)

请注意,blob 不存储文件名(和位置)。这就是为什么当你更改文件位置时 git 会丢失历史记录的原因之一。

????如果你在本机运行,可能会得到不同的 hash 值(作者和日期不一样)!

2再来一次提交

现在我们要更新 index.js ,给文件再添加一行:

  1. echo "console.log('world')" >> index.js

  2. git add . && git commit -m "Second commit"

于是又多了 3 个 对象:

$ tree .git/objects

.git/objects
├── 11
│   └── 75e42a41f75f4b25bab53db36d581f72387aa9
├── 2d
│   └── 1a41ebd2d32cb426b6d32e61e063f330aa1af8
├── 66
│   └── 87898e2a0fe2da282efab6b5f7f38b7f788f56
├── c2
│   └── 43f24fb294ebc954b0a7ee6020589245f78315
├── c3
│   └── 0500babf488d06033e8d039cbf64be3edbd089
├── ee
│   └── c2cd5e0b771793e03bbd5f8614c567af964a4e
├── fc
│   └── 512af17ca7ec04be6958047648b32629e4b5a5
├── info
└── pack

9 个目录,7 个文件

现在我们得到这样的结果:

这里有意思了:Git 并没有存储文件之间的差异!幸亏有packfiles (位于 .git/objects/pack),Git 在硬盘上保留了一个合理的位置。

3乱改一通

在最后一步,我们将添加一个提交。然后,我们将回到过去以“删除此提交”。

  1. echo "console.log('')" >> index.js

  2. git add . && git commit -m "Third commit"

你可能猜到了,git 创建了3个新文件。结构跟第二步的类似。

.git/objects
├── 00
│   └── ee8c50f8d74eaf1d3a4160e9d9c9eb1c683132
├── 09
│   └── f760de83890e3c363a38e6dc0700b76e782bc1
├── cf
│   └── 81d6f570911938726cff95b62acbf198fd3510
└── ...

12 个目录, 10 个文件

现在,我们假装想回退一个提交(git reset HEAD~1 --hard)。

现在你可能会认为,你搞砸了一切,提交记录再也找不到了。是不是?

可能是。让我们看看还有多少个 git 对象……

$ tree .git/objects

.git/objects
└── ...

12 个目录, 10 个文件

看到没,我们还有10个文件!没有被删!你猜怎么着?如果我用命令git cat-file cf81d6f570911938726cff95b62acbf198fd3510 -p,我还能查看第三次提交的 index.js 文件内容。

"在 git 里你不可能丢失代码。"

——鲁迅

比这更严重的是,我每天使用git push --forcegit rebasegit reset --hard,但我从来没有丢失任何东西。但是,我们是人类,人类是容易犯错的。

别担心,如果你想回滚,不用丢弃所有文件。接下来就是见证奇迹的时刻!

神奇的 reflog

如果你尝试使用git log检索历史记录,则不会看到“Third commit” 这个提交。但是,如果加了参数-g(代表 --walk-reflogs),就会看到第三个提交。

为了让结果更好看,你可以用 git log -g --abbrev-commit --pretty=oneline

这个超有用的命令有个别名:git reflog ❤️

$ git reflog

eec2cd5 (HEAD -> master) HEAD@{0}: reset: moving to HEAD~1
00ee8c5 HEAD@{1}: commit: Third commit
eec2cd5 (HEAD -> master) HEAD@{2}: commit: Second commit
c30500b HEAD@{3}: commit (initial): First commit

(注意:在 .git/logs/HEAD里可以看到类似的结果)

现在,你有了第三个提交的指纹: 00ee8c5。你可以用 git reset 00ee8c5 --hard 来撤销之前的重置。

注意事项:

在某些情况下,git reflog 对你没有帮助:

  • 当你拉取别人的代码

  • 当你删除仓库并重新克隆

  • 当你查找超过90天前的改动(被git gc清理掉了 )。我不知道你们的情况,反正我连一个月前做了啥都不记得了。所以记录保存3个月应该足够了。

另外,如果你像 ctrl + s一样使用 git commit,就可能很容易犯迷糊。很抱歉,除了建议你阅读我的那篇有关 conventional commits 的文章外,我也无能为力。我认为这是使用 git 最干净的方法。

总结

  • 有4种不同类型的 git 对象:committreeblob 和tag

  • blob 并不存储文件名(这就是为什么移动文件后会丢失历史)

  • Git 不存储文件差异

  • 提交后的代码不会丢失。git reflog 能帮到你。

今天就到这里,下课!


关注【编程微刊】公众号,

回复【领取资源】,500G编程学习资源干货免费送。

  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
程序员的必经之路! 【限时优惠】 现在下单还享四重好礼: 1、教学课件免费下载 2、课程案例代码免费下载 3、专属VIP学员群免费答疑 4、下单还送800元编程大礼包 【超实用课程内容】  根据《2019-2020年中国开发者调查报告》显示超83%的开发者都在使用MySQL数据库。使用量大同时掌握MySQL早已是运维、DBA的必备技能甚至部分IT开发岗位也要求对数据库使用和原理有深入的解和掌握。 学习编程可能会犹豫选择 C++ 还是 Java;入门数据科学可能会纠结于选择 Python 还是 R;但无论如何 MySQL 都是 IT 从业人员不可或缺的技能!   套餐中一共包含2门MySQL数据库必学的核心课程(共98课时)   课程1:《MySQL数据库从入门到实战应用》   课程2:《高性能MySQL实战课》   【哪些人适合学习这门课程】  1)平时只接触语言基础并未学习任何数据库知识的人;  2)对MySQL掌握程度薄弱的人课程可以让更好发挥MySQL最佳性能; 3)想修炼更好的MySQL内功工作中遇到高并发场景可以游刃有余; 4)被面试官打破沙锅问到底的问题问到怀疑人生的应聘者。 【课程主要讲哪些内容】 课程一:《MySQL数据库从入门到实战应用》 主要从基础篇SQL语言篇、MySQL进阶篇三个角度展开讲解帮助大家更加高效的管理MySQL数据库。 课程二:《高性能MySQL实战课》主要从高可用篇、MySQL8.0新特性篇性能优化篇面试篇四个角度展开讲解帮助大家发挥MySQL的最佳性能的优化方法掌握如何处理海量业务数据和高并发请求 【能收获到什么】  1.基础再提高针对MySQL核心知识点学透用对; 2.能力再提高日常工作中的代码换新貌不怕问题; 3.面试再加分巴不得面试官打破沙锅问到底竞争力MAX。 【课程如何观看】  1、登录CSDN学院 APP 在我的课程中进行学习; 2、移动端:CSDN 学院APP(注意不是CSDN APP哦)  本课程为录播课课程永久有效观看时长 【资料开放】 课件、课程案例代码全开放给可以根据所学知识自行修改、优化。  下载方式:电脑登录课程观看页面点击右侧课件可进行课程资料的打包下载。
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值