news 2026/8/26 7:19:48

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/8/25 8:54:53

Python 使用 openpyxl 从 URL 读取 Excel 并获取 Sheet 及单元格样式信息

摘要 本文介绍了基于openpyxl库实现从URL读取Excel文件并提取单元格内容和样式信息的方法。主要实现了两个功能&#xff1a;1)获取指定Sheet指定区域的单元格值及样式(如字体大小、颜色、背景色等)&#xff1b;2)获取Excel中所有Sheet名称列表。文章详细说明了核心思路、完整代…

作者头像 李华
网站建设 2026/8/21 15:29:18

人层粘连蛋白γ1链在细胞外基质结构与功能中有何核心作用?

一、层粘连蛋白如何构成基底膜的核心支架&#xff1f;层粘连蛋白&#xff08;Laminin&#xff0c; LN&#xff09;是一类高分子量的糖蛋白&#xff0c;分子量约在820至850 kDa之间。它是细胞外基质中基底膜最核心的非胶原糖蛋白成分。经典的层粘连蛋白分子由三条多肽链&#xf…

作者头像 李华
网站建设 2026/8/8 3:53:36

SpringBoot 拦截器-监听器实战

文章目录SpringBoot4.0 拦截器-监听器实战**拦截器的概念和作用**拦截器实战监听器概念和作用**监听器实战SpringBoot4.0 拦截器-监听器实战 拦截器的概念和作用 核心概念说明 拦截器&#xff08;Interceptor&#xff09;是Spring MVC框架中的重要组件&#xff1b;用于在请求…

作者头像 李华
网站建设 2026/8/22 21:39:24

全网最全8个AI论文网站,专科生搞定毕业论文+格式规范!

全网最全8个AI论文网站&#xff0c;专科生搞定毕业论文格式规范&#xff01; AI 工具如何助力专科生高效完成毕业论文 在当前的学术环境中&#xff0c;越来越多的专科生开始借助 AI 工具来辅助自己的毕业论文写作。这些工具不仅能够帮助学生节省大量时间&#xff0c;还能有效降…

作者头像 李华
网站建设 2026/8/25 13:12:55

网易云音乐无损下载完整指南:快速构建个人高品质音乐库

网易云音乐无损下载完整指南&#xff1a;快速构建个人高品质音乐库 【免费下载链接】NeteaseCloudMusicFlac 根据网易云音乐的歌单, 下载flac无损音乐到本地.。 项目地址: https://gitcode.com/gh_mirrors/nete/NeteaseCloudMusicFlac 想要将网易云音乐中的心爱歌单以无…

作者头像 李华
网站建设 2026/8/22 6:59:06

终极解密:5分钟掌握RPCS3汉化补丁,让PS3游戏说中文

终极解密&#xff1a;5分钟掌握RPCS3汉化补丁&#xff0c;让PS3游戏说中文 【免费下载链接】rpcs3 PS3 emulator/debugger 项目地址: https://gitcode.com/GitHub_Trending/rp/rpcs3 还在为看不懂日文或英文的PS3游戏而烦恼吗&#xff1f;RPCS3模拟器的汉化补丁功能让你…

作者头像 李华