news 2026/3/22 10:43:02

Git 原理解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Git 原理解析

Git 原理解析

目录

  • 概述
  • Git 的核心设计
    • 内容寻址的文件系统
    • 键值对数据库
  • Git 的四种核心对象
    • Blob:文件内容
    • Tree:目录结构
    • Commit:版本快照
    • Tag:里程碑标签
  • 对象关系图
  • 从 git add 到 git commit 的底层流程
  • .git 目录核心结构
  • 分支的本质
  • 引用和符号引用
  • Git 对象存储机制
  • 哈希算法与完整性
  • 实际示例演示
  • 总结

概述

Git 不仅仅是一个版本控制系统,它的核心是一个内容寻址的文件系统。理解 Git 的内部机制,能够帮助我们更好地使用 Git,并在遇到问题时知道如何解决。

核心特点

  • 内容寻址:通过内容计算哈希值,相同内容产生相同哈希
  • 不可变性:对象一旦创建,内容不可修改
  • 完整性:通过哈希值验证数据完整性
  • 高效性:相同内容只存储一份,通过引用共享

Git 的核心设计

内容寻址的文件系统

Git 的核心是一个键值对数据库 (key-value store),其工作流程如下:

┌─────────────────────────────────────┐ │ Git 内容寻址流程 │ ├─────────────────────────────────────┤ │ 1. 计算哈希 (SHA-1) │ │ 2. 存储内容 (.git/objects/) │ │ 3. 通过哈希寻址 │ └─────────────────────────────────────┘

工作流程

  1. 计算哈希:对一段内容(如文件、目录结构、提交信息)附加一个头部信息,计算出一个唯一的 SHA-1 哈希值(40位十六进制字符串)

  2. 存储内容:将内容压缩后,以其 SHA-1 值的前两位为目录名、后38位为文件名,存储在.git/objects/目录下

  3. 通过哈希寻址:之后,Git 便通过这个 SHA-1 哈希值来读取或引用该内容

存储结构

.git/objects/ ├── ab/ │ └── c123def456... (哈希值: abc123def456...) ├── cd/ │ └── e789f012... (哈希值: cde789f012...) └── ...

键值对数据库

Git 本质上是一个键值对数据库:

  • 键 (Key):SHA-1 哈希值
  • 值 (Value):对象内容(blob、tree、commit、tag)

特点

  • ✅ 相同内容产生相同哈希,只存储一份
  • ✅ 内容一旦存储,不可修改(不可变性)
  • ✅ 通过哈希值可以验证内容完整性
  • ✅ 高效的内容去重

Git 的四种核心对象

Git 的对象主要分为四类:blobtreecommittag。其中前三者构成了版本控制的基础。

Blob:文件内容

作用:仅存储文件内容,不包含文件名、权限等任何元数据。

特点

  • 内容相同,SHA-1 值就相同
  • Git 只存储一份副本(内容去重)
  • 不包含文件名信息

示例

# 将 "hello" 存入对象库,返回其 SHA-1 值echo'hello'|githash-object -w --stdin# 输出: ce013625030ba8dba906f756967f9e9ca394464a# 查看对象类型gitcat-file -t ce013625030ba8dba906f756967f9e9ca394464a# 输出: blob# 查看对象内容gitcat-file -p ce013625030ba8dba906f756967f9e9ca394464a# 输出: hello

存储位置

.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a

内容去重示例

# 创建两个内容相同的文件echo"hello">file1.txtecho"hello">file2.txt# 添加到 Gitgitaddfile1.txt file2.txt# 查看对象,发现只存储了一份gitcat-file --batch-check --batch-all-objects|grepblob# 两个文件指向同一个 blob 对象

Tree:目录结构

作用:代表项目中的目录,记录了该目录下所有文件和子目录的列表。

内容:每条记录包含:

  • 文件模式(如100644普通文件,100755可执行文件,040000目录)
  • 对象类型(blobtree
  • SHA-1 值
  • 文件名

示例:一个tree对象的内容可能如下:

100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README 100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile 040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib

这代表一个包含READMERakefile文件和lib子目录的目录。

查看 tree 对象

# 查看根目录的 treegitcat-file -p HEAD^{tree}# 查看指定 tree 对象gitcat-file -p<tree-hash># 查看 tree 的类型gitcat-file -t<tree-hash>

文件模式说明

模式说明示例
100644普通文件README.md
100755可执行文件script.sh
040000目录src/
120000符号链接link

Commit:版本快照

作用:代表项目在某个时间点的完整快照,并记录了提交元信息。

内容

  • 指向根目录tree对象的 SHA-1 值
  • 一个或多个父commit的 SHA-1 值(首次提交无父提交,合并提交有多个父提交)
  • 作者、提交者信息及时间戳
  • 提交信息(commit message)

示例:一个commit对象的内容可能如下:

tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 parent fdf4fc3344e67ab068f836878b6c4951e3b15f3d author Alice <alice@example.com> 1617187200 +0800 committer Alice <alice@example.com> 1617187200 +0800 First commit

查看 commit 对象

# 查看 commit 对象gitcat-file -p<commit-hash># 查看 commit 的类型gitcat-file -t<commit-hash># 查看最新提交gitcat-file -p HEAD

提交链

所有commit通过parent指针串联起来,形成一条可追溯的历史链:

C1 ← C2 ← C3 ← C4

合并提交

合并提交有多个父提交:

C2 ← C3 / \ C1 C4 (合并提交) \ / C5 ← C6

Tag:里程碑标签

作用:为某个特定的commit对象(或其他对象)创建一个永久的、人类可读的别名,通常用于标记发布版本(如v1.0.0)。

类型

  1. 轻量标签 (Lightweight Tag)

    • 直接指向commit的指针
    • 不包含额外信息
  2. 附注标签 (Annotated Tag)

    • 一个独立的tag对象
    • 包含打标签者信息、日期和消息

创建标签

# 创建轻量标签gittag v1.0.0# 创建附注标签gittag -a v1.0.0 -m"Release version 1.0.0"# 查看标签gitshow v1.0.0

查看 tag 对象

# 查看 tag 对象内容gitcat-file -p v1.0.0# 查看 tag 类型gitcat-file -t v1.0.0

对象关系图

一次git commit后,这几个对象的关系如下:

[Commit] | v [Tree] (根目录) / | \ v v v [Blob] ... [Tree] ... (子目录和文件) (文件内容)

关系说明

  1. Commit指向项目根目录tree
  2. Tree指向其包含的文件blob和子目录tree
  3. Blob仅存储文件内容

完整示例

Commit: abc123... | v Tree: def456... (根目录) | +-- 100644 blob ghi789... README.md | +-- 100644 blob jkl012... main.py | +-- 040000 tree mno345... src/ | +-- 100644 blob pqr678... utils.py

从 git add 到 git commit 的底层流程

1. git add:生成 Blob & 更新暂存区

流程

  1. 读取工作区文件内容
  2. 生成blob对象并存入.git/objects
  3. 更新暂存区 (Index),使其指向这个新生成的blob对象

暂存区 (Index)

  • 位置:.git/index
  • 格式:二进制文件
  • 内容:记录了下一次提交的内容快照
  • 作用:暂存待提交的文件

查看暂存区

# 查看暂存区内容gitls-files --stage# 查看暂存区的 treegitwrite-treegitcat-file -p$(gitwrite-tree)

2. git commit:生成 Tree & Commit

流程

  1. 生成 Tree:根据暂存区的内容,为当前目录及所有子目录生成新的tree对象

  2. 生成 Commit:创建一个commit对象,它指向根目录的tree,并指向上一个commit(形成历史链)

  3. 更新分支:将当前分支(如main)的引用更新为这个新commit的 SHA-1 值

详细步骤

# 1. 生成 tree 对象(基于暂存区)tree_hash=$(gitwrite-tree)# 2. 创建 commit 对象commit_hash=$(echo"First commit"|gitcommit-tree $tree_hash)# 3. 更新分支引用gitupdate-ref refs/heads/main$commit_hash

.git 目录核心结构

.git目录是 Git 仓库的核心,包含所有版本控制信息:

.git/ ├── objects/ # 对象存储目录 │ ├── ab/ # 哈希值前两位作为目录名 │ │ └── c123... # 哈希值后38位作为文件名 │ └── ... ├── refs/ # 引用目录 │ ├── heads/ # 分支引用 │ │ ├── main # main 分支指向的 commit │ │ └── dev # dev 分支指向的 commit │ └── tags/ # 标签引用 │ └── v1.0.0 # 标签指向的 commit ├── HEAD # 当前分支的符号引用 ├── index # 暂存区(二进制文件) ├── config # 仓库配置 ├── hooks/ # Git 钩子脚本 └── ...

objects/ 目录

存放所有blobtreecommittag对象。

存储方式

  • 使用 SHA-1 哈希值的前两位作为目录名
  • 后38位作为文件名
  • 内容经过压缩(zlib)

查看对象

# 列出所有对象find.git/objects -type f# 查看对象类型和内容gitcat-file -t<hash># 类型gitcat-file -p<hash># 内容gitcat-file -s<hash># 大小

refs/ 目录

存放各种引用(分支、标签等),如refs/heads/main指向main分支的最新commit

分支引用

.git/refs/heads/main 内容: abc123def456... (commit 的 SHA-1 值)

标签引用

.git/refs/tags/v1.0.0 内容: abc123def456... (commit 的 SHA-1 值)

查看引用

# 查看分支引用cat.git/refs/heads/main# 查看所有引用gitshow-ref# 查看分支指向的 commitgitrev-parse main

HEAD 文件

一个符号引用,指向当前检出的分支(如ref: refs/heads/main)。

查看 HEAD

# 查看 HEAD 内容cat.git/HEAD# 输出: ref: refs/heads/main# 查看 HEAD 指向的 commitgitrev-parse HEAD

分离 HEAD

当检出到某个具体的 commit 时,HEAD 直接指向 commit:

gitcheckout abc123cat.git/HEAD# 输出: abc123def456...

index 文件

二进制文件,即暂存区,记录了下一次提交的内容快照。

查看暂存区

# 查看暂存区内容gitls-files --stage# 查看暂存区的统计信息gitdiff--cached --stat

分支的本质

Git 的分支本质上是一个指向某个commit的轻量级指针文件

分支的存储

例如,refs/heads/main文件里存储的就是main分支最新commit的 SHA-1 值:

cat.git/refs/heads/main# 输出: abc123def456789...

分支操作的本质

新建分支

  • 创建一个新文件(如refs/heads/dev
  • 内容指向当前commit

提交

  • 在当前分支上提交时,Git 更新该分支文件
  • 使其指向新的commit

切换分支

  • HEAD文件的内容被修改,指向新的分支引用
  • 工作区文件随之更新

分支关系图

C1 ← C2 ← C3 ↑ ↑ main dev

分支的优势

由于commit对象之间通过parent指针相连,分支的创建、合并、切换都非常高效,因为它们只是在移动指针。

创建分支:只需创建一个文件,指向当前 commit(几乎瞬间完成)

切换分支:只需修改 HEAD 和工作区文件(非常快速)

合并分支:只需创建一个新的 commit,指向两个父 commit

引用和符号引用

引用 (Reference)

引用是一个指向 commit 的指针,存储在.git/refs/目录下。

类型

  • 分支引用:refs/heads/<branch>
  • 标签引用:refs/tags/<tag>
  • 远程引用:refs/remotes/<remote>/<branch>

查看引用

# 查看所有引用gitshow-ref# 查看分支引用gitshow-ref --heads# 查看标签引用gitshow-ref --tags

符号引用 (Symbolic Reference)

符号引用是一个指向另一个引用的引用,如HEAD

HEAD 的两种状态

  1. 指向分支(正常状态):

    HEAD → refs/heads/main → commit
  2. 直接指向 commit(分离 HEAD):

    HEAD → commit

查看符号引用

# 查看 HEAD 指向gitsymbolic-ref HEAD# 查看所有符号引用find.git -type f -name"HEAD"-o -name"*HEAD*"

Git 对象存储机制

对象压缩

Git 使用 zlib 压缩算法存储对象,节省存储空间。

压缩效果

  • 文本文件:通常压缩率 50-70%
  • 二进制文件:压缩率较低

对象打包

为了进一步提高效率,Git 会将多个对象打包成 pack 文件。

打包机制

  • 位置:.git/objects/pack/
  • 格式:.pack文件(打包数据)+.idx文件(索引)
  • 触发:git gc(垃圾回收)时自动打包

查看打包文件

# 查看 pack 文件ls.git/objects/pack/# 查看 pack 文件信息gitverify-pack -v .git/objects/pack/*.idx

对象去重

相同内容的文件只存储一份,通过引用共享。

去重示例

# 创建两个内容相同的文件echo"hello">file1.txtecho"hello">file2.txt# 添加到 Gitgitaddfile1.txt file2.txt# 查看对象,发现只存储了一份gitcat-file --batch-check --batch-all-objects|grepblob

哈希算法与完整性

SHA-1 哈希

Git 使用 SHA-1 算法计算对象的哈希值。

特点

  • 40 位十六进制字符串
  • 相同内容产生相同哈希
  • 内容稍有改动,哈希值完全不同
  • 不可逆(无法从哈希值反推内容)

计算示例

# 计算内容的哈希值echo"hello"|githash-object --stdin# 输出: ce013625030ba8dba906f756967f9e9ca394464a

完整性验证

Git 通过哈希值验证对象完整性:

# 验证对象完整性gitfsck# 验证特定对象gitcat-file -t<hash>gitcat-file -p<hash>

损坏检测

如果对象文件损坏,Git 会检测到哈希值不匹配,报告错误。

实际示例演示

示例 1:手动创建对象

# 1. 创建 blob 对象echo"Hello, Git!"|githash-object -w --stdin# 输出: a5c19667710254f4f5137305c3b1092e62643261# 2. 查看 blob 对象gitcat-file -p a5c19667710254f4f5137305c3b1092e62643261# 输出: Hello, Git!# 3. 创建 tree 对象(需要先有 blob)gitupdate-index --add --cacheinfo100644\a5c19667710254f4f5137305c3b1092e62643261 hello.txt# 4. 写入 tree 对象tree_hash=$(gitwrite-tree)echo$tree_hash# 5. 创建 commit 对象commit_hash=$(echo"Initial commit"|\gitcommit-tree $tree_hash)echo$commit_hash# 6. 更新分支引用gitupdate-ref refs/heads/main$commit_hash

示例 2:查看对象关系

# 查看最新提交gitcat-file -p HEAD# 查看提交指向的 treegitcat-file -p HEAD^{tree}# 查看 tree 中的文件gitls-tree HEAD# 查看文件的 blobgitcat-file -p HEAD:README.md

示例 3:探索对象存储

# 查看所有对象find.git/objects -type f|head-10# 查看对象类型分布gitcat-file --batch-check --batch-all-objects|\awk'{print $2}'|sort|uniq-c# 查看对象大小gitcat-file --batch-check --batch-all-objects|\awk'{sum+=$3} END {print sum}'

总结

核心概念总结

  1. Blob:存文件内容
  2. Tree:存目录结构(文件名、子目录等)
  3. Commit:存项目快照历史tree+parent+ 元信息)
  4. 分支:一个指向特定commit指针

Git 的设计优势

  1. 内容寻址:通过内容计算哈希,相同内容只存储一份
  2. 不可变性:对象一旦创建,内容不可修改
  3. 完整性:通过哈希值验证数据完整性
  4. 高效性:分支操作只是移动指针,非常快速

理解原理的价值

理解了这套对象模型,你就能明白:

  • resetrevertmerge等高级操作背后的原理
  • 为什么 Git 如此高效
  • 如何解决复杂的问题
  • 如何优化 Git 仓库

深入学习

  • Pro Git 中文版
  • Git Internals
  • Git Objects
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/14 10:11:00

VirtualLab Fusion应用:衍射光束扩散器产生LightTrans标识的设计与分析

摘要衍射扩散器可以被设计来创建任何图案。在这里&#xff0c;我们展示了 VirtualLab Fusion的一些可能性&#xff0c;以设计、优化、建模和仿真这种衍射光学元件&#xff08;DOE&#xff09;并把公司的标志投射到一幢大楼上。有不同的方法来生成光的图案。利用相干激光和衍射扩…

作者头像 李华
网站建设 2026/3/14 11:13:03

Windows变身AirPlay接收器:Shairport4w终极使用手册

Windows变身AirPlay接收器&#xff1a;Shairport4w终极使用手册 【免费下载链接】Shairport4w An AirPlay Audio-Receiver for your Windows-PC 项目地址: https://gitcode.com/gh_mirrors/sh/Shairport4w 还在为苹果设备与Windows电脑之间的音频壁垒而烦恼吗&#xff1…

作者头像 李华
网站建设 2026/3/15 19:24:12

Windows 11性能调优实战:系统加速与资源优化完整指南

Windows 11性能调优实战&#xff1a;系统加速与资源优化完整指南 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本&#xff0c;用于从Windows中移除预装的无用软件&#xff0c;禁用遥测&#xff0c;从Windows搜索中移除Bing&#xff0c;以及执行各种其他更改以简化和改…

作者头像 李华
网站建设 2026/3/20 3:20:33

BioAge生物年龄计算:从入门到精通的完整指南

BioAge生物年龄计算&#xff1a;从入门到精通的完整指南 【免费下载链接】BioAge Biological Age Calculations Using Several Biomarker Algorithms 项目地址: https://gitcode.com/gh_mirrors/bi/BioAge BioAge是一个基于R语言的生物年龄计算工具包&#xff0c;它通过…

作者头像 李华
网站建设 2026/3/14 23:56:00

DLSS Swapper终极指南:免费提升游戏画质的神器

DLSS Swapper终极指南&#xff1a;免费提升游戏画质的神器 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 作为一名资深游戏玩家&#xff0c;我曾经对游戏中模糊的画面和帧率波动感到困扰。直到发现了DLSS Swapper这款…

作者头像 李华