当你 `git commit` 时会发生什么
如今,我们大多数项目都使用 Git 作为版本控制系统。这意味着我们大多数项目都有一个.git
文件夹。但是,你试过打开它吗?
我试过一次……不到一分钟就关闭了!
多年来我一直将 Git 用作“黑匣子”。
直到一年前,我厌倦了在不了解其底层工作原理的情况下使用它。最终,我抓住了机会,开始学习它。我阅读了《Pro Git》,并做了大量的实验。我发现它并没有看起来那么复杂!
因此,如果你:
- 就是上面图片里的那个人,
- 想要了解这个文件夹中的内容
.git
, - 已经“丢失了 git 代码”......
……这篇博文是写给你的!😃
步骤 1:Git 数据模型
为了使事情更容易理解,我将从一个新的基本项目开始(2 个文件:index.js
和README.md
)。
git init
echo "console.log('hi')" >> index.js
echo "# Cool project" >> README.md
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
Git 创建了 4 个文件。为了避免同一文件夹下出现数千个文件,git 会自动截断文件夹名称的前两个字符。要检索一个 git 对象,您必须将文件夹名称 + 文件名连接起来。
由于这些文件无法被人类读取,您可以使用git cat-file <sha-1> -p
查看文件内容(或使用-t
选项获取文件类型)。顺便说一下,您只能使用前 8 个字符。
这些文件是如何链接在一起的:
💡 Git 对象模型有 4 种不同类型:
- 提交:包含提交者、日期、消息以及目录树;
- tree:引用其他树和/或 blob;
- blob:存储文件数据;
- 标签:存储提交引用(本博文中未涉及)。
请注意,blob 不会存储其文件名(和位置)。这就是为什么有时 git 在更改文件位置时会丢失历史记录的原因之一 ;)
🤔如果您在本地尝试,预计会有不同的哈希值(不同的作者和日期)!
第 2 步:添加第二个提交!
现在我们要更新index.js
并向文件添加第二行:
echo "console.log('world')" >> index.js
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
我们现在有这样的内容:
👀 这里有一点有意思:Git 不会存储文件之间的差异!多亏了packfiles(在 中.git/objects/pack
),Git 在磁盘上保留了合理的位置。
第三步:玩弄时间,搞砸一切🤦♂️
在最后一步中,我们将添加一个提交。然后,我们将回到过去“删除此提交”。
echo "console.log('😃')" >> index.js
git add . && git commit -m "Third commit"
你可能已经猜到了,git 创建了 3 个新文件。其结构与步骤 2 类似。
.git/objects
├── 00
│ └── ee8c50f8d74eaf1d3a4160e9d9c9eb1c683132
├── 09
│ └── f760de83890e3c363a38e6dc0700b76e782bc1
├── cf
│ └── 81d6f570911938726cff95b62acbf198fd3510
└── ...
12 directories, 10 files
git reset HEAD~1 --hard
现在,我们假设我们想从 1 次提交( )回到过去。
……现在你觉得你把一切都搞砸了,你的提交永远消失了。对吗?
也许吧。让我们数一数有多少个 git 对象……
$ tree .git/objects
.git/objects
└── ...
12 directories, 10 files
瞧:我们还有 10 个文件!什么都没删除!猜猜怎么着?如果我git cat-file cf81d6f570911938726cff95b62acbf198fd3510 -p
,就能拿到第三次提交时 index.js 的内容。🎉
“使用 git,你不会丢失代码。”
——我
更严重的是,我每天都用git push --force
,git 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
(注意:你可以在 中获得类似的东西.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