理解 Mercurial
Mercurial 的分布式协同模式,对于新手而言是混乱的, 本文试图澄清一些基本概念,至于 hg 的使用,请参考:Mercurial教程
1. 仓库(Repository)中有什么?
Mercurial仓库(Repository) 包含工作目录(Working Directory) 和版本仓库(.hg目录)
版本仓库(存在于.hg隐藏目录中)包含了完整的项目历史. 不同与其它配置管理系统,那些集中式版本管理系统,只有一个包含所有版本历史的中央仓库,而每人本地的工作目录则仅包含当前最新版本. 这将有利于开发者进行并行协作.
工作目录包含的是项目文件当供给编辑的在某个时间点的状态(比如上图中的rev 2). Mercurial中的标签(Tag)和 忽略文件声明(.hgignore文件),也被版本控制,所以他们也包括在上图中.
Mercurial中的每个版本都有其 父辈版本,比如上图中rev 2的父辈是rev 1,而工作目录的父辈是rev 2.
2. 提交变更(Commit Changes)
提交(Commit)操作后,工作目录的父辈版本就成了刚刚提交的新变更集(Changeset)(也称为新 "版本(Revision)"):
上图中的rev 4是rev 2的一个分支, 现在rev 4是工作目录的父辈.
3. 版本,变更集,头部,顶部
Mercurial中多个文件的相关修改称为变更集(ChangeSet), 每个版本(Revision)对应一个变更集。 每个变更集会分配一个递增的整数 版本号。在分布式开发过程中, 各个用户的版本号会产生冲突. 因此每个变更及也会被分配一个全局唯一的变更集ID. 变更集ID是四十位的16进制数字, 也可以略写成足够明确的"e38487"形式.
在版本历史的任何一点,都可以进行分支与合并(Merge).而每一个未合并的分支,实际都创建了版本历史的一个新头部(Head).
上图中的rev 5和rev 6 都是头部。 版本号最大的头部被称为顶部(Tip), 如上图中的 rev 6.rev 4有两个父辈(rev 2和rev 3),它是个合并变更集.
4. 克隆,变更,合并,拉和更新
假设用户Alice有如下所示的仓库:
Bob 在本地克隆(Clone)了这个仓库, 得到了Alice的版本仓库的一个完整、独立的本地副本. 并通过检出操作,获得了最新顶部版本.
Bob提交(Commit)两个修改e和f(在他本地仓库):
同时,Alice也修改她的版本g, 因此她的仓库与Bob的不同了, 也就是说,她创建了一个分支(Branch):
Bob使用pulls(拉取)操作将Alice的仓库变更到本地. 这个操作将Alice的所有修改集更新到Bob的版本仓库中 (这个例子中只有一个修改集g).
需要注意的是,这个操作并 没有 更改Bob的工作目录:
因为Alice's g 版本是最新的头部, 因此此版本也是 tip(顶部).
Bob随后进行了合并(Merge)操作, 将其本地修改(f)与仓库中的tip进行合并. 这时, 他的工作目录具有两个父辈(f和g):
查看并确认操作合并成功后, Bob 提交合并结果,得到了一个新的合并变更集h在他的本地仓库中:
现在,如果Alice pulls 从 Bob的仓库, 她会得到Bob的变更e,f和h:
注意! 当前 Alice 的工作目录并没有因 pull(拉取)操作而改变. 她必须使用更新(Update)操作来同步版本仓库到合并变更集h. 这将改变她版本仓库的父辈到h,并将工作目录中的文件更新成h版本的.
Alice和Bob完全同步了.
5. 分布式系统
Mercurial是一个完全的分布式系统, 因此没有所谓的集中式仓库概念. 这也意味着用户可以自由的定义协同工作的组织结构
在一个集中式版本管理系统中提交实验性的修改可能会造成较大的负面影响, 但对于Mercurial之类的DVCS来说, 可以肆意的进行试验性操作, 大不了删除本地工作目录,因为在别的地方还有若干完整的。
6. 什么是Mercurial不能做的
SVN/CVS用户会将多个相关的项目放在同一个仓库里. 但是这真的不应该在Hg 中这么做,因为在Hg 中你只能检出整个仓库,而不是其中的某个目录.
如果确实想要将多个项目放在同一个仓库中, 可以使用1.3版本以后的子仓库 功能或者更老版本的 ForestExtension 将不同项目的仓库,嵌套成一个大仓库.
关于Mercurial的入门操作, 请参阅 Mercurial教程.