当你 `git commit` 时会发生什么

2025-05-25

当你 `git commit` 时会发生什么

如今,我们大多数项目都使用 Git 作为版本控制系统。这意味着我们大多数项目都有一个.git文件夹。但是,你试过打开它吗?
我试过一次……不到一分钟就关闭了!

多年来我一直将 Git 用作“黑匣子”。

git 解释

直到一年前,我厌倦了在不了解其底层工作原理的情况下使用它。最终,我抓住了机会,开始学习它。我阅读了《Pro Git》,并做了大量的实验。我发现它并没有看起来那么复杂!

因此,如果你:

  • 就是上面图片里的那个人,
  • 想要了解这个文件夹中的内容.git
  • 已经“丢失了 git 代码”......

……这篇博文是写给你的!😃

步骤 1:Git 数据模型

为了使事情更容易理解,我将从一个新的基本项目开始(2 个文件:index.jsREADME.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 directories, 4 files
Enter fullscreen mode Exit fullscreen mode

Git 创建了 4 个文件。为了避免同一文件夹下出现数千个文件,git 会自动截断文件夹名称的前两个字符。要检索一个 git 对象,您必须将文件夹名称 + 文件名连接起来。

由于这些文件无法被人类读取,您可以使用git cat-file <sha-1> -p查看文件内容(或使用-t选项获取文件类型)。顺便说一下,您只能使用前 8 个字符。

这些文件是如何链接在一起的:

git数据模型

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

  • 提交:包含提交者、日期、消息以及目录树;
  • tree:引用其他树和/或 blob;
  • blob:存储文件数据;
  • 标签:存储提交引用(本博文中未涉及)。

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

🤔如果您在本地尝试,预计会有不同的哈希值(不同的作者和日期)!

第 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 directories, 7 files
Enter fullscreen mode Exit fullscreen mode

我们现在有这样的内容:

包含 2 次提交的 git 数据模型

👀 这里有一点有意思:Git 不会存储文件之间的差异!多亏了packfiles(在 中.git/objects/pack),Git 在磁盘上保留了合理的位置。

第三步:玩弄时间,搞砸一切🤦‍♂️

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

  1. echo "console.log('😃')" >> index.js
  2. git add . && git commit -m "Third commit"

你可能已经猜到了,git 创建了 3 个新文件。其结构与步骤 2 类似。

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

12 directories, 10 files
Enter fullscreen mode Exit fullscreen mode

git reset HEAD~1 --hard现在,我们假设我们想从 1 次提交( )回到过去。

git reset --hard

……现在你觉得你把一切都搞砸了,你的提交永远消失了。对吗?
也许吧。让我们数一数有多少个 git 对象……

$ tree .git/objects

.git/objects
└── ...

12 directories, 10 files
Enter fullscreen mode Exit fullscreen mode

瞧:我们还有 10 个文件!什么都没删除!猜猜怎么着?如果我git cat-file cf81d6f570911938726cff95b62acbf198fd3510 -p,就能拿到第三次提交时 index.js 的内容。🎉

“使用 git,你不会丢失代码。”

——我

更严重的是,我每天都用git push --forcegit rebase而且git reset --hard从未丢失过任何东西。但是,我们是人,人都会犯错。

别担心,如果你想回滚,你不必翻遍所有文件。这里有个小妙招!

reflog: 魔杖✨

如果你尝试使用 来检索历史记录git log,你将看不到“第三次提交”。但是,如果你添加附加选项-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
Enter fullscreen mode Exit fullscreen mode

(注意:你可以在 中获得类似的东西.git/logs/HEAD

现在,您有了第三次提交的指纹:00ee8c5。您现在可以git reset 00ee8c5 --hard取消之前的重置。

注意事项:

在某些情况下,git reflog 不会帮助你:

  • 当你拉取别人的代码时;
  • 当你删除你的 repo 并再次克隆它时;
  • 如果你想要90天以上的改变(之后再git gc清理)。我不知道你的情况,但我记不清大约一个月前我做了什么。所以,3个月应该足够了😉

另外,如果你像我一样使用 git commit 的话,你可能会很容易迷失方向。很抱歉,除了推荐你阅读我关于常规提交的ctrl + s文章之外,我帮不了你什么。在我看来,这是使用 git 最简洁的方法。

总结一下~TL;DR

  • git 对象有 4 种不同类型:提交、树、blob 和标签。
  • 斑点不知道它们的名字(这就是为什么在移动过程中丢失历史记录的原因)。
  • Git 不存储差异。
  • 提交的代码不会丢失。git reflog可以帮到你。

今天就到这里!


感谢您花时间阅读这篇文章。希望它对您有所帮助!如果您喜欢,请点个❤️或🦄!也欢迎在下面的评论区留言或提问 :)


最初发表于maxpou.fr

文章来源:https://dev.to/maxpou/what-s-happens-when-you-git-commit-59n7
PREV
成为一名极度缺乏安全感的程序员
NEXT
Git:备忘单(高级)