分布式版本管理工具的内涵【三】Fossil篇
前面介绍了Git和Mercurial这两款分布式版本管理工具(DVCS),这篇开始介绍Fossil是怎么实现分布式版本管理的。
再介绍一下Fossil,他是SQLite的作者Richard Hipp开发的,集版本管理以及Wiki和问题管理于一身的DVCS。
我们还是以存储格式、文件集以及改动集的方式来介绍Fossil。
Fossil的存储格式
Fossil的存储格式是基于SQLite的,也就是说,不像Git把文件存在磁盘上,Fossil把文件存在SQLite数据库里面。
我们先用fossil new f1
创建一个名字叫做f1
的版本仓库。然后再SQlite中来查看这个版本仓库。
确保fossil和SQLite3软件包已经安装好了,在
f1
所在目录执行sqlite
命令
sqlite> .open f1
sqlite> .tables
artifact config mlink rcvfrom ticket
attachment delta orphan reportfmt ticketchng
backlink event phantom shun unclustered
blob filename plink tag unsent
concealed leaf private tagxref user
fossil把文件存储在artifact
试图中,而artifact
又是基于blob
和delta
两个表的:
sqlite> .schema artifact
CREATE VIEW artifact(rid,rcvid,size,atype,srcid,hash,content) AS SELECT blob
.rid,rcvid,size,1,srcid,uuid,content FROM blob LEFT JOIN delta ON (blob.rid
=delta.rid)
/* artifact(rid,rcvid,size,atype,srcid,hash,content) */;
sqlite> .schema blob
CREATE TABLE blob(
rid INTEGER PRIMARY KEY,
rcvid INTEGER,
size INTEGER,
uuid TEXT UNIQUE NOT NULL,
content BLOB,
CHECK( length(uuid)>=40 AND rid>0 )
);
sqlite> .schema delta
CREATE TABLE delta(
rid INTEGER PRIMARY KEY,
srcid INTEGER NOT NULL REFERENCES blob
);
从上面数据库的结构可以看出一些端倪,blob
表存储的是文件或者文件增量的内容。而delta
是用来存储文件基线版本和增量内容之间的关系。artifact
视图把这两个表统一起来了。
blob
表的初始内容
可以看下当前blob
表里面的内容:
sqlite> select * from blob;
1|1|166|fbfce048cc789eaa58c4e02fe92f3be4a491ddfdb5de869527f8f960ed510d1b|
blob
表有个字段uuid
,这个根据文件内容的哈希值所计算出来的id,跟Git和Mercurial的做法如出一辙。fossil - hash policy列出了fossil支持的哈希模式。
因此不难想象,Fossil和Git和Mercurial一样,都是基于哈希值来索引文件内容的。事实上,我们可以用fossil artifact
命令外加上uuid
来查看条目内容。针对第一个条目:
>fossil artifact fbfce048cc
C initial\sempty\scheck-in
D 2018-07-26T00:56:54.813
R d41d8cd98f00b204e9800998ecf8427e
T *branch * trunk
T *sym-trunk *
U (user) #此处隐去真实用户名
Z dfe7ed34b9d90707c7343523bdc1d524
可以看到有一条默认的条目。该条目的内容是具有某种格式(具体后面会涉及)的文本。大致可以猜到C initial\sempty\scheck-in
这一行是Comment行,内容是"initial empty check-in"。
试验:往fossil里面存入文件
> mkdir f1wd # 创建工作目录
> cd f1wd #进入工作目录
> fossil open ../f1 #在工作目录中打开f1仓库
接着我们来提交一个a.txt
文件:
> echo a > a.txt
> fossil add a.txt
ADDED a.txt
> fossil ci -m "add a.txt"
现在来查看一下仓库的内容:
sqlite3 ../f1
sqlite> select * from blob;
1|1|166|fbfce048cc789eaa58c4e02fe92f3be4a491ddfdb5de869527f8f960ed510d1b|
2|2|3|7076b0b947acfd64691706bb8d56ca04a35c8c1b966f42559777528516ecde95|
3|2|259|4f14ad79bec20100104662004cda4c804ed2999a7efaac928317a10fdc51774b|
可以看到blob
表一共有三个条目,比之前多了两条。让我们来看看多出的两条是什么内容:
>fossil artifact 7076b0b947
a
>fossil artifact 4f14ad79be
C add\sa.txt
D 2018-07-26T01:18:16.175
F a.txt 7076b0b947acfd64691706bb8d56ca04a35c8c1b966f42559777528516ecde95
P fbfce048cc789eaa58c4e02fe92f3be4a491ddfdb5de869527f8f960ed510d1b
R 641c0ab1e272e0762cba0388472b0cc1
U (user)
Z 04018b85cb966f1a7a059496a3c8af49
可以看出,第二个条目存储的是文件a.txt
的内容;第三个存储条目的内容和之前看到的第一个条目的有点相像,说明这个条目的格式很重要。下一个章节,我们来研究下这个格式。
Fossil的文件集和改动集
前面我们看到
>fossil artifact 4f14ad79be
C add\sa.txt
D 2018-07-26T01:18:16.175
F a.txt 7076b0b947acfd64691706bb8d56ca04a35c8c1b966f42559777528516ecde95
P fbfce048cc789eaa58c4e02fe92f3be4a491ddfdb5de869527f8f960ed510d1b
R 641c0ab1e272e0762cba0388472b0cc1
U (user)
Z 04018b85cb966f1a7a059496a3c8af49
这其实是Fossil的一个artifact,类型是manifest。这个manifest即是Fossil的文件集,又是Fossil的改动集,相当于把Git的Tree和Commit两种blob二合一了。
manifest的结构在fossil - file format里面有描述,它包含了以下几种元素(叫做“Card”或者“卡”):
- B baseline-manifest
- C checkin-comment
- D time-and-date-stamp
- F filename ?hash? ?permissions? ?old-name?
- N mimetype
- P artifact-hash+
- Q (+|-)artifact-hash ?artifact-hash?
- R repository-checksum
- T (+|-|*)tag-name * ?value?
- U user-login
- Z manifest-checksum
其中,B卡是用来支持manifest的增量存储。
C卡记录的是提交的注释,N卡用来标志注释的格式(Fossil可以允许注释有不同的格式?)。D卡记录的是提交的时间。F卡有多个,记录当前仓库版本中所有的文件名以及对应的uuid
。
P卡记录当前manifest的父版本。一般一个manifest又一个P卡,但是对于合并而来的manifest来说,则可以有多个。 Q卡和P卡有点类似,不过是用来支持CherryPick功能的。
T卡是用来给分支打标签。在Fossil中,分支和标签基本是同一个东西,参考fossil - Branching, Forking, Merging, and Tagging>
R卡是用来给包含在manifest所有的文件作一个checksum。如果文件数目多的话,这个计算量不小的。Fossil的解释是,它宁愿保守,也不愿出错,参考fossil - Fossil Repository Integrity Self-Checks。
U卡用于记录用户信息,Z卡用来记录当前manifest的checksum。
最后,此处是Fossil自身仓库中的一个manifest的示例。
小结
- 总的来说,Fossil和Git、Mercurial对于文件的处理都是一样的,都是将其哈希后存储,只不过存储方式有所不同。Fossil是基于SQLite,但是以fossil的设计,换做其他数据库问题也是不大。
- Fossil的文件集和Mercurial比较像,都是扁平化的,一个列表里面包含了仓库中所有目录下的文件。而不像Git那样,有tree类型的blob来表示目录。总的来说,扁平化的文件列表存储时会比较耗费空间,除非是用增量方式存储。扁平化的好处是处理起来比较方便,不必像Git那样递归查找目录。
- Fossil的文件集和改动集是一起的,都是由manifest表示。其实Mercurial也可以这么操作,虽然Mercurial的改动集由changeset表示,而文件集由manifest表示,但是两者同增同减,一致性很强。
(本篇完)