hav
Concepts

Rewriting history

History curation in hav is cheap, ordinary, and safe, because identity (knots) is separate from content (snaps) and because conflicts are data. This page covers the five curation verbs and the machinery that makes them safe: the auto-rebase cascade.

The verbs

edit: change an old knot in place

hav edit <knotish> moves your workspace onto an existing knot. From then on, your captures append versions to that knot, editing history directly instead of stacking fixups. Every snap-changing capture triggers the cascade (below), so tied descendants follow automatically.

edit pins the workspace (it stops tracking a buoy) and enforces one workspace per knot. If another workspace already holds the knot, it refuses, and the escape is hav new on top with a squash later.

squash: fold a knot into its parent

hav squash [<knot>] folds a knot into its first parent (or base): the parent takes the child's snap, descriptions concatenate, and the child is retired, meaning hidden from log and never deleted. The parent's other descendants cascade.

split: additive, by file or by hunk

hav split --take <path>[@N]... --into-new -m <msg> moves selected work, either whole files or individual hunks by their path@N tags from st and diff, into a new knot beneath the current one. The remainder stays where it was. You select what leaves, not what stays.

The result is an untied stack, and a later bare tie lands the whole stack, bottom-up, in one command.

absorb: route fixes to the knots that own the lines

hav absorb takes the workspace knot's edits and distributes them into the untied ancestors that own the touched lines, per-line blame style. The rules:

  • Never guesses. A change moves only when every replaced line has exactly one unambiguous owner. Anything ambiguous, conflicted, or owned by tied history stays put, and whole-file additions and deletions always stay.
  • Never touches landed history. The candidate chain stops at the first tied knot or workspace-held knot.
  • Never changes your tree. Only attribution moves, and working-tree bytes are identical before and after. Your knot's diff shrinks to just its own work.

discard: abandon a draft

hav discard abandons the workspace's untied draft: the tree reverts to the draft's base, a fresh empty draft takes its place, and the abandoned knot is retired. It refuses tied history (dropping landed history is move, pointer surgery stated as such) and drafts with knots stacked on them. Like everything else, it is one op, so hav undo brings the work back, files included.

The auto-rebase cascade

Whenever a knot's snap changes under descendants, whether from a capture on an edited knot or a squash fold, every descendant re-merges automatically, atomically with the change:

  • Each descendant 3-way merges its own content onto the rewritten ancestor, its ancestry edge re-pins to the new snap, and the process recurses while snaps keep changing.
  • Conflicts are stored as data inside whichever descendant they arise in. The cascade never stops, never asks, and never leaves a half-rebased state. (Conflicted inputs flatten first; conflicts never nest.)
  • Buoy heads keep their ids. Buoys point at knot ids, and knot ids are stable, so heads absorb rewrites without moving.
  • The whole cascade records inside the triggering command's op: one undo restores everything.

Workspaces are never cascaded

The one exclusion is the files-never-move principle again: knots held by a workspace are skipped, with a warning. That workspace's base is now stale, its st says base kn-… rewritten — hav sync, and an explicit sync refreshes it in place. Nothing ever auto-rebases under a running build.

Unheld knots, such as tied history or a split stack's intermediates, have no files and rebase freely.

What "retired" means

Squashed and discarded knots are hidden from log, not destroyed. Their objects remain reachable through the op journal until they age out of retention, and hav sweep is the only thing that physically frees them. Nothing in hav's curation vocabulary deletes bytes.

On this page