Compare commits

...

3 commits

Author SHA1 Message Date
86a49961ad
docs: add project documentation 2026-02-10 00:00:27 +00:00
0bc174e341
chore: add license file 2026-02-09 23:58:40 +00:00
d8710a79c9
docs: add README.md 2026-02-09 23:57:36 +00:00
7 changed files with 638 additions and 0 deletions

55
LICENSE.md Normal file
View file

@ -0,0 +1,55 @@
# Blue Oak Model License
Version 1.0.0
## Purpose
This license gives everyone as much permission to work with
this software as possible, while protecting contributors
from liability.
## Acceptance
In order to receive this license, you must agree to its
rules. The rules of this license are both obligations
under that agreement and conditions to your license.
You must not do anything with this software that triggers
a rule that you cannot or will not follow.
## Copyright
Each contributor licenses you to do everything with this
software that would otherwise infringe that contributor's
copyright in it.
## Notices
You must ensure that everyone who gets a copy of
any part of this software from you, with or without
changes, also gets the text of this license or a link to
<https://blueoakcouncil.org/license/1.0.0>.
## Excuse
If anyone notifies you in writing that you have not
complied with [Notices](#notices), you can keep your
license by taking all practical steps to comply within 30
days after the notice. If you do not do so, your license
ends immediately.
## Patent
Each contributor licenses you to do everything with this
software that would otherwise infringe any patent claims
they can license or become able to license.
## Reliability
No contributor can revoke this license.
## No Liability
***As far as the law allows, this software comes as is,
without any warranty or condition, and no contributor
will be liable to anyone for any damages related to this
software or this license, under any kind of legal claim.***

84
README.md Normal file
View file

@ -0,0 +1,84 @@
# arc ![license] ![activity]
[license]: https://badge.hanna.lol/license/BlueOak-1.0.0
[activity]: https://badge.hanna.lol/activity/hanna/arc
A delta-based version control system written in Rust.
Unlike Git's snapshot-based model, Arc stores incremental deltas using
ZSTD-compressed MessagePack files. Changes are automatically tracked
without manual staging, and commits are immutable once created.
Arc uses a **bookmark** system instead of branches, and bridges to Git
remotes for push, pull, clone, and sync operations via `libgit2`.
## Features
- Incremental delta storage (ZSTD + MessagePack)
- Automatic change tracking (no staging step)
- Bookmarks and immutable tags
- Named stashes
- Three-way merge and graft (cherrypick/rebase)
- Git bridge for remote operations (push, pull, clone, migrate, sync)
- Optional SSH commit signing
- Per-repo and global YAML configuration with aliases
- `.arcignore` / `.ignore` support
## Building
Arc builds exclusively through Nix:
```sh
nix build
```
The flake uses `nixpkgs-unstable`, `flake-parts`, `fenix`, and `crane`.
A dev shell is available for iterative work:
```sh
nix develop
```
## Usage
```
arc init [path] Initialize a new repository
arc commit <message> Commit current changes
arc status Show changes since last commit
arc diff [start..end] Show a diff of changes
arc log [start..end] Show commit history
arc show <mark|tag|commit> Show details of a ref or commit
arc history <file> [start..end] Show per-line modification history
arc switch <mark|tag> Switch worktree to a bookmark or tag
arc merge <mark|tag> Merge a bookmark or tag into the worktree
arc revert <commit|start..end> Revert a commit or range
arc reset [file...] Reset worktree to the last commit
arc graft <target> --onto <dest> Cherrypick commits onto a bookmark
arc mark add|rm|list|rename Manage bookmarks
arc tag add|rm|list Manage tags
arc stash create|use|push|pop|rm|list Manage named stashes
arc push [-r remote] Push to a git remote
arc pull [-r remote] Pull from a git remote
arc clone [-b branch] <url> [path] Clone a git remote as an arc repo
arc migrate Convert a git repo to an arc repo
arc sync [-p] Sync bookmarks and tags with remote
arc remote add|rm|list Manage remotes
arc config set|get|show|unset [-g] Manage configuration
```
## Testing
```sh
nix develop -c cargo test
nix develop -c cargo clippy
nix develop -c cargo fmt --check
```
## License
[Blue Oak Model License 1.0.0](LICENSE.md)

120
docs/architecture.md Normal file
View file

@ -0,0 +1,120 @@
# Architecture
Arc is a version control system with its own data model, storage format, and a
git bridge for interoperability.
## Repository Layout
An arc repository keeps all state in an `.arc/` directory at the worktree root:
| Path | Format | Purpose |
|------|--------|---------|
| `HEAD` | YAML | Current state — one of three variants: **unborn** (no commits yet; has `bookmark`), **attached** (on a bookmark; has `bookmark` + `commit`), or **detached** (raw commit; has `commit`). |
| `config.yml` | YAML | Local repository configuration. |
| `commits/<id>.zst` | Zstandard-compressed MessagePack | Commit objects. Each file contains a `CommitObject` that bundles a `Commit` and its `Delta`. |
| `bookmarks/<name>.yml` | YAML | One file per bookmark. Contains a `RefTarget` with an optional `commit` field. |
| `tags/<name>.yml` | YAML | Same format as bookmarks. |
| `stashes/state.yml` | YAML | Tracks the active stash. |
| `stashes/named/<name>.yml` | YAML | Per-stash state files. |
| `remotes.yml` | YAML | Map of remote names to URLs. |
| `git/` | Bare git repo | Shadow repository used by the git bridge. |
| `git-map.yml` | YAML | Bidirectional mapping between arc commit IDs and git OIDs. |
## Data Model (`src/model.rs`)
`CommitId` and `DeltaId` are newtype wrappers around `String`, holding SHA-256
hex hashes.
**Commit** — `id`, `parents` (Vec), `delta` (DeltaId), `message`,
`author` (optional `Signature`), `timestamp` (i64 unix),
`ssh_signature` (optional PEM string).
**Delta** — `id`, `base` (optional parent CommitId), `changes` (Vec of
`FileChange`).
**FileChange** — `path` plus a `kind`: Add, Modify, Delete, or Rename.
**FileContentDelta** — either `Full { bytes }` (complete snapshot) or
`Patch { format, data }` (incremental).
**Head** — enum with variants Unborn, Attached, and Detached.
## Storage (`src/store.rs`)
`CommitObject` bundles a `Commit` and its `Delta` into a single unit that is
serialized as MessagePack, then compressed with Zstandard at level 3. Files are
written atomically (write to `.tmp`, then rename). IDs are computed by SHA-256
hashing the MessagePack-serialized content-addressable data.
## Tracking (`src/tracking.rs`)
`FileTree` is a `BTreeMap<String, Vec<u8>>` mapping relative paths to file
content.
- `scan_worktree` — recursively walks the working directory, respecting ignore
rules and skipping `.arc/` and `.git/`.
- `materialize_committed_tree` — rebuilds the full file tree by replaying the
linear delta chain from the root commit.
- `detect_changes` — compares the committed tree against the worktree to produce
a list of `FileChange` entries (adds, modifies, deletes).
## Ignore System (`src/ignore.rs`)
Reads `.arcignore` first, falling back to `.ignore`. Always ignores `.arc/` and
`.git/`.
- `*` and `?` glob wildcards.
- `!` prefix for negation.
- Patterns without `/` match any path component; patterns with `/` match the
full relative path.
- Patterns ending with `/` match directories only.
## Git Bridge (`src/bridge.rs`)
Maintains a shadow bare git repository under `.arc/git/`.
`GitMap` provides bidirectional mapping (`arc_to_git` / `git_to_arc`) persisted
in `.arc/git-map.yml`.
- `arc_to_git` recursively converts arc commits to git commits, materializing
file trees as git tree objects.
- `git_to_arc` does the reverse, computing deltas from git tree diffs.
- SSH authentication via agent or key files (`~/.ssh/id_ed25519`, `id_rsa`,
`id_ecdsa`).
## Merge (`src/merge.rs`)
Full three-way merge with line-level merging for text files using Myers diff.
Conflicts are marked with `<<<<<<< ours` / `=======` / `>>>>>>> theirs`.
Binary files fall back to keeping the "ours" version on conflict.
## Signing (`src/signing.rs`)
Optional SSH key signing using the `ssh-key` crate. Signs with SHA-512 under
the `arc` namespace. Verification extracts the public key from the signature
itself. Supports `~` expansion in key paths.
## Source Modules
| Module | Responsibility |
|--------|----------------|
| `main.rs` | Entry point, macro definitions |
| `cli.rs` | Clap-based CLI parsing and command dispatch |
| `model.rs` | Core data types |
| `store.rs` | Commit/delta serialization and content-addressing |
| `tracking.rs` | Worktree scanning, change detection, commit logic |
| `repo.rs` | Repository init/open/discover, path validation |
| `config.rs` | YAML config loading, merging (local-first), effective config |
| `refs.rs` | Bookmark/tag CRUD, switch, worktree write/clean |
| `bridge.rs` | Git bridge (shadow repo, push, pull, clone, migrate, sync) |
| `diff.rs` | Unified diff rendering |
| `inspect.rs` | Log, show, history (blame), Myers diff |
| `merge.rs` | Three-way merge |
| `modify.rs` | Reset, revert, merge command, graft |
| `resolve.rs` | Target/range resolution (bookmarks, tags, prefixes, HEAD) |
| `ignore.rs` | Ignore file parsing and matching |
| `signing.rs` | SSH commit signing and verification |
| `stash.rs` | Named stash system |
| `remote.rs` | Remote management (remotes.yml) |
| `error.rs` | Error types |
| `ui.rs` | Colored output formatting |

180
docs/commands.md Normal file
View file

@ -0,0 +1,180 @@
# Command Reference
## Global Flags
- `-v` / `--verbose` — Increase verbosity (up to `-vvv`).
- `--version` — Show version.
- `--help` / `help` — Show help.
## Core
### `arc init [path]`
Initialize a new arc repository. Creates the `.arc/` directory structure including `commits/`, `bookmarks/`, `tags/`, `stashes/`, `config.yml`, and `HEAD`. The default bookmark is `main`.
### `arc commit <message>`
Commit all current changes. No staging area is needed — changes are detected automatically by comparing the worktree to the last commit. Creates a ZSTD-compressed MessagePack commit object in `.arc/commits/`. If a signing key is configured (`user.key`), the commit is signed with SSH.
### `arc status`
Show added, modified, and deleted files since the last commit.
### `arc diff [start..end]`
Show a unified diff of changes. Without arguments, shows uncommitted changes. With a range, shows the diff between two commits.
### `arc log [start..end]`
Show commit history. Without arguments, shows all commits. Supports ranges like `start..end` (inclusive, either side optional). Each entry shows commit ID, timestamp, author, message, and a `[signed]` tag if applicable.
### `arc show <mark|tag|commit>`
Show full details of a commit including author, date, parent(s), signature verification status, and the diff it introduced. Resolves bookmarks, tags, commit IDs, and commit prefixes.
### `arc history <file> [start..end]`
Show per-line blame/annotation for a file, showing which commit last modified each line. Uses the Myers diff algorithm.
## Branching (Bookmarks)
### `arc mark add <name> [commit]`
Create a bookmark at the given commit. Defaults to HEAD if no commit is specified.
### `arc mark rm <name>`
Remove a bookmark. Cannot remove the active bookmark.
### `arc mark list`
List all bookmarks, marking the active one.
### `arc mark rename <name> <new>`
Rename a bookmark. Updates HEAD if the active bookmark is renamed.
## Tags
### `arc tag add <name> [commit]`
Create an immutable tag at the given commit. Defaults to HEAD if no commit is specified. Tags cannot be overwritten.
### `arc tag rm <name>`
Remove a tag.
### `arc tag list`
List all tags with their commit IDs.
## Navigation
### `arc switch <mark|tag>`
Switch the worktree to a bookmark or tag. Switching to a bookmark attaches HEAD. Switching to a tag or raw commit detaches HEAD. Requires a clean worktree.
### `arc merge <mark|tag>`
Three-way merge from a bookmark or tag into the current worktree. Creates a merge commit with two parents. Reports conflicts with `<<<<<<< ours / ======= / >>>>>>> theirs` markers.
## Undo & Modification
### `arc revert <commit|start..end>`
Create a new commit that reverses the changes from a commit or range. Uses three-way merge.
### `arc reset [file...]`
Reset the worktree to match the last commit. Without arguments, resets all files. With file arguments, resets only those files.
### `arc graft <target> --onto <bookmark|commit>`
Cherry-pick/rebase commit(s) onto a target. Uses three-way merge. If the target is a bookmark, updates the bookmark pointer.
## Stash
### `arc stash create <name>`
Create a named stash and set it as active.
### `arc stash use <name>`
Switch the active stash.
### `arc stash push`
Push dirty changes onto the active stash and reset the worktree.
### `arc stash pop`
Pop the most recent entry from the active stash and apply it to the worktree. Requires a clean worktree and matching HEAD.
### `arc stash rm <name>`
Remove a named stash.
### `arc stash list`
List all named stashes with entry counts.
## Configuration
### `arc config set [-g] <key> <value>`
Set a config value. Use `-g` for global config. Keys use `section.field` format (e.g. `user.name`, `default.bookmark`, `aliases.c`).
### `arc config get [-g] <key>`
Get a config value. Without `-g`, resolves local first, then falls back to global.
### `arc config show [-g]`
Show full configuration as YAML. Without `-g`, shows effective (merged) config.
### `arc config unset [-g] <key>`
Remove a config key.
### Valid Config Fields
- `user.name` — Author name.
- `user.email` — Author email.
- `user.key` — SSH key path for commit signing.
- `default.bookmark` — Default bookmark name (default: `main`).
- `default.remote` — Default remote name (default: `origin`).
- `aliases.<name>` — Command aliases, expanded at the CLI level.
## Git Bridge & Remotes
### `arc remote add <name> <url>`
Add a remote. Stored in `.arc/remotes.yml`.
### `arc remote rm <name>`
Remove a remote.
### `arc remote list`
List all configured remotes.
### `arc push [-r remote]`
Push to a git remote. Converts arc commits to git commits via a shadow git repo (`.arc/git/`), with the mapping stored in `.arc/git-map.yml`.
### `arc pull [-r remote]`
Pull from a git remote. Converts git commits to arc commits. Fast-forward only.
### `arc clone [-b branch] <url> [path]`
Clone a git remote and convert it to an arc repository.
### `arc migrate`
Convert an existing git repository to an arc repository. Imports all branches as bookmarks and all tags. Preserves commit history.
### `arc sync [-p]`
Sync all bookmarks and tags to the shadow git repo. With `-p`, also pushes to the default remote.

64
docs/configuration.md Normal file
View file

@ -0,0 +1,64 @@
# Configuration
Arc uses YAML configuration files.
```yaml
user:
name: hanna
email: me@hanna.lol
key: ~/.ssh/id_ed25519
default:
bookmark: main
remote: origin
aliases:
c: commit
p: push
l: pull
```
## File Locations
- **Local config**: `.arc/config.yml` inside the repository. Created automatically on `arc init` with default bookmark/remote values.
- **Global config**: `$XDG_CONFIG_HOME/arc/config.yml`, or `~/.config/arc/config.yml` if `XDG_CONFIG_HOME` is not set.
## Resolution Order
Local config values take priority over global config values. For each field, the local value is checked first; if not set, the global value is used. Aliases from both configs are merged, with local aliases overriding global ones with the same name.
## Sections
### `user`
| Key | Description |
|---|---|
| `user.name` | Author name for commits. |
| `user.email` | Author email for commits. Both name and email must be set for author info to appear on commits. |
| `user.key` | Path to an SSH private key for commit signing (e.g. `~/.ssh/id_ed25519`). Tilde expansion (`~`) is supported. When set, commits are automatically signed. |
### `default`
| Key | Description |
|---|---|
| `default.bookmark` | Default bookmark name (used on init). Defaults to `main`. |
| `default.remote` | Default remote name (used by push/pull/sync). Defaults to `origin`. |
### `aliases`
Maps short names to command names. For example, `aliases.c: commit` lets you run `arc c "message"` instead of `arc commit "message"`. Aliases are expanded at the CLI level before command dispatch. Any alias key is valid; the value should be a valid arc command name.
## Commands
```
arc config set <key> <value> Set a local config value.
arc config set -g <key> <value> Set a global config value.
arc config get <key> Get a value (local first, then global).
arc config get -g <key> Get a value from global config only.
arc config show Show the effective (merged) configuration.
arc config show -g Show only the global configuration.
arc config unset <key> Remove a key from local config.
arc config unset -g <key> Remove a key from global config.
```
Keys use dot notation: `section.field` (e.g. `user.name`, `default.remote`, `aliases.st`).

70
docs/getting-started.md Normal file
View file

@ -0,0 +1,70 @@
# Getting Started
Arc is a delta-based version control system written in Rust. It builds exclusively through Nix.
## Building
Arc uses a Nix flake (nixpkgs-unstable, flake-parts, fenix, crane):
```sh
nix build
```
Enter a dev shell with `nix develop` (provides rustc, cargo, clippy, rustfmt, git, pkg-config, cmake, perl).
Run Arc with `nix run` or `result/bin/arc` after building.
## Configuration
```sh
arc config set user.name "yourname"
arc config set user.email "you@example.com"
```
Global config lives at `$XDG_CONFIG_HOME/arc/config.yml` (or `~/.config/arc/config.yml`).
## Creating a Repository
```sh
arc init [path]
```
This creates a `.arc/` directory containing:
- `commits/` — commit storage
- `bookmarks/` — branches (default: `main`)
- `tags/` — tagged commits
- `stashes/` — stashed changes
- `config.yml` — repository config
- `HEAD` — current position
The default remote is `origin`.
## Tracking Changes
Arc tracks changes automatically — there is no staging area.
```sh
arc status # see what changed
arc commit <message> # commit all changes
arc log # view history
arc diff # see diffs
```
## Ignoring Files
Create a `.arcignore` or `.ignore` file with gitignore-like glob syntax. Prefix a pattern with `!` to negate it.
## Working with Git Repositories
Clone a git repo:
```sh
arc clone <url> [path]
```
Migrate an existing git repo (run inside the repo):
```sh
arc migrate
```

65
docs/git-bridge.md Normal file
View file

@ -0,0 +1,65 @@
# Git Bridge
Arc uses an internal git bridge to interoperate with git remotes. Since Arc uses its own delta-based storage format (ZSTD-compressed MessagePack), it maintains a shadow bare git repository to translate between formats when communicating with git servers.
## Shadow Repository
- Located at `.arc/git/` — a bare git repository created automatically on first use.
- A bidirectional mapping between arc commit IDs and git OIDs is stored in `.arc/git-map.yml`.
- The map has two fields: `arc_to_git` and `git_to_arc`, both string-to-string mappings.
## Commit Conversion
**Arc → Git:** The bridge materializes the full file tree at each arc commit, writes it as a git tree object, and creates a git commit with the same message, author, and timestamp. Parents are recursively converted. Cached mappings are reused.
**Git → Arc:** The bridge reads the git tree, computes deltas against the parent's tree using Arc's `detect_changes`, and creates arc `CommitObject` entries. Author info and timestamps are preserved from the git commit.
## Authentication
- SSH authentication is attempted first via the SSH agent, then by scanning `~/.ssh/` for key files (`id_ed25519`, `id_rsa`, `id_ecdsa`).
- Interactive/password authentication is not supported.
- Up to 4 authentication attempts are made before failing.
## Commands
### Push (`arc push [-r remote]`)
- Converts all arc commits on the current bookmark to git commits.
- Pushes the current bookmark as a git branch to the remote.
- Only pushes the active bookmark's branch.
- Uses fast-forward only; diverged histories produce an error.
- Default remote is `origin` (configurable via `default.remote`).
### Pull (`arc pull [-r remote]`)
- Fetches from the remote into the shadow git repo.
- Converts new git commits to arc commits.
- Fast-forward only — if the local bookmark has diverged, an error is returned.
- Updates the bookmark pointer and worktree.
### Clone (`arc clone [-b branch] <url> [path]`)
- Clones the git remote into a new arc repository.
- Imports all branches as bookmarks and tags.
- Checks out the specified branch (or the remote HEAD, or `main` by default).
- Writes the full worktree after import.
### Migrate (`arc migrate`)
- Converts an existing git repository (in the current directory) to an arc repository.
- Creates `.arc/` alongside the existing `.git/`.
- Imports all `refs/heads/*` as bookmarks and `refs/tags/*` as tags.
- Preserves remotes from the git config.
- Sets HEAD to the same branch as the git repo's HEAD.
### Sync (`arc sync [-p]`)
- Ensures the shadow git repo mirrors all arc bookmarks and tags.
- Converts any new arc commits to git commits and updates git refs.
- With `-p` (`--push`), also pushes all refs to the default remote.
## Remotes
- Stored in `.arc/remotes.yml` as a YAML map of name → URL.
- Managed with `arc remote add|rm|list`.
- When pushing to git, bookmarks become git branches and tags become git tags.