Datahike and Geschichte deliberately expose two branch systems:
refs/heads/main) is repository data inside a workspace.Every filesystem or staging transaction creates a durable Datahike checkpoint. A user-visible Geschichte commit selects the checkpoint immediately before its commit-marker transaction. This gives autosaved work without placing every write in user-visible history.
Datahike: C0 -- write C1 -- stage C2 -- marker/ref C3 -- write C4
Geschichte: commit A -> snapshot C2
Git: commit OID -> projection of A
The canonical Datahike :db branch is deliberately headless: it is the serialized
publication authority, not a checkout. Every physical or virtual checkout has a
private Datahike branch, even the initial directory. Thus several agents can check
out refs/heads/main simultaneously without Git's shared-worktree restriction.
They commit locally, explicitly publish a fast-forward into :db, and explicitly
advance clean peer workspaces from it. The local .geschichte/workspaces.edn
registry maps projection paths to branches using locked atomic replacement; it is
recoverable coordination metadata rather than replicated repository history.
File bytes are immutable values in the database's Konserve store. Geschichte:
datahike.blob/blob-id;datahike.gc-guard/with-unreferenced-writes;k/bassoc;:db.type/store-ref datom;A crash can therefore leave an orphan eligible for GC, never a published dangling file reference.
Paths do not point directly at physical payloads. They point at logical content
entities identified by the hash of reconstructed bytes. A content entity records
:full or :line-delta, its store-ref payload, optional base-content ref, logical
size, and bounded delta depth. Consequently a snapshot, diff, merge, or Git export
observes the same content ID regardless of compaction decisions. Invalid UTF-8 and
NUL-bearing files remain byte-exact full payloads.
:geschichte.commit/snapshot is currently a UUID. Before history-window GC is
enabled it should become a generic Datahike commit root, tentatively
:db.type/commit-ref. GC and replication must follow such values to the stored
commit record, its indices, its payload store-refs, and retained parents.
geschichte.fs exposes the Datahike working tree without depending on Muschel,
dvergr, or a physical checkout. Hosts adapt its byte-oriented operations to their
filesystem protocol. Directory nodes are workspace state: parent directories are
derived from file paths and explicit empty directories live in Datahike, but only
files enter Geschichte/Git commits.
agent shell / SCI loader
|
v
host FS adapter (for example muschel.fs/FS)
|
v
geschichte.fs -- byte I/O, stat/list, mkdir, rename, delete
|
+--> geschichte.repo -- worktree, index, refs, commit DAG
| |
| +--> Datahike versioning -- checkpoint, branch, merge, as-of
| |
+--> geschichte.content --+--> Konserve raw blobs/full + line deltas
Git HTTP/SSH <--> object/pack/protocol adapters <--> the same commit DAG
Native .git import enters at the exact-object side of this boundary. A JVM
adapter discovers normal/bare/linked Git directories, follows commondir and
object alternates, imports loose objects and complete packfiles, interprets refs
and config, and then invokes the same lazy checkout used after HTTP/SSH fetch.
The native Git index is intentionally not copied: Geschichte reconstructs its
own clean index from the selected commit, while an in-place import requires an
explicit overwrite opt-in. Exact Git objects remain available for round trips.
This is now the dvergr integration seam. Muschel's Geschichte adapter exposes the
repository as a virtual root, its integrated git uses the same command engine,
and dvergr routes shell builtins, SCI loading and file I/O, structured tools,
skills, and app serving through that root. The Yggdrasil adapter advertises
snapshot, branch, graph, merge, and commit capabilities without inventing a fake
physical path. External native processes remain an explicit projection boundary.
Portable semantics live in CLJC and operate on native platform buffers: JVM
byte[] and CLJS Uint8Array. The shared byte facade performs bulk slicing and
concatenation, while codec hot loops retain direct primitive array access through
reader conditionals. This avoids persistent-vector conversion and per-byte
protocol dispatch.
The first Node gate covers Myers/unified diff, canonical Git SHA-1/SHA-256 object
identity, binary tree parsing, pkt-line, protocol-v2 and receive-pack framing,
sideband reconstruction, commit ancestry, and three-tree merge planning under
advanced compilation. Node uses native crypto; JVM stream reads remain a thin
platform-only extension to the portable pkt-line codec.
Repository and storage mutation are the next portability boundary. JVM Datahike
calls are synchronous, while Node/browser create, connect, transact, branch, and
store-ref operations are asynchronous. Pure queries and merge/diff interpretation
remain synchronous. Geschichte will follow Datahike's platform-default :sync?
model rather than making pure algorithms asynchronous.
geschichte.query is the first database-backed portable slice. Node tests create
and connect asynchronously, install the full schema and transact repository data,
then query repository identity, refs, commits, worktree/index metadata, content
representation, and imported Git catalogs synchronously from @conn. The same
views accept historical values returned by commit-as-db, so a browser UI does
not need separate Git-shaped indexes to interpret a synchronized repository.
Effectful portable APIs use partial-cps, matching Yggdrasil and Muschel's async
interpreter branch. geschichte.macros/async+sync compiles one body to direct JVM
code by erasing async/await, or to an awaitable CPS value on CLJS. Datahike and
Konserve currently return core.async channels for asynchronous operations;
geschichte.async/io-result uses partial-cps's optional channel bridge so channels
remain an implementation boundary rather than Geschichte's public composition
model. JavaScript Promises have a similarly small CPS adapter.
This preserves the existing synchronous Clojure API and its fast path while letting Geschichte operations compose directly inside the partial-cps Muschel interpreter. Browser or Node shell I/O still requires Muschel's CLJS filesystem/host adapters; repository use and synchronization do not depend on the shell being present.
git index-pack --stdin.git fsck differential tests.ls-refs and fetch. Started:
strict pkt-line framing, capability advertisements, and transport-independent
ls-refs/fetch request construction plus ls-refs response parsing are
implemented. Fetch responses reconstruct sideband pack streams and progress.
Native git upload-pack accepts the generated ls-refs and fetch requests.
An end-to-end differential run fetched 57 objects (including 11 deltas),
reconstructed the sideband pack, and persisted all objects into Datahike.unpack ok); the resulting bare repository passes git fsck --strict and resolves the expected HEAD.Smart HTTP is a thin injectable transport: it constructs discovery and RPC
requests, validates status/media types, removes the service prelude, and delegates
all protocol bytes to the same fetch/push paths used by native-process differential
tests. Java's HTTP client is the default; tests substitute an in-memory sender.
Native git http-backend validation exercised discovery and fetch end to end,
importing 103 objects (36 REF_DELTA entries) and both HEAD/main refs.
The same backend accepted a Geschichte-projected commit and pack over smart HTTP,
returned unpack ok, updated main, and produced a repository passing strict fsck.
SSH uses a stateful process seam: stream the service advertisement through its flush packet, write one protocol command on the same process, then consume the response. Command construction is argv-based with an escaped remote repository argument; the same seam launches local Git services for differential tests. Local stateful validation fetched 127 objects (45 deltas). The receive-pack side uses the same advertisement/request session and supports Geschichte push over SSH.
Pull compares exact imported/projected Git ancestry before touching the worktree.
It reports up-to-date and local-ahead states, materializes a true fast-forward, or
rejects divergence under :ff-only. With :merge, the remote tip is materialized
under a temporary logical ref and fed into Geschichte's two-parent merge planner.
7. Optimized pack delta selection sharing Geschichte's byte-match kernel.
Imported Git commits may remain object records until checked out. Materializing every commit of a very large foreign history as a Datahike checkpoint is optional, not a requirement for preserving or pushing its Git OID.
Fetched refs live in a separate Git ref catalog (refs/remotes/...) and point at
OIDs. On checkout, Geschichte parses the selected commit/tree graph, creates
lightweight logical metadata for its ancestors, writes only the selected tree into
the versioned filesystem, and pins that one Datahike checkpoint. Selecting an
ancestor later can fill its snapshot lazily without changing its Git mapping.
The native differential path currently fetches Geschichte's own four-commit Git
history (70 packed objects, including 18 deltas), imports it into a fresh Datahike
database, materializes HEAD's 28 files, and reproduces the four-message log with a
clean worktree.
The inverse path is implemented at the object-graph boundary: a Geschichte commit and its reachable parents project to canonical blobs, trees, and commits; the graph can be persisted as exact Git payloads keyed by OID, with commit-to-OID mappings. Pack and wire adapters can therefore operate on Git objects without coupling their identity to Geschichte's physical full/delta content representation.
Merge-base traversal operates on Geschichte's explicit parent graph. Three-tree planning compares path metadata containing logical content IDs, not physical payload IDs, and implements the standard unchanged-on-one-side resolution rules. Conflicts retain base/ours/theirs values. Clean plans can replace the worktree and index, record a pending second parent, and produce a two-parent commit. Persisted conflict entities, textual conflict resolution, rename detection, and recursive criss-cross merge bases are the next layer; the pure planner keeps those policies testable and filesystem-independent.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |