Git 核心概念总结
Git 是分布式版本控制的事实标准,但多数人只会背命令,遇到冲突和误操作就慌。其实 Git 的所有命令都在操作同一套简单模型:三个区、三种对象、一堆指针。建立这套模型,比记一百条命令更有用。
为什么是分布式版本控制
版本控制系统的发展大致是三步:本地版本控制(靠复制目录或本地数据库记录差异)→ 集中式版本控制(CVCS,如 SVN,所有历史存在一台中央服务器上,客户端只取当前版本)→ 分布式版本控制(DVCS,如 Git)。
集中式的软肋很明显:中央服务器宕机,全员无法提交;中心磁盘损坏且没备份,历史全部丢失。分布式的解法是每个客户端都克隆完整的仓库——包括全部提交历史。任何一台机器挂了,随便找个克隆就能恢复。这也让 Git 的大部分操作(提交、看日志、切分支)都在本地完成,不依赖网络,速度极快。
三区模型:工作区、暂存区、仓库
日常操作只跟三个「区」打交道:
- 工作区(Working Directory):你实际看到和编辑的文件。
- 暂存区(Staging Area / Index):一个待提交清单,
git add把修改放进来。它让你可以精确控制「这次提交包含哪些改动」,而不是把一揽子修改全提交。 - 本地仓库(Repository):
.git目录里保存的提交历史,git commit把暂存区的内容固化成一个提交。
推送到远程仓库是第四步:git push 把本地提交同步到 GitHub 等远端。git status 本质上就是在汇报这三个区之间的差异,看不懂输出时先想清楚「哪个区和哪个区在比」。
对象模型直觉:blob、tree、commit
Git 不存「文件差异」,而是存内容快照,所有数据都是内容寻址的对象(以内容的 SHA-1 哈希为键),核心就三种:
- blob:一个文件的内容。两个文件内容相同,在 Git 里只存一份。
- tree:一个目录,记录文件名到 blob 或子 tree 的映射。
- commit:一次提交,指向一个根 tree(即该时刻整个项目的快照),并记录父提交、作者、时间戳和提交信息。
提交通过父指针串成一条有向无环图——这就是「提交历史」的真相。理解这一点后,很多「魔法」都不再神秘:git commit 只是新建一个 commit 对象;哈希相同意味着内容完全一致;Git 天然去重,所以仓库比想象中省空间。
分支的本质:一个指针
分支不是什么代码副本,只是一个指向某个 commit 的可移动指针,创建分支就是写一个 41 字节的文件(40 位哈希 + 换行)。这就是 Git 分支「秒建、秒切、秒删」的原因——不像 SVN 那样复制整个目录树。
还有一个特殊指针叫 HEAD,指向当前所在的分支(或 commit)。提交时:新建 commit 对象 → 当前分支指针前移 → HEAD 跟着分支走。git checkout -b feature 等价于「新建指针 + HEAD 转过去」,两步都是改指针,所以瞬间完成。
merge vs rebase:场景化选择
分支合并有两条路,行为差异决定了适用场景:
- merge:新建一个合并提交,把两条历史汇合。历史保留分叉结构,忠实记录「这些工作是并行做的」。
git merge feature不改写任何已有提交,绝对安全。 - rebase:把当前分支的提交「摘下来」,逐个重放到目标分支最新提交之后。历史变成一条直线,干净易读;但重放会生成新的 commit 对象(哈希变了),等于改写了历史。
选择原则一句话:已推送(共享)的提交不 rebase,未推送的本地提交放心 rebase。典型工作流:
# 本地功能分支开发期间,同步主干最新代码:用 rebase 保持线性 |
变基过程中断想放弃:git rebase --abort;合并到一半想反悔:git merge --abort。
reset、revert、checkout 辨析
三个「撤销系」命令最容易混:
git reset:移动分支指针(回退历史)。三种模式:--soft只动指针,改动留在暂存区;--mixed(默认)指针回退且清暂存区,改动留在工作区;--hard连工作区一起清掉,未提交的改动直接丢失,慎用。reset 会改写历史,已推送的提交不要 reset。git revert:不改写历史,而是新建一个「反向提交」来抵消指定提交的效果。因为只是新增提交,它是撤销已推送提交的唯一安全方式。git checkout/git restore/git switch:在「区」之间搬运或切换。git checkout -- file(新语法git restore file)丢弃工作区改动;git restore --staged file把文件移出暂存区;git switch branch切分支。
撤销操作决策树
遇到「想撤销」,按两个问题定位:
- 改动提交了吗?
- 没提交,想丢弃工作区修改:
git restore <file>。 - 没提交,但已 add 进暂存区:
git restore --staged <file>(退回工作区)。
- 没提交,想丢弃工作区修改:
- 提交推送了吗?
- 没推送,想撤销最近提交但保留改动:
git reset --soft HEAD~1(改动回暂存区)或git reset --mixed HEAD~1(改动回工作区)。 - 没推送,想彻底丢弃最近提交:
git reset --hard HEAD~1。 - 已推送:
git revert <commit>,生成反向提交后正常 push。千万别对已推送历史 reset 后强推,除非你确定没有别人基于它工作。
- 没推送,想撤销最近提交但保留改动:
误删了提交也别慌:git reflog 记录 HEAD 的每一次移动,找到丢失提交的哈希,git reset --hard <hash> 就能找回来——提交对象在仓库里会留存相当长的时间。
小结
- Git 是分布式系统:每个克隆都是完整备份,大部分操作离线完成。
- 三区模型:工作区 →(add)→ 暂存区 →(commit)→ 仓库 →(push)→ 远程。
- 对象模型:commit 指向 tree,tree 指向 blob,历史是 commit 组成的有向无环图。
- 分支和 HEAD 都是指针,这是 Git 轻量分支能力的根源。
- merge 保历史、rebase 求线性;共享提交不 rebase。
- 撤销先问「提交了吗、推送了吗」:未提交用 restore,未推送用 reset,已推送用 revert,误删用 reflog 找回。

