What is this?
ftgr is a tool made for a very specific purpose: Take a
Fossil repository that has only had PGP-signed
commits from one person (possibly with multiple keys), and produce
a semantically equivalent Git repository. The
Git repository's commits will have the same dates and
signatures as the original
Fossil commits (and the dates inside
those signatures will also match).
$ mvn clean package
Compilation produces a jar file containing all of the dependencies.
$ java -jar target/io7m-ftgr-*-main.jar usage: ftgr.conf [logback.xml]
The program accepts a configuration file and an optional Logback configuration file to control logging. The default is to log everything.
ftgr configuration file is in Java Properties
# Absolute path to fossil executable com.io7m.ftgr.fossil_executable = /usr/bin/fossil # Absolute path to git executable com.io7m.ftgr.git_executable = /usr/bin/git # Absolute path to GPG executable com.io7m.ftgr.gpg_executable = /usr/bin/gpg # Absolute path to faketime executable com.io7m.ftgr.faketime_executable = /usr/bin/faketime # True if no changes should be written to disk com.io7m.ftgr.dry_run = false # True if repository verification should be performed (See "Verification" below) com.io7m.ftgr.verify = true # Absolute path to the Git repository that will be created com.io7m.ftgr.git_repository = /tmp/output # Absolute path to the input Fossil repository com.io7m.ftgr.fossil_repository = /tmp/input.fossil # See "Commit mappings" below. com.io7m.ftgr.commit_map = /tmp/output-commits.txt # See "Name mappings" below. com.io7m.ftgr.name_map.someone = Some Oneemail@example.com com.io7m.ftgr.name_map.someone_else = Some Onefirstname.lastname@example.org
Fossil uses short usernames to mark each commit.
tends towards full names and email addresses in each message. In
order to produce the latter from the former, the configuration file
requires the definition of name mappings. For a given username
com.io7m.ftgr.name_map.u defines a name and email address
separated by the pipe symbol
(U+007C). It is an error to fail
to provide a mapping for a
Fossil username: The program will
fail loudly before attempting to write any data if one or more are
In order to allow for verification of commits at a later date,
saves a log of the
Git commits made for each
Fossil commit. The
resulting log consists of one mapping
m per line, where
m has the
gc is a the SHA-1 hash of a
Git commit, and
fc is the
SHA-1 hash of a
Verification of commits proceeds by checking out each
mentioned in the recorded commit map, and then checking out the
Fossil commit and checking that the same files with
the same contents are present in both commits.
Git use a similar internal model: A directed acyclic
graph of immutable objects representing files, directories, and
commits. Therefore, it's a fairly simple case of transforming one
to the other by literally performing each commit in a
as it was performed in the original
Fossil repository, with minor
adjustments to account for differences in how merges and branches are
Read all commits from the
Fossilrepository into a directed acyclic graph structure, keeping track of the
commentof the original commits. The vertices of the graph are the commits and the edges of the graph are the parent → child links.
Fossilimplicitly marks the first commit of each branch; the details of how that happens aren't important here, just the fact that it's possible to know unambiguously whether a commit was responsible for "creating" the branch or not.
Fetch the manifest for each commit, and extract the key ID of the PGP key used to sign the manifest. Maintain a list of all keys used.
Check that the running user has the private key for each of the listed keys above. Give up if any are missing.
Check that a mapping exists from all
Fossiluser names to
Gitauthors. This is so that the author/committer user names in the original commits can be transformed into the conventional
An A Author <email@example.com>format. Give up if any are missing.
Create an empty
Gitrepository. Create an initial unsigned root commit consisting of a single
.gitignorefile that hides any relevant
Fossilrepository into the
Gitrepository directory. This allows
Fossiloperations to be performed by executing the
fossilcommand line with that directory as the current working directory.
Order the commits by time. Oldest commits come first.
For each commit
cwas the first commit of a branch, create a new
chas two parents, merge the current branch with whichever of the two parents are on the other branch.
- Otherwise, checkout commit
Fossil, replacing all files in the current directory (and removing all files that are not part of the commit). Switch
Gitto the relevant branch. Add all files to the
Gitindex and commit using the original message, time, and signing the result with the original PGP key.
faketime is used to execute both
allows for new commits to have the exact times as specified in the
original commits. Amusingly, it also allows for the use of expired
keys in GnuPG.