What this project has had to understand about Fossil's git export and import, beyond Fossil's manual: the points that changed a design or caused a failure. The rest of what it has learned about Fossil is in docs/fossil.md.
Git export and import
Tried with Fossil 2.27 and a scratch git repository, 2026-10-07 (f3bb7b2032):
fossil git export DIRwrites a git repository inDIR/.gitand what its next export needs inDIR/.mirror_state/db:mconfig(the main branch, the autopush) andmmark, each check-in's hash beside the git commit it became. It recordsDIRin the repository'slast-git-export-reposetting, and runs again into it given noDIR;--if-mirroreddoes nothing for a repository with none. It pushes only where--autopushsaid, a setting the export keeps.fossil git statussays where the export is, when Fossil last exported, its autopush, without any password its URL holds, and main branch, andN check-ins awaiting exportorup-to-date.git fast-export --allnames a commit by the first ref that reaches it, a tag before a branch: it wrotemain's history underrefs/tags/v1, sofossil import --gitput those check-ins on no branch, with only the newest onmain, and tagged every check-intrunk. An import names branches first.- A plain
fossil import --gitthenfossil git exportrewrites every commit, keeping each tree: authors become their e-mail, times move a second, zones are dropped, each message gains aFossilOrigin-Name:line, the check-ins on no branch come back on a branchunknown, and an annotated tag comes back lightweight. - Seeded, the export keeps the upstream's commits. Export marks from
both sides (
git fast-export --export-marks,fossil import --git --export-marks), joined on the mark intommark, and the git repository holding the upstream's objects, make the first export add nothing. A Fossil check-in then exports as a commit whose parent is the upstream's own, and a clone of the upstream fetches it as a fast-forward. Itsmconfigkeystart, the newest exported check-in's Julian day, is whatfossil git statuscounts awaiting check-ins from. git fast-export --brancheswith no tags gives every check-in its branch, andfossil git exportleaves refs it does not know as they are, so an upstream's annotated tags stay as they were.fossil import --gitreads its standard input given no file, so git's fast-export goes straight into it through a pipe and no stream is kept: beagleboard-org's, 3.0 GB packed on GitHub with 527,641 commits, passed 8 GB as a file within ten minutes (tried 2026-10-21; HISTORY.md decision 120).fossil import --gitwrites each check-in's whole manifest, every file of its tree, not a delta manifest, whatever git's commit changed: a November 2018 commit of beagleboard-org, one of 453,383 that each changed one file, lists 10,779. Its cost follows the commits times the tree, not the pack: that upstream, 3.3 GB packed, took 13 h 17 min of CPU and made a 10.6 GB repository, its temporary tables 1.1 GB; therefossil artifactread that November check-in's manifest in 5 min 20 s, the tip's in 16 ms (tried 2026-10-22; HISTORY.md decision 120; docs/backlog.md).- Its last step is a VACUUM unless given
--no-vacuum: SQLite copies the whole repository into a temporary file in its temporary directory (SQLITE_TMPDIR, else/tmp), deleted as it is opened, so only/proc/PID/fdshows it; in a sandbox's/tmp, a tmpfs, it is held in memory. A 180 MB import was the same size after a VACUUM as before, so an import leaves it out (tried 2026-10-21). fossil import --gitkeeps git's branch names,masteramong them, and a repository with notrunkopens a checkout on its newest check-in, whatever its branch.--refspec refs/heads/main:refs/heads/trunkon the fast-export names the upstream's own branchtrunkin Fossil, and the export, its main branchmain, names it back.