简介
commit 是 Git 最核心的东西
一句话定义
commit 是一次“完整项目快照”的记录 + 元数据说明。
它不是“记录修改”,
而是记录:
某一时刻,整个项目长什么样。
1.commit 不是“差异”,而是“快照”
很多人误以为:
commit 记录的是改动内容(diff)
错
Git 实际上存的是:
整个目录树的状态(snapshot)
举例:
第一次提交:
main.go
README.md
第二次你只改了 main.go。
Git 内部仍然会记录:
main.go(新版本)
README.md(旧版本复用)
它是一个完整树结构。
2.一个 commit 里面到底包含什么?
一个 commit 对象包含:
tree(项目文件结构)
parent(父提交)
author(作者)
committer(提交者)
时间
commit message
我们探讨其中重点的几个部分
1)parent(形成链条)
每个 commit 都会指向它的父 commit。
比如:
A → B → C
C 里面会记录:
parent: B
这就是为什么 Git 能形成历史链。
如果是 merge commit:
parent: B
parent: E
会有两个父提交。
2)author / committer
Git 区分两个概念:
- author:原始作者
- committer:真正提交的人
3)tree(最重要)
tree 是:
这个提交对应的“目录结构”
它包含:
- 文件(blob 对象)
- 子目录(tree 对象)
blob 对象和tree 对象也称为git的基本对象
他们的关系为:
blob → 文件内容
tree → 目录结构
commit → 指向 tree
注意注意:
每一次 commit 的 tree 代表的是 整个项目的完整目录结构,不是只包含你这次修改的目录。
但——
Git 会“复用没有变化的对象”,
不会重复存储整个项目。
这点非常关键。
commit 记录的是“完整快照”(强调)
Git 不会把整个项目再存一遍,这是 Git 最聪明的地方。
假设你的项目是:
project/ (tree 对象)
├── main.go 文件(blob 对象)
├── config.yaml 文件(blob 对象)
└── service/ (tree 对象)
├── user.go 文件(blob 对象)
└── order.go 文件(blob 对象)
现在你只修改了:
service/user.go
你执行:
git commit
新的commit会变成:
project/
├── main.go (复用旧 blob)
├── config.yaml (复用旧 blob)
└── service/
├── user.go (新 blob)
└── order.go (复用旧 blob)
当你修改 user.go,并进行commit提交后:
Git 会:
1️⃣ 生成新的 blob(内容变了)
2️⃣ 生成新的 service tree
3️⃣ 生成新的根 tree
4️⃣ 生成新的 commit
所以,只有那条路径上的tree对象是新的。
4)commit message
比如:
feat: add payment API
这是纯文本。
3.commit 是不可变的
一旦 commit 生成:
- 不能修改
- 不能编辑
- 不能改内容
所谓“修改 commit”,
其实是:
创建一个新的 commit
然后让分支指向它
这就是为什么 rebase 会“重写历史”。
4.为什么 Git 这么设计?
因为:
- blob 是按内容 hash 存储
- 相同内容只存一份
- tree 只是指针集合
核心问题
commit 的 tree 是整个项目结构
但 Git 只会新增变化路径上的对象
其他全部复用
这也是为什么 Git 很快
- 创建分支几乎零成本
- commit 速度很快
- 存储空间高效
5.commit的操作命令
1)仓库必须已经初始化
要么:
git init
要么:
git clone ...
否则不能 commit。
2)必须有“已暂存(staged)”的内容
Git 提交的是:
暂存区(index)里的内容
❌ 不是工作区(Working Directory)里的内容
举个例子
你修改了:
main.go
现在状态是:
git status
会看到:
modified: main.go
这时候不能直接提交这个修改。
必须先:
git add main.go
这样文件才进入暂存区。
3)完整的 commit 操作流程
修改文件之后,查看状态
git status
添加到暂存区
添加单个文件:
git add main.go
添加全部改动:
git add .
提交
git commit -m "feat: add login API"
到这里,你成功在g仓库中创建了一个新的commit对象