An anchor MUST identify the exact content it was captured against: the commit, the repository-relative file path, the file’s blob object id, and an optional 1-based inclusive line range, absent for a whole-file anchor. Creating an anchor MUST validate the path and the line range against the revision’s actual content.
docs/spec/anchor.adoc
The Anchor
A durable pointer into source: a blob, an optional line range, and a specific commit. Anchors resolve and project independently of any consumer — comments are merely the first client; reviews, TODO trackers, and blame overlays can reuse the same mechanism.
An anchor is authoritative at creation and MUST NEVER be mutated afterward. Its anchored text MUST be fully derivable from the blob and the line range and MUST be derived at read time, never stored redundantly. The anchored commit’s id MUST be recorded only as a plain data field (<<anchor.retention>>); nothing in the anchor mechanism itself pins it, so it MAY be garbage collected once nothing else keeps it reachable.
The document storing an anchor MUST embed the anchored blob, referenced by
the existing blob’s own object id rather than copied, and a context blob of
the surrounding source lines, written fresh, as ordinary tree entries in
the document’s own stored tree.
This embedding MUST keep the anchored content reachable from
refs/meta/* — surviving force-push, branch deletion, and gc — for as
long as the document’s ref exists, with no gc special-casing and no
pinned ancestry; content addressing makes the embed free.
This MUST NOT use a gitlink (mode 160000): a gitlink names a commit in
another repository and is not itself a reachability edge, so it would keep
nothing reachable.
Redaction (<<receive.redaction-ingest>>) is the sole deliberate exception to
this retention.
Displaying an anchor against any revision other than its own MUST be a read-time projection reporting one of four outcomes: current (the exact blob still sits at the anchored path), relocated (the file moved and/or the range shifted past edits outside it, reported with the new path and range), outdated (an edit touched the anchored region, or the entry is no longer a regular file), or deleted (the file is gone). Projection MUST follow renames and MUST work between any two commits — forwards, backwards, or across unrelated history — as long as the anchor’s own commit still exists. Projection MUST NEVER mutate the stored anchor.
Once the anchored commit has been garbage collected, projection MUST degrade to fuzzy-matching the retained context blob (<<anchor.retention>>) against the target commit’s version of the same path, reporting relocated (with the matched line range) on a good match, outdated on a poor one, and deleted when the path itself is gone — the same four outcomes, recovered approximately rather than exactly. An outdated or deleted projection MUST NOT lose the anchor: the original anchor remains displayable.
Capturing an anchor MUST support the working tree as its source: the file’s current on-disk bytes are written to the object database as a blob and embedded per <<anchor.retention>>, so an anchor to uncommitted content survives that content being committed, amended, or discarded. Such an anchor MUST record `HEAD’s commit in its commit field — the same best-effort, never-load-bearing data field <<anchor.immutable>> already makes it. Projection MUST support the working tree as its target, diffing the anchored blob against the path’s current on-disk bytes — or against a caller-supplied buffer standing in for them (<<lens.working-tree>>) — and reporting the same four outcomes as <<anchor.projection>>. A working-tree projection MAY degrade rename following to the context fallback (<<anchor.fuzzy-fallback>>): there is no commit on the target side to diff trees against.