Skip to content

Releases: BangRocket/mcagit

mcagit v20260607-e772a39

Choose a tag to compare

@github-actions github-actions released this 07 Jun 00:20
e772a39

mcagit build from commit e772a39c1920b3304ebd3196b78dd0867fa09477.

Dispatched from PR #44.

v0.7.0 — network remotes (HTTP + SSH)

Choose a tag to compare

@BangRocket BangRocket released this 03 Jun 19:34

mcadiff is now a distributed VCS — push / fetch / clone over the network, not just the local filesystem. A remote URL can be a path, http(s)://, or ssh://. Because objects are content-addressed, transfer copies only what the other side lacks.

HTTP (built-in daemon)

git's model: anonymous read, authenticated push.

mcadiff -C <repo> serve --port 8421 --allow-push --token s3cret
mcadiff clone http://host:8421 ./world.mcagit        # anonymous read
mcadiff push origin main --token s3cret              # authenticated push

Push is rejected (401) without the token; --allow-push is required for any write. Uploaded objects are integrity-checked (decompress + SHA must match the name), so a bad peer can't poison the store.

SSH (no daemon)

Runs mcadiff serve-stdio on the remote over your ssh session; auth & encryption are ssh's job (keys/agent). Requires mcadiff installed on the remote.

mcadiff clone ssh://user@host/path/to/world.mcagit ./world.mcagit
mcadiff push  ssh://user@host/path/to/world.mcagit main

Under the hood

A single transport abstraction (IRemoteTransport) means clone/fetch/push are written once; filesystem, HTTP, and ssh are just transports. The filesystem path was refactored onto it with no behavior change.

75 tests (was 72): live HTTP push/clone + token auth against an in-process server, and the ssh stdio protocol (clone + push) over in-memory pipes. Verified end-to-end on the example worlds — HTTP push of 4,389 objects then a 0-object re-push, anonymous clone, 401 without a token, and the cloned-over-HTTP world checks out byte-clean.

Limitations

Per-object transfer (no packfiles/delta yet); the HTTP daemon is simple (single token, no TLS — front it with a reverse proxy for https). No staging index.

v0.6.0 — git-likeness Tier 2 (remotes, reflog, cherry-pick, gc)

Choose a tag to compare

@BangRocket BangRocket released this 03 Jun 19:18

Takes mcadiff from a local VCS toward a distributed one — sync world history between repositories (e.g. push backups to another drive/NAS). No network yet; remotes are repository directories, and because objects are content-addressed, transfer copies only what the other side lacks.

New

  • Remotes (filesystem): clone, remote add, fetch, push. push is fast-forward-checked (--force overrides); fetch populates refs/remotes/<remote>/* (resolvable as origin/main).
  • reflog — HEAD movement history (logged on commit).
  • cherry-pick <commit> — apply one commit onto HEAD via the 3-way engine.
  • gc — prune objects unreachable from any ref.

Fixes (found via Tier-2 testing)

  • Merge dropped a one-sided region: a region present on only one side (including an empty region like a fresh poi/) was lost by the manifest 3-way merge. Region existence is now resolved per-region (added / deleted / delete-modify conflict).
  • Nondeterministic classification hardened: a rare transient parse failure during the parallel commit could silently downgrade a valid NBT .dat to an opaque blob, making manifests nondeterministic. Parsing now retries before falling back.

72 tests (was 67): clone+push fast-forward, fetch remote-tracking refs, gc prunes unreachable objects, reflog records commits, merge carries a one-sided/empty region.

Deferred (by design)

A staging index and an in-place conflict-resolution workflow don't map cleanly to whole-world snapshots; network transport (ssh/http) and delta/packfiles remain future work.

v0.5.0 — git-likeness Tier 1

Choose a tag to compare

@BangRocket BangRocket released this 03 Jun 19:01

Rounds out the local VCS so it feels git-native, reusing the existing diff/patch/merge engines.

  • Revision syntax — HEAD, branch/tag names, abbreviated hashes, and ~n / ^n ancestor suffixes everywhere (diff HEAD~2 HEAD, checkout main~1, show <short>).
  • tag — create/list/-d, resolvable as revisions.
  • show <ref> — a commit's metadata + its diff vs the first parent.
  • reset <ref> [--hard] — move the current branch (and update the worktree with --hard).
  • restore <ref> <path>... — materialize selected files/regions from a snapshot.
  • revert <commit> — a new commit that undoes one, via the 3-way merge engine (conflicts reported).
  • log — --oneline, -p (patch), --stat, -n.
  • .mcaignore — gitignore-lite (*.ext, dir/, name, /anchored/path) honored on commit.

67 tests (was 61). Next: Tier 2 — filesystem remotes (clone/fetch/push), reflog, cherry-pick, gc.

v0.4.0 — git-aligned CLI + repo-aware diff

Choose a tag to compare

@BangRocket BangRocket released this 03 Jun 18:49

The CLI now works like git, and diff understands the repository.

Git-aligned CLI

  • Repo discovery: the repository is the current directory (or nearest ancestor) — or pass -C <repo> before any command, exactly like git -C. The old <repo> first-positional is gone from every command.
  • Bound worktree: bind a world to the repo (mcadiff init <repo> --worktree <world>, or mcadiff config worktree <path>). Then commit / status / diff / checkout need no path — just like git's working tree. A path can still be passed to override.
mcadiff init <repo> --worktree <world>
cd <repo>                         # or prefix any command with -C <repo>
mcadiff commit -m "before raid"   # snapshots the bound worktree
mcadiff status                    # changes vs HEAD
mcadiff diff                      # worktree vs HEAD
mcadiff log / branch / merge / checkout …

Repo-aware diff

diff now connects to history:

  • mcadiff diff → worktree vs HEAD
  • mcadiff diff <ref> → ref vs worktree
  • mcadiff diff <refA> <refB> → any two snapshots (branch / commit / HEAD / or a path to a working world)

It compares chunk hashes from the manifests first, so unchanged chunks are skipped instantly and only differing chunks are decoded — then reuses the same colored/JSON output as the file diff. Outside a repository, mcadiff diff <A> <B> still diffs two worlds/files as before.

Tests

61 (was 58): RepoDiffer across two commits and commit-vs-working-tree. Verified end-to-end on the example worlds — -C and cwd discovery, bound-worktree commit/status/diff, and all three diff forms.

v0.3.1 — repository polish

Choose a tag to compare

@BangRocket BangRocket released this 03 Jun 17:45

Polish pass on the repository/VCS layer (v0.3.0).

Fixed

  • Correctness — detached HEAD: committing on a detached HEAD no longer silently clobbers main. It advances HEAD itself and leaves all branches untouched; commit/checkout now report when you're detached.
  • Robustness — dedup: the canonical form now sorts NBT compound keys recursively (lists preserved). Two semantically-identical chunks that differ only in key order now dedup to a single object instead of being stored twice.
  • Performance — re-commit cache: a per-repo cache maps a chunk's compressed-payload hash to its stored object hash, so re-committing a mostly-unchanged world skips decoding the unchanged chunks. Measured ~7× faster re-commit on the example world (0.54s → 0.08s). Commit still verifies the object exists before trusting a cache hit; status reads the cache but never writes it.

Tests

58 (was 55): detached commit keeps main put; reordered-key compounds hash identically; re-commit adds no objects and matches HEAD. Verified end-to-end on the real world — re-commit is a fast no-op and checkout still round-trips clean.

Deliberately deferred (documented in README)

hardlink/reflink for the apply copy; delta-packed objects; remotes; in-place conflict resolution; criss-cross merge-base; and addressing NBT keys that contain literal ./[.

v0.3.0 — semantic version control (history + 3-way merge)

Choose a tag to compare

@BangRocket BangRocket released this 03 Jun 17:10

Turns mcadiff into a git-like, semantic version-control system for Minecraft worlds — on top of the existing diff and patch/restore.

New: repository

A content-addressed, deduplicating object store (external repo). Each chunk is hashed by its decoded NBT, so an unchanged chunk is stored once no matter how many snapshots reference it — the thing fastback + git-LFS fundamentally can't do (they store whole region blobs per snapshot).

mcadiff init    <repo>
mcadiff commit  <repo> <world> -m "msg"
mcadiff log     <repo>
mcadiff status  <repo> <world>
mcadiff checkout <repo> <ref> <world-out>
mcadiff branch  <repo> [name]
mcadiff merge   <repo> <other-branch> [--theirs]
  • Whole-world snapshots: region/entities/poi as deduped per-chunk objects, loose NBT canonical, all other files (datapacks, stats, advancements) as raw blobs → faithful, playable checkout.
  • True 3-way merge: finds the common ancestor and merges per NBT node — changes from both sides touching different nodes both land; only a genuine same-node clash is a conflict (kept ours, or theirs with --theirs, and reported). Far finer than line-based merge.

Quality

55 tests (was 48): object-store dedup, canonical-form determinism, commit→checkout reproduces a world, merge-base, and 3-way merge (combine / conflict ours+theirs / fast-forward). Verified on the example worlds — committing Older then Newer reproduces each exactly on checkout, and shared chunks are stored once (~4,389 objects for ~5,250 chunks across both commits).

Requires .NET 9. See the README's Version control section.

v0.2.0 — diff + patch/restore

Choose a tag to compare

@BangRocket BangRocket released this 03 Jun 16:28

First tagged release of mcadiff — a semantic, git-style diff and patch/restore tool for Anvil-format Minecraft (Java Edition) worlds.

Diff

  • Element-by-element diff of two worlds: region/ + entities/ + poi/ across all dimensions, plus loose NBT (level.dat, playerdata/*.dat, data/*.dat).
  • Recurses the full NBT tree to leaf level; compounds matched by key, set-like lists matched by identity (block coords, entity UUID, inventory slot, string id) else by index; packed arrays summarized (--expand for per-index).
  • Colored unified output (auto-off when piped) or --json; --only, --summary. Exit 0/1/2.
  • Fast paths skip byte-identical files and byte-identical compressed chunks; a ~2,900-file self-diff runs in seconds.

Patch & restore (new)

  • mcadiff extract <old> <new> -o changes.mcapatch — portable, bidirectional patch (each op stores old + new, losslessly type-encoded).
  • mcadiff apply <patch> <target> -o <out> — non-destructive: copies the target to a fresh output world and rewrites only the patched nodes, each 3-way guarded (mismatch → conflict, skipped, never clobbered). --reverse restores old state; --force, --dry-run, --only.
  • New write path (RegionWriter + ChunkCodec.Encode), Nbt/ utilities (NbtJson, NbtPath, NbtEquality, NbtIdentity), and a single IDiffSink walk driving both diff and extract.

Quality

  • 48 xUnit tests. Forward/reverse round-trips verified end-to-end on real worlds: extract Older→Newer + apply reproduces Newer exactly; apply --reverse reproduces Older.

Requires .NET 9. See the README for usage and a worked example.