Git 核心概念
内容:设计思想 -> 数据模型 -> 核心对象 -> 工作流 -> 分支原理 -> 合并原理
Git 是一个:内容寻址(Content-addressable)的对象数据库
- SVN:存“文件 + 差异”
- Git:存“内容快照 + 指针”
👉 这就是 Git 快、分支便宜、合并强的根本原因。
每个 Git 仓库都包含:
- 完整历史
- 所有分支
- 所有对象
所以 Git 的 commit、log、diff 都是本地操作,非常快。
Git 仓库本质是一个对象数据库,只有 4 种对象:
| 对象 | 作用 |
|---|---|
| blob | 文件内容 |
| tree | 目录结构 |
| commit | 一次提交 |
| tag | 标签(指向 commit) |
- 只存文件内容
- 不存文件名、不存路径
- 相同内容 -> 相同 SHA-1 -> 只存一份
"hello\n" -> blob -> sha1: aaf4c61d...
- 记录:
- 文件名
- 权限
- 指向 blob / 子 tree 的指针
类似 Linux 文件系统的 inode。
tree
├── file1.txt -> blob
└── src/ -> tree
└── main.py -> blob
commit 不是“文件集合”,而是:
commit
├── 指向一个 tree(项目快照)
├── 指向父 commit(1 个或多个)
├── 作者、时间、message
👉 commit 是一个指针对象
- 轻量 tag:直接指向 commit
- 附注 tag:一个对象,指向 commit
用于版本发布。
- Git 使用 SHA-1 哈希
- 内容一样 -> hash 一样
- 内容变了 -> hash 全变
👉 所以:
- Git 能自动去重
- Git 能检测数据损坏
- Git 非常适合分布式
Working Tree -> Index -> Repository
| 区域 | 作用 |
|---|---|
| Working Tree | 实际文件 |
| Index(Stage) | 准备提交的快照 |
| Repository | 已提交历史 |
关键理解:
- git add:不是“添加文件”,而是更新 Index
- git commit:把 Index 写成 commit
HEAD 是一个指针,指向当前分支或 commit
HEAD -> refs/heads/main -> commit
HEAD 直接指向 commit,而不是分支:
HEAD -> commit
👉 提交会“漂浮”,不属于任何分支。
分支不是拷贝
Git 分支只是一个 指向 commit 的可移动指针
refs/heads/main -> commit A
创建分支:
refs/heads/feature -> commit A
成本几乎为 0。
因为:
- 不复制文件
- 只是新建一个引用文件
Git 合并时会找:
- 共同祖先
- 当前分支
- 被合并分支
然后:
- 自动合并不冲突的部分
- 冲突部分交给人处理
commit M
├── parent1 -> main
└── parent2 -> feature
👉 一个 commit 可以有多个父提交。
rebase 本质:
把一组 commit 复制到另一个基点上
A - B - C (main)
\
D - E (feature)
rebase 后:
A - B - C - D' - E'
- D’、E’ 是新 commit(hash 全变)
- 历史被重写
👉 这就是为什么 已 push 的分支不要 rebase
Index 的作用:
- 支持 部分提交
- 支持复杂合并
- 支持精细控制
没有 Index:
- 你只能一次性提交全部文件
- 合并冲突处理会非常痛苦
因为:
- 内容寻址
- 三方合并
- tree / blob 分离
- 分支极轻量
👉 Git 从设计之初就假设:分支和合并是常态
- 所有对象都有 hash
- 任何损坏都会被发现
- push/pull 会校验对象完整性
| Git | 文件系统 |
|---|---|
| blob | 文件内容 |
| tree | 目录 |
| commit | 快照 |
| branch | 指针 |
| HEAD | 当前指针 |
因为:
- git reflog 记录了指针变化
- 大部分“删除”只是移动指针
- 真正数据会被保留一段时间
👉 99% 的误删都能救回来
- Git 不是文件差异系统,而是一个内容寻址的对象数据库
- commit 是指向 tree 的指针
- 分支只是可移动引用
- merge 是三方合并
- rebase 是提交复制