字数
1179 字
阅读时间
5 分钟
版本控制系统
版本控制系统(VCSs
)是一类用于追踪源代码(或其他文件、文件夹)改动的工具。它的核心作用包括:
- 管理代码的修改历史;
- 让协作编码更方便。
VCS
通过“快照”机制工作:将某个文件夹及其内容的完整状态保存为快照,同时还会维护快照创建者、快照相关信息等。
实用性
无论单人开发还是团队协作,VCS都很有用:
- 单人开发时:可创建项目快照,记录每个改动的目的,支持多分支并行开发等;
- 团队协作时:能查看他人对代码的修改,还可解决“并行开发引发的冲突”,是协作的“无价之宝”。
现代版本控制系统的能力能轻松(甚至自动)回答这类问题:
- “当前模块是谁编写的?”
- “这个文件的某一行,什么时候被编辑?谁改的?修改原因是什么?”
- “最近1000个版本中,何时/为什么导致了单元测试失败?”
Git
尽管版本控制系统有很多,但Git是事实上的行业标准。文中还提到,一篇“XKCD漫画”反映了人们对Git的评价。
Git 接口存在 抽象泄漏(leaky abstraction) 问题,接口虽“丑陋”,但 底层设计和思想非常优雅,容易被理解。
Git 的数据模型
Git通过精心设计的数据模型,支持版本控制的关键特性:维护历史记录、支持分支开发、促进团队协作。
快照(文件与目录的Git表述)
Git将“顶级目录的文件/文件夹”视为集合,通过快照管理历史。其中涉及:
- Blob对象:Git中“文件”的表述,本质是“一组纯数据”(仅存储文件内容)。
- “树”(Tree):Git中“目录”的表述,负责映射“名字”与 Blob对象(文件)或其他树对象(子目录)(因此目录可嵌套子目录)。
- 快照:被追踪的最顶层“树”,代表某一时刻项目的“完整文件结构”。
顶层树结构示例
<root> (tree)
|-- foo (tree)
| |-- bar.txt (blob, contents = "hello world")
|-- baz.txt (blob, contents = "git is wonderful")
顶层树(<root>
)包含两个元素——子树foo
(内部含Blob对象bar.txt
)、直接的Blob对象baz.txt
。
历史记录建模:快照的关联方式
版本控制系统需将“快照(某一时刻的项目状态)”串联为历史。线性历史是最简单的模型(快照按时间顺序排成一条线),但 Git 并未采用这种方式。
Git 的历史是 由“快照”(在 Git 中称为「提交」)组成的有向无环图:
- 无需纠结“有向无环图”的数学概念,核心理解:每个「提交」都有 “父辈”提交(即它“之前”的快照)。
- 一个提交可以有 多个父辈(典型场景:分支合并后,新提交会同时关联两个分支的“父提交”)。
o <-- o <-- o <-- o
^
\
--- o <-- o
o <-- o <-- o <-- o <---- o
^ /
\ v
--- o <-- o
- 图中
o
代表一次「提交(快照)」,箭头指向“当前提交的父辈”(表示“时间/因果上的先后:父辈在当前提交之前”)。 - 体现了 分支与合并 的场景:
- 第三次提交后,历史“分岔”为两条独立分支(可用于并行开发不同特性);
- 开发完成后,分支“合并”产生新提交——这个新提交会同时包含两个分支的特性,且有多个父辈(来自两条分支的最后一次提交)。
Git 中的提交是不可改变的。但这并不代表错误不能被修改,只不过这种“修改”实际上是创建了一个全新的提交记录。而引用(参见下文)则被更新为指向这些新的提交。