Releases: Spheroman/newgit
Release list
v0.2.0
Added
-
[[render]]on a resource substitutes per-instance values into a file the
project commits (#10).[ports]reached commands as{{ports.x}}and as an env var, which assumes
the tool takes its port on argv. Most do not: Supabase reads
supabase/config.toml, Expo reads.env, Compose readscompose.yaml.
Everyone who hit this wrote the same section-aware config rewriter inside
theirpreparehook.[[render]] path = "supabase/config.toml" replace = [ { find = 'project_id = "faretable"', with = 'project_id = "faretable-{{branch.slug}}"' }, { find = "port = 54321", with = "port = {{ports.api}}" }, ]
There is no template file.
port = 54321is the project's working default,
so a clone without newgit still starts on it; newgit substitutes into the
committed content and writes the result into one workspace.findis a
literal string, never a regex, and must match exactly once — which is
also the drift detector: when the default changes upstream, the bind fails
naming the file and the string instead of quietly doing nothing. A
multi-linefinddisambiguates two sections sharing a value, andcount = N
declares a genuine repeat.A render reads committed content —
HEAD, or the bound lane rev for a
tracker-owned path — never the working file, so it is idempotent:undo
andtracker pullre-render off the binding record and values never
compound.Instance values stay out of everything downstream. Source-owned targets are
marked--skip-worktree, so they never show ingit statusandgit add -A
cannot commit them;newgit exportand a checkpoint's uncommitted-state
capture both take them fromHEAD; andnewgit tracker capturereverses
the substitution for tracker-owned targets, so a key you add to.env.local
reaches the shared lane and this instance's port does not.On a source-owned path, skip-worktree also means real edits to that file in
this workspace do not survive it. newgit reports that precisely rather
than as a caveat: a render is a pure function of committed content and the
binding record, so the expected bytes are recomputable, and newgit compares
against them at checkpoint and before every re-render — naming the file and
how many lines a re-render will discard, and saying nothing when the file is
what the render produced.Replacements are simultaneous: every
findis located in the committed
content and the whole batch applies in one pass, so a replacement's output
is never a match target and reordering thereplacearray cannot change the
result. Two rules claiming overlapping text are refused by name. -
newgit referenceprints the definition format — every tracker and resource
key with its type and default, the checkpoint/restore/ownership tables, and
which template variables are in scope for which hook
(#13).All of it was documented in
newgit-v1-mvp.md, and none of it travelled:
cargo install newgitleaves a binary and a README on disk, and the
README's relative link to that file resolved to nothing on crates.io,
docs.rs, or in the registry directory. Reading the crate source — or running
stringson the binary to enumerate{{...}}variables — was the only way
to answer whatcheckpoint.modeaccepts. The reference now ships inside the
binary, which is the one copy guaranteed to be wherever the definitions are,
andinit,tracker create, andresource addeach name it. The README's
links to the design documents are absolute, and say they live on GitHub. -
newgit status <instance> --pathprints that instance's workspace path and
nothing else (#12).A bootstrap script, a README snippet, or an editor integration wanting the
path had to parse table output or read.newgit/branches/<name>.toml, which
makes the store layout someone else's API. Now:
W=$(newgit status auth-refactor --path). The instance is inferred inside a
workspace, and warnings stay on stderr so stdout is a path. -
newgit remove <name> --purgeandnewgit cleanup --purge-archivedrelease
the snapshot revs an archived instance's checkpoints were pinning
(#12).Cleanup never prunes a rev a checkpoint points at, which is right for a live
instance and a dead end for a removed one:undoneeds a binding record, so
those checkpoints are unreachable while their revs are permanent. Purging
drops the checkpoint log and itsrefs/newgit/checkpoints/<slug>/*store
refs, and the same pass reclaims what they held; a purging--dry-run
reports exactly that. It stays opt-in and never touches a live instance's
checkpoints. Ordinarycleanupnow reports how many of its retained revs
are held only by archived instances, andremovesays how many checkpoints
it kept. -
newgit tracker capture <tracker> --from-storeseeds a lane from the store
repo's working tree and sets the lane head, so the firstspawncomes up
with the content (#9).A lane starts empty and
capturereads from an instance workspace, so
adopting newgit for a tracker carrying.envfiles meant spawning an
instance guaranteed to come up without them, copying the files in, capturing,
merging, and re-running the resource action that had already failed. The
content was in the store repo at the same relative paths the whole time.
That bootstrap is now two commands:tracker track, then
tracker capture --from-store.Declared paths with nothing behind them are reported as warnings rather than
silently seeding a partial lane; a tracker with nothing at all on disk is an
error, not an empty lane head.newgit tracker tracknow suggests
--from-storewhen the paths it just added already have content. -
.newgit/scripts/and a{{scripts}}template variable, so a resource's
script lives under the same rule as the definition that calls it
(#8).A resource definition is read from the store, but anything its commands
shelled out to was read from the workspace, where it is subject to source
materialization. The two halves of one definition lived under different
rules and only the TOML half was editable in place, so iterating on a
preparemeant committing every attempt or copying the script into the
workspace by hand between runs.{{scripts}}resolves to.newgit/scripts/in the store. Edit a script
and the nextnewgit actionruns it — nothing to commit, nothing to copy,
and it works on the first spawn. The directory is control plane and is
listed ininit's "commit these" output;initwrites aREADME.mdthere
because Git will not track an empty directory. Project-owned scripts are
unaffected: they stay in the project tree, are read from the workspace, and
must still be committed before a spawn that calls them.
Changed
- The
command-snapshottemplate's[actions.migrate]says what it is: a
convenience command you invoke withnewgit action <resource>.migrate, not
a lifecycle hook. No stage ever ran it, and sitting besideprepare— which
spawnand arecomputerestore do run — it read like one
(#12).
Fixed
-
A declared
capturesname that never appeared in an action's stdout failed
in total silence: the command exited 0, newgit reportedprepare: ok,
marked the resourceready, and published an empty handle that went
unnoticed until an API call returned 401
(#7).Each missing capture is now warned for by name, with the log to look in and
the convention that explains nearly every occurrence — whencapturesis
set, stdout belongs to newgit, so everything else the command prints should
go to stderr. Warnings appear atspawn, onnewgit action, and during a
recompute restore. A missing name is still not an error: a resource may
legitimately publish a handle only on some runs. Theexternaltemplate's
comments now state the stdout convention too. -
An undo where a resource restore failed reported
Restoredon its first
line andFAILEDon its fourth, describing one operation two ways
(#11). A restore command
is not transactional — one that rebuilds a schema and then fails to load
the rows leaves its resource in neither the pre-undo state nor the
checkpoint state — so the summary no longer implies the instance is in a
known state:Undo of `smoke` to ckpt_001 ("before agent") INCOMPLETE: 0 of 1 resources restored `db` may be in a partial state — a failed restore command is not rolled backnewgit undonow exits non-zero when the undo was incomplete. -
A failed undo left a pre-undo checkpoint indistinguishable from one a human
named, so three failed attempts left three of them, each pinning its
tracker revs. The safety checkpoint now recordsundo_completedonce the
undo it preceded finishes, andnewgit checkpointsshows those entries as
failed-undorather thanbefore-undo— they are not redo points. The
record is annotated rather than deleted: newgit cannot know at save time
whether the undo will succeed, and discarding the only record of a state is
what checkpoints exist to prevent. Releasing the revs those entries pin is
acleanupconcern, tracked in
#12. -
A resource whose
depends_onnamed something that did not exist yet made
every newgit command fail, includingtracker createandtracker track
— the commands that create the missing name. The only way...
v0.1.1
Fixed
newgit --versionreported(unknown)instead of the commit for every
binary installed from crates.io. The build stamp was derived by running
git rev-parse, but a published.cratetarball ships no.git, so the
stamp only ever worked when building from a clone — the one case where you
can already see the commit. It now reads thesha1thatcargo publish
records in.cargo_vcs_info.json, falling back togitfor repo builds.
A tarball packaged with--allow-dirtygets the same-dirtysuffix a
dirty clone does.
v0.1.0
First release. All seven milestones of the v1 MVP are implemented, and the
product promise holds end to end: given a source revision plus tracker and
resource definitions, newgit materializes a consistent branch instance and
can put the whole thing back.
The two primitives
- Trackers — named, versioned lanes of file content, each with an
audience, storage, and source-merge policy.capture,merge,pull,
andcheckoutmove content through content-addressed lanes; identical
captures dedupe.sourceis just the default tracker, special only
because Git owns its history. - Resources — lifecycle units for state that cannot travel as content:
processes, ports, installs, databases, and things another system owns.
Command-based hooks, deterministic port allocation, exports, dependency
ordering, and PID-file supervision for long-running actions.
Commands
init, spawn, status, run, action, checkpoint, undo,
checkpoints, export, remove, cleanup, plus tracker and resource
subcommands for definitions. Instance names are inferred from the workspace
you are standing in.
Checkpoints and undo
One coherent snapshot across source (including uncommitted and untracked
work), every tracker lane, and every resource's state. newgit undo restores
it; it takes a safety checkpoint first, so undo is itself undoable and
running it twice is redo. Failed resource restores leave a recovery record
instead of a half-explained failure.
Export
newgit export --to <dir> writes an ordinary Git repository. Tracker
audience is the default filter and fails closed at public; --include
overrides per path and --exclude wins over everything. One commit, never
history — exporting the branch's commits would carry any file they contain,
including what the filter just withheld. Path-level filtering only; not a
concealment mechanism.
Cleanup
newgit cleanup [--dry-run] finalizes instances whose workspace is gone,
deletes unclaimed workspaces and dead process state, and prunes unreferenced
tracker snapshots. It never removes a checkpoint record, and never a snapshot
rev a checkpoint still points at. Resource [cleanup] hooks run on both
remove and cleanup, gated by ownership: project- and user-owned
resources are never torn down by per-branch teardown.
Templates
process, pnpm, command-snapshot (a daemon-owned database whose
checkpoint deposits into a tracker), and external (a resource newgit holds
only a handle to). Templates bring the companion definitions and tracker
lanes they depend on.
Known limits
Deliberately out of scope for v1, and not bugs: no FUSE projection
(workspaces are full clones), no hermetic builds, no hunk-level privacy, no
native remote, no command shims, and storage = "remote" parses but only
local is implemented. v1 does not claim security properties — it keeps
non-public tracker content out of Git by construction, not against a hostile
agent.
An instance whose workspace is deleted cannot be re-spawned under the same
name until newgit cleanup finalizes it.