Skip to content

Import from Git

This guide walks you through importing an existing Git repository into TOVIO with tovio git import. It is a one-way, read-only walk of your Git history: TOVIO reads a local Git repository's object graph and writes it into a fresh TOVIO repository you create first. Your Git repository is never modified.

This works today

tovio git import shipped with the offline core. Everything on this page runs offline against today's CLI — no Forge, no network, no keys required.

Before you start

You need:

  • TOVIO installed — see Get started.
  • A local Git repository. Import reads a path on disk; it does not clone a URL. If your project lives on a remote, git clone it yourself first (the ordinary way), then point import at the clone.

That's it. Import runs fully offline against the local clone, and it only reads — so there's nothing to back up and nothing to undo on the Git side.

Import a repository

Import writes into the TOVIO repository it is run from, and that repository must be fresh (no commits, no lanes yet). So the flow is: clone your Git repo, make an empty TOVIO repo next to it, and import into it.

# 1. Get a local clone (skip if you already have one). Import reads this path — it does not clone URLs.
git clone https://github.com/acme/widgets.git

# 2. Create a fresh TOVIO repo and import the clone into it.
mkdir widgets-tovio && cd widgets-tovio
tovio init
tovio git import ../widgets

tovio git import <path> walks the full Git DAG, assigns a stable Change ID to each commit, and writes TOVIO's content-addressed objects into the current repository. When it finishes you are already inside a working TOVIO repository:

tovio status
tovio log

If you run tovio git import with no path, it imports the Git repository in the current directory (.). Two flags adjust how authorship is carried over: --anonymize replaces each author/committer with a stable pseudonym, and --mailmap <file> canonicalizes identities through a Git-style mailmap. Import also repacks the imported objects when it finishes (loose-object count is what makes a large imported repository slow afterwards); --no-repack skips that step so you can run tovio gc --repack later.

Try it on a throwaway copy first

Because import never writes back to Git, the safe way to learn it is to import a repo you care about into a throwaway TOVIO repo, poke around with tovio log and tovio diff, and delete the TOVIO repo if you don't like it. Nothing you do to the imported repo can reach the original.

What carries over

Import is designed to preserve everything that makes your history yours. Here's the full picture:

From Git Becomes in TOVIO Notes
Commit history (the full DAG) TOVIO commits, one per Git commit Merge commits and all parents are preserved.
Each commit A stable Change ID (chg:…) Derived deterministically from the Git commit hash (or recovered from a Tovio-Change-Id: trailer that a TOVIO export left behind), so importing the same history twice yields the same IDs; survives later history rewriting.
The original Git commit hash Recorded locally Written to .tovio/git-import/commits.tsv so you can trace a TOVIO commit back to its Git origin. This map is local to the imported repo — it is not yet carried in the synced commit object.
Local branches TOVIO lanes Mapped to TOVIO's CRDT lane refs. Remote-tracking branches, reflogs, notes, and stashes are not imported.
Tags TOVIO tags Tags are immutable in TOVIO, same as you'd expect. Annotated tags are peeled to their commit; a tag that cannot be mapped is listed as skipped in the import report.
Author, committer, dates, messages Preserved verbatim Your git log and your tovio log tell the same story (unless you pass --anonymize/--mailmap).

Change IDs are new — and that's the point

TOVIO gives every imported commit a Change ID (chg:<base32>) that is distinct from the commit hash. It is derived from the Git hash, so it is the same on every import of that history, but from then on it is the identity that survives rebase, amend, and land — operations that, in Git, change the commit hash and make a commit hard to track. The original Git hash is recorded in the local .tovio/git-import/commits.tsv map, so you never lose the link to where a commit came from; the Change ID is what lets TOVIO follow that work through every future rewrite.

If Change IDs are new to you, the core concepts explain why they matter.

Ignore files

TOVIO honors a .tovioignore file with the same glob syntax you already know from Git. Your .gitignore is carried over unchanged as an ordinary tracked file, and import also derives a .tovioignore from it.

Two things happen, and the second matters more than it looks:

  1. Every .gitignore is rebased onto the repository root. TOVIO reads only the root .tovioignore, so a nested one would have no effect. A pattern from sub/.gitignore is re-anchored with Git's own depth rule — build/ becomes /sub/**/build/ (any depth, as Git means it), while /exact becomes /sub/exact. Blocks are emitted root-first, so precedence between files survives the flattening.
  2. Dot-prefixed paths your history tracks are re-included. TOVIO ignores every dot-prefixed entry by default, but your Git history tracks .github/, .gitignore, and often .editorconfig. Without a ! for each, the first tovio status after an import would report them as deletions. Import writes those re-inclusions for you.

Review the result — if you relied on Git-specific ignore tricks, this is the place to double-check them. Anything that could not be carried across is listed in the import report with the reason. Pass --no-ignore-translate to skip the whole step and write the file yourself.

After the import

Your imported repository is a normal TOVIO repository. A good first loop:

tovio status          # where am I, what's modified
tovio log             # the history you just imported, with Change IDs
tovio log <path>      # scope history to one file
tovio diff            # current change vs. its parent

From here, the everyday workflow is the same as any TOVIO repo — commit, lane, switch, land. The everyday guides cover that loop in full.

Export and bridge are separate jobs

Import is one-way: Git → TOVIO. If you need commits to keep flowing both directions while some teammates stay on Git, that's a Git bridge — see Run a Git bridge. Writing a TOVIO repo back out through one-shot tovio git export and running explicit bidirectional bridge cycles are implemented.

Troubleshooting

\"git import needs a freshly-initialized repository\" (TVO-MIG-002)

Import refuses to run into a repository that already has commits or lanes, so imported history never tangles with hand-made work. Create an empty repo (mkdir … && cd … && tovio init) and import into that. To retry a partial or failed import, re-init a clean repo.

\"not a Git repository at …\"

The path you passed isn't a Git repository. Import reads a local clone — it does not clone a URL. git clone the remote yourself first, then point tovio git import at the resulting directory.

\"Cannot safely use repository path .tovio\" (TVO-CLI-021)

.tovio/ is TOVIO's reserved metadata directory, so a Git history that tracks a path under it cannot be imported as-is — the import stops at the first commit that contains one. Rewrite the Git history (a git filter-repo-style pass that drops .tovio/**) in a throwaway mirror, then import that mirror. Such a rewrite re-hashes the first affected commit and every descendant of it — a child's hash covers its parent's — so only the history strictly before the first .tovio commit keeps its original Git hashes in .tovio/git-import/commits.tsv.

The import is taking a while on a huge repo

Import walks the entire DAG and re-stores every blob in TOVIO's content-addressed format, then repacks the result. On a large, long-lived repository this is real work — the repack alone can take longer than the import, and --no-repack defers it. It only happens once; the result is a normal repo that commits incrementally from then on.

Did anything change in my Git repo?

No. Import only reads. There is no commit, no push, and no write to your Git remote anywhere in the process.

Where to go next

Last reviewed September 9, 2026

Suggest an improvement to this page Not for security reports — see disclosure