0%

Git底层剖析

Git 底层剖析:深入理解 Git 的数据存储机制

Git 之所以高效且灵活,核心在于其独特的底层数据模型。不同于传统的版本控制系统(如 SVN)以 “差异” 存储版本,Git 以 “快照” 形式保存完整数据,并通过哈希值(SHA-1)唯一标识每个对象。本文将深入解析 Git 底层的核心文件和数据结构,揭开 HEADindexobjectsrefs 的神秘面纱。

Git 底层核心组件

Git 的所有版本数据和元信息都存储在仓库根目录的 .git 文件夹中,其中四个核心组件构成了其底层基础:

组件 作用
HEAD 特殊指针,指向当前检出的分支(或直接指向某个提交)。
index 暂存区(Staging Area)的快照,记录待提交文件的元数据(哈希、权限等)。
objects/ 存储所有版本数据(提交对象、树对象、数据对象)。
refs/ 存储分支、标签等引用的指针(指向对应的提交对象)。

objects 目录:Git 的 “数据库”

objects/ 是 Git 最核心的目录,所有版本数据都以 “对象” 形式存储于此。每个对象通过 SHA-1 哈希值 命名(40 位十六进制字符),前 2 位为目录名,后 38 位为文件名(如哈希 a1b2c3... 对应 objects/a1/b2c3...)。

Git 有三种基础对象类型,共同构成版本快照:

1. Blob 对象:存储文件内容

  • 作用:保存单个文件的原始内容(如代码、文本),与文件名无关(相同内容的文件共享同一个 blob)。
  • 特点:仅包含文件数据,不包含文件名、权限等元信息。
示例:创建并查看 blob 对象
1
2
3
4
5
6
7
8
9
10
# 1. 向文件写入内容
echo "hello git" > test.txt

# 2. 手动创建 blob 对象(Git 内部通过 hash-object 命令生成)
git hash-object -w test.txt
# 输出:d670460b4b4aece5915caf5c68d12f560a9fe3e4 (该文件的 blob 哈希)

# 3. 查看 blob 对象类型和内容
git cat-file -t d670460 # 输出:blob(确认类型)
git cat-file -p d670460 # 输出:hello git(查看内容)

2. Tree 对象:记录目录结构

  • 作用

    :类似文件夹,记录一个目录下的文件和子目录信息,包括:

    • 子对象的哈希值(blob 或 tree);
    • 文件名或目录名;
    • 文件权限(如 100644 表示普通文件,040000 表示目录)。
示例:创建并查看 tree 对象
1
2
3
4
5
6
7
8
9
10
11
# 1. 将 test.txt 加入暂存区(生成 index 快照)
git add test.txt

# 2. 从暂存区创建 tree 对象(记录当前目录结构)
git write-tree
# 输出:33c76843f743d6b5f4512d04f1edeb63f792da20 (tree 哈希)

# 3. 查看 tree 对象内容
git cat-file -p 33c7684
# 输出:100644 blob d670460b4b4aece5915caf5c68d12f560a9fe3e4 test.txt
# 含义:权限 类型 哈希值 文件名

3. Commit 对象:记录提交元数据

  • 作用:标记一次完整的版本提交,包含以下关键信息:
    • 对应的 tree 对象哈希(当前版本的目录结构);
    • 父 commit 对象哈希(上一个版本,首次提交无父节点);
    • 提交者信息(姓名、邮箱、时间戳);
    • 提交说明(commit message)。
示例:创建并查看 commit 对象
1
2
3
4
5
6
7
8
9
10
11
12
# 1. 手动创建 commit 对象(基于上述 tree 对象)
echo "first commit" | git commit-tree 33c7684
# 输出:f642db0c7115a403f2f7d45654f8e2b04f57388a (commit 哈希)

# 2. 查看 commit 对象内容
git cat-file -p f642db0
# 输出:
tree 33c76843f743d6b5f4512d04f1edeb63f792da20
author Your Name <your@email.com> 1620000000 +0800
committer Your Name <your@email.com> 1620000000 +0800

first commit

4. 对象之间的关系

三种对象形成层级关系,共同构成版本快照:

1
2
3
4
5
6
7
8
9
commit 对象
↓(指向)
tree 对象(根目录)
↓(包含)
├─ blob 对象(文件1内容)
├─ blob 对象(文件2内容)
└─ tree 对象(子目录)
↓(包含)
└─ blob 对象(子文件内容)

例如,一个包含 src/main.jsREADME.md 的项目,其对象关系为:

  • 1 个 commit 对象 → 1 个根 tree 对象 → 2 个 blob 对象(main.jsREADME.md)。

refs 目录:分支与标签的本质

refs/ 目录存储 “引用”(指针),用于指向具体的 commit 对象,简化版本操作(避免直接使用冗长的哈希值)。

1. 核心子目录

  • refs/heads/:存储本地分支,每个文件对应一个分支,内容为该分支最新 commit 的哈希。
    示例:refs/heads/master 内容为 f642db0...(master 分支的最新提交)。
  • refs/remotes/:存储远程分支,如 refs/remotes/origin/master 指向远程仓库 originmaster 分支最新提交。
  • refs/tags/:存储标签,轻量标签直接指向 commit 哈希,注解标签指向标签对象(包含标签元信息)。

2. 分支的本质

分支本质上是一个 “移动的指针”,每次提交时,当前分支指针会自动指向新的 commit 对象。例如:

1
2
3
4
5
6
7
8
# 查看当前分支指向的 commit
cat .git/refs/heads/master # 输出:f642db0...

# 创建新提交后,分支指针自动更新
echo "new line" >> test.txt
git add test.txt
git commit -m "second commit"
cat .git/refs/heads/master # 输出:新的 commit 哈希

HEAD:当前版本的指针

HEAD 是一个特殊文件,用于标记当前工作的版本(或分支):

  • 指向分支:大多数情况下,HEAD 指向某个分支(如 refs/heads/master),表示当前在该分支上工作。
    示例:cat .git/HEAD 输出 ref: refs/heads/master
  • 分离 HEAD:若直接检出某个 commit(如 git checkout f642db0),HEAD 会直接指向该 commit 的哈希,处于 “分离 HEAD” 状态。
    示例:cat .git/HEAD 输出 f642db0...

index:暂存区的快照

index 文件是暂存区的核心,记录待提交文件的元数据,包括:

  • 文件的 blob 哈希值;
  • 文件名;
  • 权限;
  • 时间戳。

可以将 index 理解为 “待提交的 tree 对象草稿”,执行 git commit 时,Git 会根据 index 生成新的 tree 对象,并关联到 commit 对象。

查看暂存区内容:

1
2
3
4
# 以原始格式查看暂存区
git ls-files --stage
# 输出示例:100644 d670460... 0 test.txt
# 含义:权限 blob哈希 阶段 文件名

底层命令与日常命令的对应关系

日常使用的 Git 命令本质上是对底层对象的封装:

日常命令 底层操作(简化)
git add test.txt 生成 test.txt 的 blob 对象,更新 index 文件。
git commit 基于 index 生成 tree 对象,再生成 commit 对象,更新当前分支指针。
git branch dev 创建 refs/heads/dev 文件,内容为当前 commit 哈希。
git tag v1.0 创建 refs/tags/v1.0 文件,内容为当前 commit 哈希。

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10