No description
  • Rust 99.1%
  • Shell 0.9%
Find a file
Levi Neuwirth 46cb8ab179
Scope commits to paths, resolve paths from cwd, add revision specs (#2)
- \`levcs commit <paths>\` scopes a commit to the named files and directories. The tree is HEAD's plus the named paths, so files tracked or forgotten elsewhere stay pending, and a scoped commit during a merge is refused. \`diff <paths>\` shares the rule.
- Path arguments resolve from the current directory, every path command refuses a path matching nothing, and \`forget\` is bounded to the index so it cannot delete a file that was never tracked.
- \`diff\`, \`construct\`, \`merge\` and \`branch --create\` accept revisions: \`HEAD\`, a branch, a full hash or a unique hex prefix (4+ characters) of a commit or release, followed by chainable \`~N\` and \`^N\`. Malformed suffixes are rejected, explicit paths reach path handling, and a prefix that is also a file is refused until disambiguated with \`./name\`.
- README documents the revision syntax.
2026-09-30 13:08:58 +00:00
.github/workflows integrate CI and docs 2026-05-01 11:29:18 -04:00
bench Condition benchmark claims on the run that produced them 2026-07-29 17:38:03 -04:00
crates Scope commits to paths, resolve paths from cwd, add revision specs (#2) 2026-09-30 13:08:58 +00:00
deploy levcs 0.1.0 - initial core 2026-05-01 11:14:36 -04:00
doc Make a staged projection adoptable, reclaimable, and recoverable 2026-07-31 18:18:55 -04:00
scripts Condition benchmark claims on the run that produced them 2026-07-29 17:38:03 -04:00
.gitignore levcs 0.1.0 - initial core 2026-05-01 11:14:36 -04:00
Cargo.lock Implement D0-B storage publication interfaces 2026-07-27 22:31:32 -04:00
Cargo.toml Implement D0-B storage publication interfaces 2026-07-27 22:31:32 -04:00
LICENSE integrate CI and docs 2026-05-01 11:29:18 -04:00
README.md Scope commits to paths, resolve paths from cwd, add revision specs (#2) 2026-09-30 13:08:58 +00:00

LeVCS

A distributed version control system with first-class federation, signed authority chains, and a cascading merge engine. Content-addressed by BLAKE3, signed with Ed25519, and built to fix what git can't.

Status: v0.1.0 — protocol substrate complete, workflow surface deferred. The object model, federation API, merge cascade, and instance server all work end-to-end. There is no PR review surface, issue tracker, or web UI yet — those are the next layer up. See doc/technical-report.md for a full framing of where this project is and why.


What's different from git

  • Identity is in the protocol. A repo's membership is a versioned, signed authority object with explicit roles (Reader/Contributor/Maintainer/Owner). Force-push enforcement and push authorization are protocol-level, not server policy.
  • Federation is first-class. Every repo has a global repo_id (BLAKE3 of its genesis authority); instances mirror each other in three storage modes (full / release-only / metadata-only).
  • Merge is a cascade, not a line-level diff. Per-file dispatch to a handler ranked by aggressiveness: textual fallback, format-aware (JSON / YAML / TOML / XML / Markdown / prose), tree-sitter for source code (Rust, Python, JS/TS, Go, C/C++, Java, Ruby, Bash), and wasm-sandboxed plugins for the long tail.
  • BLAKE3, not SHA-1. Tree-hashed, ~5 GiB/s on a laptop, 32-byte IDs everywhere.
  • Releases are signed objects, not mutable name pointers.

For a deeper comparison and context, see the technical report.


Building

LeVCS is a Rust workspace. You'll need a recent stable toolchain (workspace MSRV is 1.75) and a C compiler for the tree-sitter grammars.

cargo build --release

Two binaries land in target/release/:

  • levcs — the user-facing CLI.
  • levcs-instance — the federation HTTP server.

Install them somewhere on PATH:

sudo install -m 0755 \
    target/release/levcs target/release/levcs-instance \
    /usr/local/bin/

Quick start (single user, local only)

# Generate an identity key (stored in $XDG_CONFIG_HOME/levcs/keys.toml).
# Pass --encrypt if the key should not sit in plaintext.
levcs key generate me

# Create a repository wherever you have files to track.
mkdir /tmp/demo && cd /tmp/demo
echo "hello" > a.txt

levcs init --key me
levcs track --all
levcs commit -m "first commit"
levcs log

That's a fully working LeVCS repo.

A commit can be scoped to paths, which is how you commit one piece of work out of a tree that holds several:

levcs commit -m "just the notes" notes/ README.md

Everything outside the named paths keeps the content it has in HEAD and stays uncommitted — including deletions, which are not read as "drop it from the tree" when the commit was never about that file. A path naming a directory takes everything tracked beneath it; a path matching nothing tracked is refused rather than silently committing nothing. levcs diff <paths> uses the same rule, so it is an exact preview of what levcs commit <paths> will take.

Path arguments are relative to the current directory, and every command that takes them refuses one that matches nothing. Those two go together: a name can then only ever miss, and a miss is reported. Output stays repository-relative, so commit's scoped to line doubles as confirmation of what your argument resolved to.

This matters more than convenience when a repository is written by more than one hand. Attribution is the entire point of signing a commit, and a commit that had to sweep up someone else's unfinished edits in order to exist attributes their work to whoever signed it.

forget stops tracking a file and leaves it on disk:

levcs forget build/output.bin          # untrack, keep the file
levcs forget --delete build/output.bin # untrack and remove it

This is a deliberate divergence from git rm. The verb only ever acts on paths that are already tracked — an untracked path is refused, and a directory expands to the tracked files beneath it — so everything --delete can reach has a blob in the object store and comes back with levcs construct. Bounding the verb is what makes deletion safe by construction rather than by analysis; the alternative, deleting by default and refusing when the content is not yet in a commit, would make the tool answer "is this recoverable?" on every call, with deletion as the price of being wrong.

Commands that take a commit (diff, construct, merge, branch --create) accept a revision, not just a full hash:

levcs diff HEAD~1            # against the previous commit
levcs construct HEAD~3 a.txt # a.txt as it was three commits ago
levcs diff 3fa9c1            # a unique hex prefix (4+ characters)
levcs merge main^2           # the second parent of main's tip

The base is HEAD, a branch, a full hash, or a hex prefix of a commit or release. ~N follows the first parent N times (~ alone is ~1); ^N takes the Nth parent, counting from 1, which is how you reach the other side of a merge (^0 is the commit itself). Suffixes chain: HEAD~2^2. A branch beats a prefix of the same spelling, and a prefix matching more than one commit is refused with the candidates listed rather than guessed at.

diff and construct also take paths, so a word that could be either is resolved conservatively: a bare branch name or an unmatched hex word is a path, and so is a name like notes~ whose base is not a ref. If a bare prefix matches a commit and a file in the current directory, the command refuses and asks you to write ./name for the file.

Branch and merge — note that creating a branch and switching to it are separate operations, so a commit cannot land on a branch you only meant to make:

levcs branch --create feature/x
levcs branch --switch feature/x
echo "more" >> a.txt
levcs commit -m "wip"

levcs branch --switch main
levcs merge feature/x

merge reports each file and the handler that resolved it:

AUTO     note.md  (markdown)

merge summary:
  auto-resolved: 1
  conflicts:     0

To step through a merge rather than take it wholesale, and to abandon one that went badly:

levcs merge feature/x --review
levcs merge --abort

--explain prints why each file went to the handler it did, and --format json emits one structured object per §6.7 for scripting.

Cut a release:

levcs release v0.1.0 -m "first release"

Hosting an instance

To dogfood the federation surface, run levcs-instance on a VPS behind nginx or Caddy. The full walkthrough is in deploy/README.md: build, systemd unit, reverse proxy templates, firewall, and the laptop-side bootstrap.

The compressed version:

sudo cp deploy/levcs-instance.service /etc/systemd/system/
sudo cp deploy/instance.toml.example /etc/levcs/instance.toml
sudo $EDITOR /etc/levcs/instance.toml
sudo systemctl enable --now levcs-instance
# ... then drop deploy/Caddyfile.example into /etc/caddy/Caddyfile

From your laptop, point the local repo at the instance and push:

levcs instance --set https://levcs.example.com/levcs/v1
levcs push refs/branches/main

The first push to a fresh instance auto-inits the repo with your genesis authority. Subsequent pushes are role-checked against the authority chain.


Repository layout

crates/
  levcs-core/        Object model (Blob/Tree/Commit/Release/Authority),
                     hash, store, refs, repository abstractions.
  levcs-identity/    Authority objects, Ed25519 keys, signing/verify.
  levcs-merge/       Cascade engine, format and tree-sitter handlers,
                     plugin runtime, merge records.
  levcs-protocol/    Pack codec, wire types, request signing, P2P.
  levcs-client/      Thin HTTP client over the federation API.
  levcs-instance/    Axum HTTP server (the federation peer).
  levcs-cli/         The `levcs` user-facing CLI.
  levcs-tui/         Conflict-resolution terminal UI.

deploy/              Production deployment artifacts (systemd, Caddy, nginx).
scripts/             Reproducible benchmark and ops scripts.
doc/                 Technical report and architecture docs.
.github/workflows/   CI configuration.

Testing

cargo test --workspace

Runs the full suite — unit tests, integration tests, federation end-to-end tests including the three-instance "dogfood" scenario, the merge conformance corpus, and property-based fuzz tests. ~194 tests at v0.1.0; full run is well under a minute on a modern laptop.

Useful subsets:

# A single crate's tests
cargo test -p levcs-merge

# A specific integration test
cargo test -p levcs-instance --test dogfood

# Property tests only
cargo test -p levcs-merge --test proptest_textual

Benchmarks

Microbenchmarks live in each crate's benches/ directory and use criterion. A reproducible runner with metadata capture and optional flamegraph generation is at scripts/bench.sh:

scripts/bench.sh --quick               # smoke test (~ a minute total)
scripts/bench.sh                       # full criterion run (~ a few minutes)
scripts/bench.sh --flamegraph          # generate per-bench SVG flamegraphs
scripts/bench.sh --bench pack_codec    # one bench only

Output goes to bench-results/<host>-<UTC-timestamp>/ with a parsed summary.txt, criterion's HTML reports, and a metadata.txt capturing rustc version, kernel, CPU, and git rev for run-to-run comparison.

Headline numbers on a Ryzen 7 laptop:

  • Pack decode at 10 × 1 MiB entries: ~2.3 ms (4.3 GiB/s).
  • Blob serialize + BLAKE3 at 1 MiB: ~190 µs (5.1 GiB/s).
  • Textual three-way merge of a 100 KiB document: ~4.6 ms.

Pack encoding is the throughput floor at ~380 MiB/s — bottlenecked by zstd level 3 on incompressible data.


Documentation

  • doc/technical-report.md — Distribution document. What LeVCS is, how to use it, and how it differs from / improves upon git. Targets technical evaluators and the workflow-spec reader.
  • deploy/README.md — Comprehensive VPS deployment walkthrough.
  • spec/ — The protocol specification and trust-root revision. Currently kept private; ask the maintainer for a copy.

Contributing

This is a young project. The most useful contributions right now are:

  1. Trying it. Run levcs init on a real project, push to a local instance, and report friction.
  2. Workflow design. The next major piece of work is the workflow spec — PR/review surface, issues, CI conventions. Discussion welcome.
  3. Plugin handlers. The wasm plugin protocol exists; concrete handlers (e.g. protobuf, SQL migrations) are needed to validate it.
  4. Tightening CI. The fmt and clippy GitHub jobs are informational; flipping them to gating would close a small but real quality gap.

Please open an issue or reach out before starting non-trivial work so we can coordinate.


License

Released under the Apache License 2.0 — see LICENSE for the full text.


Citation

If LeVCS supports academic work, please cite the v0.1.0 release. A formal citation entry will land with the workflow spec; in the meantime a repository-URL reference is fine.