Skip to content

The change model

The change is the concept at the center of TOVIO, and it is the one that most repays understanding. Get this and the rest of the system clicks into place: why there's no staging area, why rebasing is safe, why lanes are landing targets, why stacks just work.

This page is about why the model is shaped this way. For the commands, see Get started.

Two identities for two different things

Git gives a commit one identity — its hash — and uses it for two jobs at once: this exact snapshot of bytes, and this unit of work in my history. That conflation is the root of rebase anxiety. The moment you amend or rebase, the hash changes, and "the thing you were working on" appears to vanish and be replaced by a stranger. CI loses track of it. A teammate's review comment points at a commit that no longer exists. You force-push and hope.

TOVIO splits the two jobs into two identities:

What it names When it changes
Change ID (chg:xkqm7y) The unit of work Never — survives amend, rebase, split, absorb
Commit ID (blake3:…) The exact snapshot Whenever the content changes — like a Git hash

A change points at its current commit. Rewriting history produces new commits but keeps the change's identity.

Mental model

A change is a Git branch and its commits as one named unit of work — with a permanent name that history edits can't disturb. The commit is the snapshot; the change is the story.

Why the Change ID is random, not content-derived

A Change ID is a random 128-bit value, drawn from a secure random source at the moment work begins, rendered as chg: plus Crockford base32. It is deliberately not derived from content — and that is the whole point. If identity were a hash of content, it would change the instant you amended a typo, which is exactly the property we are trying to eliminate. Identity has to be stable under rewriting, so it cannot be a function of the thing being rewritten.

The trade-off is that random IDs aren't chronologically sortable. That's fine: ordering comes from the commit DAG and the Hybrid Logical Clock, never from the ID. The two namespaces — chg: for changes, blake3: for commits — are kept visibly distinct so a value is never ambiguous about which kind of thing it names. Like Git short hashes, tools accept any unambiguous prefix.

On Git import

When you import a Git repository, each imported commit is assigned a fresh Change ID, and the original Git hash is preserved as metadata. Your history keeps its provenance and gains stable identity.

No staging area: the working copy is the change

There is no index and no tovio add. The working copy is continuously snapshotted into the current change. On each command boundary (and optionally on a filesystem watcher event), TOVIO hashes only the paths that changed and reuses unchanged subtrees, so the snapshot is fast and atomic.

tovio commit doesn't "stage then commit" — it finalizes the current change and starts a new one. Granularity that you'd have reached for staging to get, you recover with explicit operations instead:

  • tovio commit --amend — fold more edits into the current change; the Change ID is unchanged.
  • tovio change split — divide one change into a landable sub-change and a work-in-progress remainder.
  • tovio change absorb — push fixup edits down into the changes that originally introduced those lines.

Because there is always a tracked snapshot, undo and crash recovery are trivial: there is never a moment where your work exists only in the editor.

Lanes are destinations; changes are workspaces

In Git you live on a branch. In TOVIO you live in a change, and a lane is a landing target — a name for "the integrated state of finished work." You don't sit on main; you land changes onto it.

This is what makes TOVIO trunk-based by idiom. Because the change is your workspace, you land changes continuously onto a small set of protected lanes rather than maintaining long-lived feature branches. Long-lived develop / staging / release lanes are still supported, but they carry the same integration debt they do in Git: the more a lane diverges, the more its eventual land costs.

Stacking: branching off a branch, without the pain

Because a change knows its parent — and a change's parent can be another change — you can stack changes:

main
 └─ chg:aaa  "add CGM client"          ← base of the stack
     └─ chg:bbb  "wire CGM into sync"  ← builds on aaa
         └─ chg:ccc  "CGM UI panel"    ← builds on bbb

You work on all three at once, switching between them with no stashing. When the base is ready, you tovio land my-lane --into main. Here is the part that dissolves the usual stacked-PR misery:

The land cascade

Landing the base of a stack automatically re-bases the changes above it onto the new lane state — and their Change IDs do not change. chg:bbb and chg:ccc are still chg:bbb and chg:ccc; they're just rebased. Reviews, CI runs, and references that point at them stay valid.

This is stacked development without the fragility: no manual rebase chain, no force-push storm, no re-pointing a tower of pull requests by hand.

History rewriting is a normal, safe operation

commit --amend, rebase, change split, change absorb — these are everyday, unremarkable operations in TOVIO, because the thing that made them scary in Git is gone. They produce new commits, but they keep Change IDs, and the operation log records every step so tovio undo can reverse any of them. The interface even says so out loud: a rebase reports that the Change IDs are unchanged, and a land that "your work kept its identity."

✓ Rebased chg:xkqm7y onto main (4 change(s) reparented, Change IDs unchanged) - undo with `tovio undo`

That single line is the cure for force-push anxiety. You are never one keystroke from losing track of your work.

Where to go next

Last reviewed September 9, 2026

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