发布于 2026-01-05 10 阅读
0

通过探索 .git 目录真正理解 Git。“哇,我刚读完这篇关于 Git 的快速教程,我的天哪,它太酷了!我现在用起来感觉超级舒服,而且一点也不怕搞砸。”—— 没人这么说过。DEV 全球展示挑战赛,由 Mux 呈现:展示你的项目!

通过探索 .git 目录来真正理解 Git。

“哇,我刚看完这篇关于 Git 的快速教程,我的天哪,它太酷了!我现在用起来感觉超级舒服,一点也不怕搞砸了。”—— 从来没有人这么说过。

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

“哇,我刚看完这篇关于 Git 的快速教程,我的天哪,它太酷了!我现在用起来感觉超级舒服,一点也不怕搞砸了。”—— 从来没有人这么说过。

对于初学者来说,使用 Git 就像一个不懂当地语言的人去一个陌生的国家旅行。一旦你知道自己身在何处,要去哪里,一切都很顺利;但一旦你迷路了,大麻烦就开始了(#糟糕的比喻)。

网上有很多关于学习 Git 基本命令的文章,但本文并非其中之一。我将尝试一种不同的方法。

[xkcd](https://xkcd.com/1597/)

XKCD

新用户通常对 Git 感到畏惧,这的确很难避免。它无疑是个强大的工具,但并不十分友好。它引入了许多新概念和命令,如果传递了文件参数,其功能就会完全不同,而且反馈信息也晦涩难懂……

我认为克服这些最初困难的一个方法是,除了执行 git commit/push 之外,还要做一些更深入的事情。我认为,如果我们花时间真正了解 git 的组成原理,它可以帮你避免很多麻烦。

进入 .git 目录

那么,我们开始吧。当你使用 `git init` 创建一个 Git 仓库时,Git 会创建这个神奇的目录:`.git`。这个文件夹包含了 Git 运行所需的所有信息。需要说明的是,如果你想从项目中移除 Git,但保留项目文件,只需删除 `.git` 文件夹即可。不过,你为什么要这么做呢?

    ├── HEAD
    ├── branches
    ├── config
    ├── description
    ├── hooks
    │ ├── pre-commit.sample
    │ ├── pre-push.sample
    │ └── ...
    ├── info
    │ └── exclude
    ├── objects
    │ ├── info
    │ └── pack
    └── refs
     ├── heads
     └── tags
Enter fullscreen mode Exit fullscreen mode

这是你第一次提交之前的 .git 文件内容:

我们稍后再谈这个问题。

  • 配置

此文件包含您的仓库设置,例如远程仓库的 URL、您的邮箱、用户名等。每次您在控制台中使用“git config …”命令时,都会在此处结束。

  • 描述

由 gitweb(GitHub 的前身)用于显示仓库描述。

  • 钩子

这里有一个很有意思的功能。Git 自带一组脚本,可以在每个重要的 Git 操作阶段自动运行。这些脚本被称为钩子(hooks),可以在提交(commit)、变基(rebase)、拉取(pull)等操作之前或之后运行。脚本的名称决定了它的执行时机。一个有用的推送前钩子示例是测试所有样式规则是否都得到遵守,以确保远程仓库(远程仓库)的一致性。

  • 信息 — 排除

所以你可以把不想让 Git 处理的文件放在 .gitignore 文件中。排除文件(exclude 文件)的作用类似,只是它不会被共享。例如,如果你不想跟踪自定义 IDE 相关的配置文件,虽然大多数情况下 .gitignore 文件就足够了(如果你真的用过这个文件,请在评论区告诉我)。

提交中包含什么内容?

每次创建文件并跟踪其更新时,Git 都会对其进行压缩并将其存储到自身的数据结构中。压缩后的对象将具有唯一的名称和哈希值,并存储在对象目录下。

在探索对象目录之前,我们首先需要了解什么是提交。提交可以看作是工作目录的一个快照,但它的作用远不止于此。

事实上,当你提交代码时,git 只做两件事来创建工作目录的快照:

  1. 如果文件没有更改,git 只会将压缩文件的名称(哈希值)添加到快照中。
  2. 如果文件已更改,git 会对其进行压缩,并将压缩后的文件存储在对象文件夹中。最后,它会将此压缩文件的名称(哈希值)添加到快照中。

这是简化版,整个过程比较复杂,将在以后的文章中详细介绍。

一旦创建了快照,它也会被压缩并以哈希值命名,所有这些压缩对象最终会放在哪里呢?在对象文件夹中。

├── 4c
│ └── f44f1e3fe4fb7f8aa42138c324f63f5ac85828 // hash
├── 86
│ └── 550c31847e518e1927f95991c949fc14efc711 // hash
├── e6
│ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 // hash
├── info // let's ignore that
└── pack // let's ignore that too
Enter fullscreen mode Exit fullscreen mode

这是我创建并提交一个空文件 file_1.txt 后对象目录的样子。请注意,如果你的文件哈希值为“4cf44f1e…”,git 会将该文件存储在名为“4c”的子目录中,并将文件名命名为“f44f1…”。这个小技巧可以将 /objects 目录的大小减少 255 个字节。

你看,这里有三个哈希值。一个是我的 file_1.txt 文件,另一个是我提交时创建的快照。第三个是什么呢?因为提交本身就是一个对象,所以它也会被压缩并存储在对象文件夹中。

你需要记住的是,一次提交由 4 部分组成:

  1. 工作目录快照的名称(哈希值)。

  2. 一条评论

  3. 提交者信息

  4. 父提交的哈希值

就是这样,你自己看看解压缩提交文件会发生什么:

// by looking at the history you can easily find your commit hash
// you also don't have to paste the whole hash, only enough 
// characters to make the hash unique

git cat-file -p 4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828
Enter fullscreen mode Exit fullscreen mode

这就是我得到的。

tree 86550c31847e518e1927f95991c949fc14efc711
author Pierre De Wulf <test[@gmail.com](mailto:pierredewulf31@gmail.com)> 1455775173 -0500
committer Pierre De Wulf <[test@gmail.com](mailto:pierredewulf31@gmail.com)> 1455775173 -0500

commit A
Enter fullscreen mode Exit fullscreen mode

你看,我们如预期那样得到了快照哈希值、作者以及我的提交信息。这里有两点很重要:

  1. 正如预期的那样,快照哈希“86550…”也是一个对象,可以在对象文件夹中找到。

  2. 因为这是我的第一次提交,所以没有父提交。

我的快照里到底有什么真实内容?

git cat-file -p 86550c31847e518e1927f95991c949fc14efc711

100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file_1.txt
Enter fullscreen mode Exit fullscreen mode

在这里,我们找到了之前存在于对象存储中的最后一个对象,也是快照中唯一的对象。它是一个blob对象,但这又是另一个故事了。

分支、标签、HEAD 都一样。

现在你应该明白,Git 中的所有内容都可以通过正确的哈希值访问。接下来我们来看看 HEAD。那么 HEAD 里有什么呢?

cat HEAD
ref: refs/heads/master
Enter fullscreen mode Exit fullscreen mode

好的,这不是哈希值,这是合理的,因为 HEAD 可以被视为指向你当前工作分支顶端的指针。现在,如果我们看一下 refs/heads/master 目录下的内容,我们会看到:

cat refs/heads/master
4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828
Enter fullscreen mode Exit fullscreen mode

这看起来眼熟吗?没错,这和我们第一次提交的哈希值一模一样。这说明分支和标签只不过是指向某个提交的指针。也就是说,你可以删除所有分支和标签,它们指向的提交仍然存在。只是访问起来会更加困难。如果你想了解更多,可以去看看Git 的官方文档

最后一件事

所以现在你应该明白,Git 在提交时所做的就是将当前工作目录“压缩”并连同其他信息一起存储到 objects 文件夹中。但如果你足够熟悉这个工具,你就会知道你可以完全控制哪些文件应该包含在提交中,哪些文件不应该包含在提交中。

我的意思是,提交操作实际上并不是工作目录的快照,而是你想提交的文件的快照。那么,在实际提交之前,Git 会把这些你想提交的文件存储在哪里呢?它把它们存储在索引文件中。我们现在就不深入探讨这个问题了,如果你真的很好奇,可以看看这个

感谢阅读:

希望这篇文章能让你学到一些有价值的东西,并让你在使用 Git 时更加轻松。

您可以在这里阅读第二部分

如果你喜欢JS,我刚刚发布了一些你可能会喜欢的东西:

请在评论区告诉我你最近遇到的 Git 困惑,别害羞哦🙂 如果你喜欢这篇文章,别忘了订阅我的新闻邮件,以后会有更多精彩内容(而且你还能免费获得我的下一本电子书的前几章😎)。

文章来源:https://dev.to/daolf/git-series-13-understanding-git-for-real-by-exploring-the-git-director--5bd0