原文地址 http://www.fossil-scm.org/index.html/doc/tip/www/tech_overview.wiki
- 介绍
在最底层,一个Fossil仓库有一组未排序且不可变的"artifacts(神器、远古物件、工件)"组成。你可以将这些神器视为“文件”,因为在大多数情况下这些神器就是文件。但是其他的“控制工件”也混合其中。这些控制工件定了工件之间的关系 - 也就是构成项目特定版本的文件,它含谁检入了哪个版本,何时,何物被检入,注释是什么,项目包含什么维基页面,每个维基页面的编辑历史有哪些,包含了什么BUG报告或者标签,每个标签的由谁贡献处理以及其它信息。这个底层级的文件格式叫做仓库的“全局状态”,因为这些信息会在使用推送push和拉取pull命令在同等仓库之间同步。这个底层文件还可以叫做“持久层”,因为它将会保持存在很多年。如上关于底层、持久层、全局文件格式已经分别进行了阐述。
这篇文件是关于Fossil当前是如何实现的介绍。为了说明“持久数据格式”及其它文件,我们不使用抽象的概念,在这篇文件里提供了关于Fossil如何在磁盘上存储信息的详细说明。
- 三个数据库
Fossil在SQLite数据库文件里存储状信息。SQLite是一个完整的关系数据库,包含多个表及索引,且在一个磁盘文件里。SQLite库允许数据库文件可以使用工业标准的SQL语言进行有效地进行查询和更新。SQLite更新是原子级的,所以在系统崩溃或者电源故障时仓库内容任然会被保护。
Fossil使用三类分离的SQLite数据库:
- 配置数据库
- 仓库数据库
- 检出数据库
配置数据库每个用户都有一个,该数据文件保存了Fossil的全局配置信息。每个项目只有一个仓库数据库。仓库数据就是人们一般引用的文件,叫它“一个Fossil仓库”。检出数据库可以在项目的工作目录里找到,它唯一地包含了当前工作目录检出的状态信息。
Fossil不会一直都使用三个数据库文件。比如网页界面为代表它只使用仓库数据库。如果使用--global选项那么fossil setting命令只会打开配置数据库。但是其它命令每次都会使用所有这三类数据库。比如,fossil status命令首先找到检出数据库,然后使用检出数据库来查找仓库数据库,最后在打开配置数据库。不管何时使用多个数据库,它们都使用SQLite的ATTACH命令创建一个数据库练级。
下面的流程图提供了Fossil的三个数据库的概览及其使用了什么,后面将详细讨论。
2.1 配置数据库
配置数据库保存了跨仓库的设置选项以及当前用户的所有仓库清单。
Fossil的setting命令可以用来设置多种运行参数以及Fossil仓库的设置选项。设置可以应用到一个单独的仓库,或者可以为一个用户设置全局选项。如果全局及某一仓库值都存在,那么仓库设置的值优先。所有的设置的默认值都是有缘由的,所以大多数用户一般不需要修改它们。但是如果期望对设置进行修改,配置数据库提供了一个单独的命令来为所有仓库进行设置修改,而不用为每一个仓库进行修改。
配置数据也维护了一个仓库的清单。这个清单会被Fossil的all命令使用来为一个用户对所有的仓库批量运行如“sync”和“rebuild”等命令。
在Unix系统里,配置数据库的的名称是".fossil",它保存在用户的主文件夹内。在Windows里,配置数据库的名称是“_fossil”(使用下划线取代点),位于系统环境变量LOCALAPPDARA、APPDATA、HOMEPATH所指定的文件夹内,就是这样。
2.2 仓库数据库
仓库数据库一个通常认为是“仓库”的文件。这是因为仓库数据库在其它东西之外包含项目的完整版本、标签、维基历史等。它的名称可以用户自定义一般是项目名称,且有一个后缀名".fossil"。比如,Fossil本身的仓库名称为“fossil.fossil“,SQLite的仓库名称为”sqlite.fossil“。
2.2.1 项目全局申明
仓库数据库大部分(大概75到85%)有项目的持久层,全局,共享规则等工件组成。工件作为BLOB组成,使用zlib进行压缩,在合适的地方使用delta-压缩。结合zlib和delta压缩的效果是节约存储空间。对于SQLite项目来说,当写刚文章的时候,全部工件的大小约为2.0GB,需要感谢内建的zlib和delta压缩的是,在仓库数据库这些内容仅使用了32MB,压缩比例大约为64:1。数据库的BLOB内容的大小平均500比特。
需要注意的是zlib和delta压缩不是Fossil文件格式固有的组成部分;它只是一个选项。Fossil中持久数据是未序列化的工件。当前Fossil实现中压缩技术的使用只是为了高效地在磁盘上存储工件。
所有原始未压缩未DELTA压缩的工件可以使用Fossil的deconstruct命令从Fossil仓库数据库解压出来。个别的工件可以使用artifact命令解压。当使用原始SQL和Fossil的SQL命令连接仓库数据库时扩展函数”content()“及一个单独参数SHA1(工件的哈希值)将会返回该工件的完整未删除和未压缩的内容。
另外一个方式,fossil的reconstruct命令将会扫描文件夹层级并将所有发现的文件添加到新的仓库数据库里。Fossil的import命令用来读取输入git-fast-export文件流,使用它构建对应的工件,工件随后将会被写入仓库数据库。
2.2.2 项目元数据
仓库数据库里的项目全局申明信息使用计算好的元数据增补,这样使得查询项目元数据更加高效。元数据包含了如下信息:
- 任何检入所发现的所有文件的文件名
- 所有修改已有文件的检入
- 每个检入的父级和子级
- 潜在的时间线行数据
- 所有符号标记的名称,及其对应的检入对象
- 所有维基页面的名称及组成每个维基页面的工件
- 标签或维基页面所对应的附件
- 每个标签的当前内容
- 标签、检入和维基页面之间的跨域参考
在仓库数据库里元数据保存在多个SQL表中。元数据设计的目的是为Fossil生成时间线和报告时可以高效地进行查询。随着Fossil功能的拓展,元数据的结构可以且一定会更改。但是结构的更改不会使数据库变得无效。 请记住元数据不会包含新信息 - 只有从规范的工件解压出来的信息,并以更加可用的格式保存。因此,当元数据的结构更改,之前的元数据可以抛弃,完整的元数据集合可以从规范工件从重新计算得来。其实这就是fossil rebuild命令所做的事情。
2.2.3 显示和执行的选项参数
仓库数据也保存为特定仓库保存的配置设置页面显示格式以取代全局数据库所对应的信息。所有这些信息(也含用户认证和权限)每一个仓库都有本地存储;它们不会再使用fossil sync命令时与其他仓库共享这些信息。那时因为有充足的理由认为相同项目的二个不同的网站可能有完全不同的显示界面选项和用户群体。项目的一个实例可能是另外一个项目的分叉,比如,从它处拉取的仓库从来不会推送或者扩展项目,这样的话其它网站的所有者不会赞成。
显示和执行信息包含如下信息:
- 项目的名称和描述
- 所有页面使用的CSS文件、页头、页脚
- 项目Logo图片
- 认为是”重大“的标签域,从何处采集 - 工件,是否可显示
- 查看、编辑和创建标签的图形模板
- 标签报告格式及显示选项
- 每一个用户的本地设置用来替换全局值的配置数据
尽管显示和执行选项参数不会在使用fossil sync时在仓库之间移动,但是这些信息可以使用fossil config push 和 fossil config pull来在仓库之间进行共享。显示和执行信息在使用fossil clone时也会被复制到新的仓库。
2.2.4 用户证书和权限
仅仅因为二个开发团队在一个项目上进行协作并允许他们在二个仓库之间使用push、pull命令,并不意味着他们信赖彼此而可以共享他们的密码和连接权限。因此用户的名称、邮箱、密码和权限这些隐私信息会保存到每一个仓库的本地。
每一个仓库数据库都有一个表保存认证用户的用户名、权限和登陆证书来关联指定的数据库。附件的,有一个名为”concealed“的表匹配了每个用户的邮箱地址的哈SHA1哈希值到真正的用户邮箱地址。Concealed表只允许在标签中保存SHA1哈希值的邮箱地址,这个就阻止了垃圾邮箱制造者获得这些真实的邮箱地址。
表user和concealed的内容可以使用fossil config push和fossil config pull命令进行推送和拉取,且需要将”user“和”email“作为AREA参数提交,且你拥有远程仓库的管理权限。
2.2.5 回避的工件列表
项目的标准工件集合 - the global state for the project - is intended to be an append-only database. In other words, new artifacts can be added but artifacts can never be removed. But it sometimes happens that inappropriate content is mistakenly or maliciously added to a repository. The only way to get rid of the undesired content is to "shun" it. The "shun" table in the repository database records the SHA1 hash of all shunned artifacts.
The shun table can be pushed or pulled using the fossil config command with the "shun" AREA argument. The shun table is also copied during a clone.
2.3 检出数据库
与其他流行的几个DVCS系统不同的是,Fossil允许一个仓库有多个工作检出。每个工作检出的根目录都有一个单独的数据库,它保存了当前检出的状态。检出数据库的名字默认为”_FOSSIL_“ ,但是如果想可以重命名为”.fslckout“。(将来的版本中可能将”.fslckout“作为默认的检出数据库名称)。检出数据库保存了如下信息:
- The name of the repository database file.
- The version that is currently checked out.
- Files that have been added, removed, or renamed but not yet committed.
- The mtime and size of files as they were originally checked out, in order to expedite checking which files have been edited.
- Other checkins that have been merged into the working checkout but not yet committed.
- Copies of files prior to the most recent undoable operation - needed to implement the undo and redo commands.
- The stash.
- State information for the bisect command.
For Fossil commands that run from within a working checkout, the first thing that happens is that Fossil locates the checkout database. Fossil first looks in the current directory. If not found there, it looks in the parent directory. If not found there, the parent of the parent. And so forth until either the checkout database is found or the search reaches the root of the filesystem. (In the latter case, Fossil returns an error, of course.) Once the checkout database is located, it is used to locate the repository database.
Notice that the checkout database contains a pointer to the repository database but that the repository database has no record of the checkout databases. That means that a working checkout directory tree can be freely renamed or copied or deleted without consequence. But the repository database file, on the other hand, has to stay in the same place with the same name or else the open checkout databases will not be able to find it.
A checkout database is created by the fossil open command. A checkout database is deleted by fossil close. The fossil close command really isn't needed; one can accomplish the same thing simply by deleting the checkout database.
Note that the stash, the undo stack, and the state of the bisect command are all contained within the checkout database. That means that the fossil close command will delete all stash content, the undo stack, and the bisect state. The close command is not undoable. Use it with care.