git-principle

Updated: 2026-04-19

Git 核心原理

理解 Git 的设计思想和内部机制,让你在遇到问题时不再慌张。


目录

  1. Git 是什么
  2. 版本控制能解决什么问题
  3. 分布式 vs 集中式
  4. 三个区域
  5. 文件的四种状态
  6. 快照,不是差异
  7. Commit 的本质
  8. 分支的本质
  9. .git 目录结构

1. Git 是什么

Git 是一个 分布式版本控制系统(DVCS),由 Linus Torvalds 于 2005 年为管理 Linux 内核源码而创建。

核心设计目标:

  • 速度快:绝大多数操作在本地完成,不依赖网络
  • 完全分布式:每个开发者的电脑上都有完整的仓库副本
  • 数据完整性:所有对象都通过 SHA-1 哈希校验,任何篡改都会被发现
  • 支持大规模并行开发:廉价的分支和高效的合并机制

2. 版本控制能解决什么问题

想象你在写一篇毕业论文:

论文_v1.docx
论文_v2.docx
论文_v2_导师修改.docx
论文_v3_最终版.docx
论文_v3_最终版_真的最终版.docx
论文_v3_最终版_打死不改版.docx

Git 帮你把这些混乱的版本管理变成一条清晰的时间线,你可以:

  • 随时回退到任何一个历史版本
  • 并行开发不同功能,互不干扰
  • 多人协作,自动合并各自的修改
  • 追踪每一行代码是谁、在什么时候、为什么改的

3. 分布式 vs 集中式

特性 集中式(SVN) 分布式(Git)
仓库位置 只在服务器上 每台电脑都有完整仓库
离线工作 不行 可以
速度 依赖网速 本地操作,极快
安全性 服务器挂了就完了 任何一份克隆都是完整备份
分支成本 高(需要复制目录) 极低(仅创建指针)
┌──── 集中式(SVN)────┐          ┌──── 分布式(Git)────┐
│                      │          │                      │
│   ┌──────────┐       │          │   ┌──────────┐       │
│   │  服务器   │       │          │   │ 远程仓库  │       │
│   │ (唯一仓库)│       │          │   │ (GitHub)  │       │
│   └────┬─────┘       │          │   └──┬───┬───┘       │
│        │             │          │      │   │           │
│   ┌────┴────┐        │          │ ┌────┴┐ ┌┴────┐      │
│   │ 客户端A  │        │          │ │完整  │ │完整  │      │
│   │(无完整   │        │          │ │仓库A │ │仓库B │      │
│   │ 历史)    │        │          │ └─────┘ └─────┘      │
│   └─────────┘        │          │                      │
└──────────────────────┘          └──────────────────────┘

4. 三个区域

Git 管理文件时有三个核心区域,这是理解 Git 工作方式的关键:

┌─────────────┐    git add     ┌──────────────┐   git commit   ┌──────────────┐
│  工作区       │ ───────���────→ │  暂存区        │ ────────────→ │  版本库       │
│ Working Dir  │               │ Staging Area  │               │  Repository  │
│              │ ←──────────── │               │               │              │
│  你编辑文件   │   git restore │  准备提交的快照 │               │  永久保存的   │
│  的地方       │               │               │               │  历史记录     │
└─────────────┘               └──────────────┘               └──────────────┘

工作区(Working Directory)

你实际编辑文件的地方,就是你在资源管理器/Finder 里看到的文件夹。你在这里创建、编辑、删除文件,和平时操作文件完全一样。

暂存区(Staging Area / Index)

一个临时区域,存放你「打算提交」的改动。

为什么需要暂存区? 想象你同时改了 5 个文件,但只想提交其中 3 个。暂存区就像一个购物车——你可以挑选哪些改动要提交,哪些暂时不管。这让你的每次提交都干净、有意义。

版本库(Repository)

.git 目录,存放所有历史记录。每次 commit 就是给当前暂存区的状态拍一张「快照」,永久保存。


5. 文件的四种状态

在 Git 的视角下,每个文件都处于以下四种状态之一:

                   git add
Untracked ─────────────────────→ Staged ──git commit──→ Committed
                                   ↑                        │
                                   │      编辑文件           │
                                   └──── Modified ←─────────┘
状态 含义 对应操作
Untracked(未跟踪) 新文件,Git 还不知道它的存在 刚创建的文件
Staged(已暂存) 文件的改动已被标记,等待下次 commit git add
Committed(已提交) 改动已安全保存到版本库中 git commit
Modified(已修改) 已提交过的文件被再次编辑 编辑已跟踪的文件后

git status 可以随时查看所有文件的当前状态。


6. 快照,不是差异

这是 Git 与大多数版本控制系统的根本区别

差异存储(Delta-based)— 其他 VCS 的做法

Version 1:  File A (original)  |  File B (original)  |  File C (original)
Version 2:  ΔA₁                |                     |  ΔC₁
Version 3:  ΔA₂                |  ΔB₁                |
Version 4:                     |  ΔB₂                |  ΔC₂

每个版本只存储相对于上一个版本的「差异(delta)」。恢复某个版本需要从头逐步叠加所有差异。

快照存储(Snapshot-based)— Git 的做法

Version 1:  File A₁  |  File B₁  |  File C₁
Version 2:  File A₂  |  File B₁  |  File C₂     ← B 没变,存一个链接即可
Version 3:  File A₃  |  File B₂  |  File C₂     ← C 没变,存一个链接即可
Version 4:  File A₃  |  File B₃  |  File C₃     ← A 没变,存一个链接即可

每次 commit 都是整个项目的一次完整快照。但 Git 很聪明:如果一个文件没有变化,它不会重新存储,而是保留一个指向上次快照中该文件的链接。这让 Git 既高效又可靠。

优势

  • 恢复任何版本都是 O(1) 操作,不需要逐步回放差异
  • 数据完整性更好,不依赖差异链
  • 分支切换极快

7. Commit 的本质

每次提交(commit)是一个不可变对象,包含:

  • tree 对象:项目所有文件的一个完整快照
  • 作者信息:谁写的代码、什么时候写的
  • 提交者信息:谁提交的(可能和作者不同)
  • 提交说明:commit message
  • 父指针:指向上一次提交(形成链式结构)
commit c3 (SHA: a1b2c3...)
│  author: Alice <alice@example.com>
│  date:   2026-04-19 10:30:00
│  message: "添加搜索功能"
│  parent:  → commit c2
│
└── tree
    ├── README.md    → blob 对象 (文件内容)
    ├── src/
    │   ├── main.py  → blob 对象
    │   └── search.py → blob 对象 (新增)
    └── .gitignore   → blob 对象

Git 的对象模型

Git 的底层是一个内容寻址文件系统,有四种对象:

对象类型 作用
blob 存储文件内容(不含文件名)
tree 存储目录结构,记录文件名和 blob 的对应关系
commit 存储提交信息,指向一个 tree 和父 commit
tag 标记特定 commit(如版本号 v1.0.0)

每个对象都通过其内容的 SHA-1 哈希值来唯一标识。这意味着:

  • 相同的文件内容永远只存一份
  • 任何内容被篡改都会导致哈希不匹配
  • 哈希值就是对象的「地址」
# 查看某次 commit 的详细信息
git cat-file -p HEAD

# 查看某个 tree 的内容
git cat-file -p HEAD^{tree}

# 查看某个 blob 的内容
git cat-file -p <blob-hash>

8. 分支的本质

分支只是一个指向某次 commit 的可移动指针。这是 Git 最优雅的设计之一。

          main
           ↓
c1 ← c2 ← c3
           ↑
         feature
  • 创建一个分支只需要写一个 41 字节的文件(40 个字符的哈希 + 换行符)
  • 切换分支只是把 HEAD 指向不同的分支指针
  • 所以 Git 的分支操作几乎是瞬间完成的

HEAD 指针

HEAD 是一个特殊指针,指向你「当前所在的分支」:

HEAD → main → c3

当你切换分支时:

git switch feature
# HEAD → feature → c3

当你在 feature 分支上提交时:

git commit -m "新功能"
# HEAD → feature → c4
# main 仍然指向 c3
         main
          ↓
c1 ← c2 ← c3
              ↖
               c4
               ↑
            feature  ← HEAD

分离的 HEAD(Detached HEAD)

如果直接 checkout 到一个 commit(而不是分支),HEAD 就不再指向分支,这叫「分离的 HEAD」:

git checkout a1b2c3    # 直接跳到某个 commit
# HEAD → a1b2c3(不经过任何分支)

在这个状态下的提交不属于任何分支,容易丢失。如果要保留,需要创建一个分支:

git switch -c save-my-work

9. .git 目录结构

每个 Git 仓库的根目录下都有一个 .git 隐藏文件夹,这就是 Git 的「大脑」:

.git/
├── HEAD              # 当前分支指针(如 ref: refs/heads/main)
├── config            # 仓库级配置
├── description       # 仓库描述(GitWeb 用)
├── hooks/            # Git 钩子脚本(提交前检查、推送后通知等)
├── index             # 暂存区(Staging Area)
├── objects/          # 所有 Git 对象(blob, tree, commit, tag)
│   ├── pack/         # 打包后的对象(节省空间)
│   └── info/
├── refs/             # 所有引用(分支、标签)
│   ├── heads/        # 本地分支(如 heads/main)
│   ├── tags/         # 标签
│   └── remotes/      # 远程跟踪分支
└── logs/             # 引用变更日志(reflog)

关键要点

  • 删除 .git 文件夹 = 丢失所有版本历史(工��区文件不受影响)
  • objects/ 目录下的文件是不可变的,一旦写入就不会被修改
  • refs/heads/main 文件内容就是一个 40 位的 commit 哈希值

延伸阅读

推荐学习资源

资源 说明
Pro Git - Git 内部原理 官方书籍中关于 Git 内部原理的章节
Learn Git Branching 交互式可视化学习 Git 分支

最后更新:2026-04-19

目录