Software engineers use version-control systems (VCS) to share the soft- ware code between the developers and to maintain the history of all the previous versions of the software. A developer checks out the code from the central version-control system, creates his modifications, and finally commits them to the VCS, thus creating a new version. In this sense, version-control systems are in fact transaction-time databases. Most version-control systems allow software engineers to create branches on the version history, so that different teams can work with different fea- tures without disturbing each other. Version-control systems are therefore fully persistent. Examples of version-control systems are the concurrent versions system (CVS) [20], Subversion [22], and Git [30].
The main difference between version-control systems and traditional databases is in their usage. Multiversion database indexes are used to lo- cate data item tuples based on a single key or a key range, by either fixing the version to a single point or by specifying a range of versions (recall the different query types from Section 2.3). Version-control systems, on the other hand, are used for much more specific queries, such as “find all the differences between versions v1 and v2” (with v1 and v2 possibly on totally
different branches), “find all the changes to the data item identified by k”, or “find the version during which the line l was changed in the text file identified by k”. These are queries that could not be effectively answered using a traditional database index. A multiversion database system could, however, conceivably use an existing version-control system for indexing the multiversion data items, and it is therefore worth it to examine these systems a little further to determine whether they would be efficient for this purpose.
3.8 Version-Control Systems
Fendt has evaluated the performance of some of the more recent version-control systems in an online article at the Linux Foundation [29]. He shows that the Git version-control system is the fastest one, alongside with another VCS called Mercurial. We will therefore concentrate on Git. The Git version-control system is based on indexing objects by the SHA-14 hash values of their contents [30, 93]. The objects are stored in
the file system, named with the hexadecimal representation of the SHA-1 hash value. The file system therefore works as the primary index for the stored objects. Four types of objects can be stored in the Git database: blobs, trees, commits, and tags. The blob type is used to represent any object that is stored in the Git VCS, and so every file that is added to Git is stored in a blob object. Tree objects are used to maintain directory hierarchies, commit tags represent commits (i.e., database versions), and tags are used to assign cryptographic tags for other objects (e.g., for verifying their contents).
Each tree object logically represents a single node and all its children in a directed acyclic graph. The tree object is basically a collection of pointers to objects stored under that node. A pointer that points to an object o is stored in a tuple of the form (m, t, s, n), where m is the file system mode of the file that represents o, t is the type, s is the SHA-1 digest of the object o, and n is the name of the object. Subdirectories are maintained by storing pointers to other tree nodes. To proceed from a tree node x to its child node, we find the correct child pointer(m, t, s, n), and use the SHA-1 hash value s to retrieve the child object from the index (i.e., by loading the file named s from the file system). Different versions are created by possibly creating new tree objects for that version. Any child object (e.g, a text file) that has changed has an updated SHA-1 hash value, because the hash is based on the contents of the stored objects. This in turn changes the tree object, because the tree object contains the SHA-1 values of its children in the pointer records. The changed tree object again needs to be indexed with a different SHA-1 value, and thus a change propagates all the way to the root of the directory structure. Unchanged nodes can however be efficiently reused between different versions. The Git VCS therefore relies on a form of path copying (see Section 3.5, p. 39). A straightforward way of using the Git version-control system as a multiversion index structure would be to store each key in a single tree object. A binary search could then be used to quickly locate the searched key among the pointers stored in the tree object. The single tree object 4SHA-1, the Secure Hash Algorithm, is a cryptographic hash function that com-
would then work as a dense index for the stored objects. While querying would be efficient, note that updating any key necessitates duplicating the entire tree object, thus duplicating the entire indexing portion of the multiversion database. The update performance and the space usage of this structure would be suboptimal. Also note that this kind of a structure could not be used as a sparse (primary) index structure, because all the child objects (data items stored in the database) must be stored in separate files. As a conclusion, the version-control systems are designed for maintaining versions of files and file directories, and they are not really suited for usage as general-purpose database indexes.