看到一篇文章,重新又勾起了我对版本控制工具的回忆和兴趣,拿出最好用的两种来比较比较吧:git和hg 。
1.对象模型(Object Model)
hg是采用增量式存储的版本控制系统,它保存相邻版本间的差异,通过在基础版本之上叠加差别的方式记录版本的更新,其组织方式自然采用链表。这点和subversion一致,数据库版本控制工具dbdeploy也采用相同原理。
hg中的基本对象有三种:file、manifest 和changeset
git并非采用增量式存储,而是为每个版本创建一个快照,最重要的组织结果是树。
基本对象有三种,自底向上为:blob、tree和commit(还有个tag, 和hg的bookmark类似,略了 )
2. 工作目录与专用目录(working dir and Git/Hg dir)
两种版本控制工具都将代码checkout到工作目录下,工作目录中保存的是项目当前branch的当前版本;同时在本地repo的根目录(也是工作目录)下为各自系统建立一个专用的目录(.hg , .git), 存储版本历史、对象与索引和配置文件等。
不同之处在于建立codebase时,这里首先要提到bare repositories的概念。
# Why bare?
一个bare repo与普通repo的区别是没有项目文件的working copy,即repo根目录下只有专用目录,而没有任何其他代码文件和文件夹;这是为了响应作为codebase应当遵循的“Only store, never update from revisions(只存储版本,不更新到实际代码文件)”原则。
hg管理的repo天生就能做codebase使用,无论是否是bare的,这点是由其分布式版本控制系统的本质决定的,它可以随时把当前的repo通过自带的http server发布代码,特别适合分布式开源项目的代码分享。
git也是分布式代码版本管理工具,不过它对作为codebase的repo做了严格的bare要求。可以看到的是许多人在初学git时不了解这一点,抱怨自己做spike时不知如何提交代码到在本机上的codebase。这里顺手写下两个tips:
* 初始建立一个bare repo
$ git init --bare
* 如果已有一个repo了,使用下面的方法将其转化为bare的
$ git config --bool core.bare true , 之后可删除除了repo根目录.git文件夹之外的所有文件,即只保留专用目录
3. 分支与合并(branch and merge)
git中是有同一个repo上不同branch的概念,而hg没有。就是说,git在切换到别的branch上时,仍然是在原来的repo中,工作目录下的代码也会相应地切换到某个版本上;而hg上新建branch 则意味着有了另一个repo,不能算做轻量级的分支(lightweight branch)。
hg中是以heads的概念来维护不同开发者checkout的代码的,它不像SVN这样的集中式版本控制,不需要回归到某一根主线上来(当然常常我们需要这样用,方便持续集成),所以向某个codebase提交时需要先与该branch上的head来merge,结果合并为一个新的共有的head(这显然需要一次提交),此时两条branch交汇到一处。
git中可以在同一repo下,实现在指定branch上独立提交和在不同branch间merge。提交和更新是灵活和易控制的。
另外,hg中鼓励以changeset为单位进行merge后提交,即先将本地修改做本地提交,之后采用mercurial queue或fetch的方式合并后提交到codebase。其原理是head之间的合并。
git中鼓励在未形成本地commit时更新代码并做merge,之后将本地提交和向codebase提交连起来做。因为本地commit之后做更新和合并,并不是git对象模型所擅长的,被迫merge时容易将被引入“no branch”的临时branch状态。这种状态上也是可以进行开发的,但风险是没有正规的branch来记录版本。这里又有一个tip。
# 'no branch' problem
* 从“no branch”状态转回正常branch上, 比如master
1)在“no branch”状态上完成与代码库的同步,即保证没有重要的本地修改和本地提交残留;
2)$ git checkout master ,直接转到master上,这时对no branch的索引就丢失了,该状态不复存在;
3)$ git log ,检查master branch上的log,找到是否有本地修改/提交残留(通常是有的,因为是在做merge时出的问题);
4)$ git reset --hard HEAD~3, 放弃已有的本地提交(假设是3个,如果是两个可用HEADE^^),--hard是删除指针及内容;
5)在没有本地修改和提交的基础上,就可放心地同步到master上最新的版本了,你知道怎么做这步。
其他
诸如git的unstaged area,mercurial queue以及两个工具的应用模式,都不在此介绍了。
参考
http://mercurial.selenic.com/wiki/GitConcepts
《Git Community Book》