Zaphod is a small Git workflow tool for developers who often work between two related branches.
It remembers a branch pair, shows where you are, and switches to the other side only when the repository is safe to touch. The goal is not to replace Git. The goal is to make one repetitive workflow easier without hiding what is happening.
Zaphod is in early development. The current 0.1.x CLI can:
- Detect the current Git repository.
- Pair two branches in repo-local metadata.
- Show the active pair status.
- Switch to the paired branch.
- Guard agent switches against target-branch claim conflicts.
- Preview a safe switch without changing branches.
- List, rename, and remove branch pairs.
- Run preflight checks for humans and coding agents, including current and paired target claim guards.
- Assert the expected branch, pair, pair side, current claim, or paired target claim before scripted work starts or resumes.
- Claim, heartbeat, and release a pair/branch for an agent session.
- Claim, refresh, and release the paired target branch without switching, so guarded agent switches can require an existing target claim.
- Emit a handoff snapshot for agent-to-agent continuation.
- Refuse unsafe switches when the worktree is dirty or Git is mid-operation.
- Emit JSON status, switch, and pair-mutation results for scripts.
- Diagnose repository, metadata, branch-pair, and claim health in text or JSON.
- Generate shell completions.
Until Zaphod reaches 1.0, command names and output may change between minor
versions.
Git is powerful, but switching between two related branches can become noisy:
mainand a feature branch.- A backend branch and a frontend branch.
- A review branch and an implementation branch.
- Two worktrees used for the same task.
Zaphod keeps that relationship explicit:
feature/api <-> feature/ui
Then it gives you a small set of commands for checking and moving between the two sides safely.
Zaphod is not a general Git replacement. For normal human Git users, it is a small safety and convenience tool. Its stronger direction is agent-safe Git workflow guardrails: giving humans and coding agents machine-readable checks, safe branch-pair movement, and clear refusal behavior before touching repository state.
Coding agents can use Zaphod as a preflight gate:
zaphod preflight --agent codex --json
zaphod preflight --agent codex --branch feature/api --side left --json
zaphod preflight --agent codex --require-claim --json
zaphod preflight --agent codex --require-target-claim --json
zaphod preflight --agent codex --stale-after 2h --json
zaphod claim --agent codex --pair api --side left --note "editing API routes" --json
zaphod claim --agent codex --pair api --stale-after 2h --json
zaphod heartbeat --agent codex --pair api --side left --note "adding tests" --json
zaphod heartbeat --agent codex --pair api --stale-after 2h --json
zaphod assert --pair api --side left --json
zaphod assert --pair api --side left --agent codex --require-claim --json
zaphod assert --pair api --agent codex --require-target-claim --json
zaphod handoff --agent codex --require-claim --json
zaphod handoff --agent codex --require-target-claim --json
zaphod handoff --agent codex --side left --stale-after 2h --json
zaphod status --json
zaphod switch --dry-run --json
zaphod switch --agent codex --require-claim --json
zaphod doctor --jsonThis lets an agent check branch, worktree, merge, rebase, and pair state before editing files or switching branches.
This matters most when an agent is operating from instructions such as "make the API change on the backend branch, then update the UI branch." Without a guard, the agent has to infer whether the current branch is the correct place to edit. With Zaphod, the script can make that expectation explicit:
zaphod assert --pair search --side left --jsonIf the repository is on the UI side, outside the pair, detached, or missing the expected pair, the command fails before any file edits happen. The agent can then stop, report the mismatch, or ask for human approval instead of continuing on the wrong branch.
When several agents, scripts, or terminals may touch the same repository, claims add a lightweight coordination layer:
zaphod claim --agent codex --pair search --note "backend search endpoint" --json
zaphod claim --agent codex --pair search --target --note "reserve UI side" --json
zaphod claim --agent codex --pair search --stale-after 2h --json
# work on the branch
zaphod preflight --agent codex --require-claim --json
zaphod preflight --agent codex --require-target-claim --json
zaphod assert --pair search --agent codex --require-claim --json
zaphod assert --pair search --agent codex --require-target-claim --json
zaphod handoff --agent codex --require-target-claim --json
zaphod heartbeat --agent codex --pair search --note "writing regression tests" --json
zaphod heartbeat --agent codex --pair search --target --note "still reserving UI side" --json
zaphod heartbeat --agent codex --pair search --stale-after 2h --json
zaphod claims --current --json
zaphod claims --target --json
zaphod claims --conflicts-for codex --current --json
zaphod claims --pair search --side left --json
zaphod claims --pair search --stale-after 2h --json
zaphod prune-claims --pair search --stale-after 2h --json
zaphod prune-claims --current --stale-after 2h --json
zaphod prune-claims --target --stale-after 2h --json
zaphod prune-claims --pair search --side left --stale-after 2h --json
zaphod prune-claims --pair search --stale-after 2h --apply
zaphod prune-claims --orphaned --json
zaphod prune-claims --orphaned --apply
zaphod unclaim --agent codex --pair search
zaphod unclaim --agent codex --pair search --branch feature/api
zaphod unclaim --agent codex --pair search --side left
zaphod unclaim --agent codex --pair search --targetClaims are local metadata only. They do not lock Git, modify branches, or delete anything. They make accidental overlap visible so another agent can refuse to start on the same pair and branch.
Use --note on claim or heartbeat when an agent should leave short local
context for the next person or process that sees the claim. Notes are optional,
stored in repo-local metadata, and surfaced in JSON claim reports.
Agent-aware switching extends that refusal to branch movement. With
zaphod switch --agent codex, Zaphod checks the target branch before switching
and refuses if another agent owns that pair/branch claim. With
--require-claim, the target branch must already be claimed by the requested
agent before the switch can happen.
Stale-claim filters help automation notice abandoned sessions after crashes,
terminal closes, or interrupted agent runs. They are read-only, so cleanup
still requires an explicit unclaim or prune-claims --apply.
Long-running agents can call heartbeat while work is in progress. It refreshes
only an existing claim owned by that agent on the current pair and branch, so
fresh sessions stay visible without allowing silent takeover of another claim.
For handoffs between agents or terminals, zaphod handoff --json captures the
current branch, selected pair status, active claims, and claim readiness in one
read-only report. With --stale-after, the handoff can also mark old claim
conflicts as stale without removing them. With --branch or --side, the
handoff can also prove the snapshot was captured from the expected branch or
pair side before another agent trusts it. When --agent is present, handoff
JSON also includes target_claim, so the receiving agent can see whether the
paired target branch is already claimed before it attempts a switch. With
--require-target-claim, handoff refuses unless that paired target branch is
already claimed by the requested agent.
Zaphod is intentionally cautious.
The CLI should refuse operations that could surprise the user or disturb local work. In particular, switching should fail when:
- The current directory is not inside a Git repository.
- The worktree has uncommitted changes.
- Git is in the middle of a merge or rebase.
- The current HEAD is detached.
- The current branch is not part of a known pair.
- The target branch cannot be found.
Forceful behavior may be added later, but the first release prioritizes clear refusals over convenience.
Download the latest binary for your platform from the GitHub Releases page.
Linux:
curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-linux
curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-linux.sha256
sha256sum -c zaphod-linux.sha256
chmod +x zaphod-linux
sudo install -m 0755 zaphod-linux /usr/local/bin/zaphodmacOS:
curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-macos
curl -LO https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-macos.sha256
shasum -a 256 -c zaphod-macos.sha256
chmod +x zaphod-macos
sudo install -m 0755 zaphod-macos /usr/local/bin/zaphodWindows PowerShell:
Invoke-WebRequest -Uri https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-windows.exe -OutFile zaphod-windows.exe
Invoke-WebRequest -Uri https://github.com/kaleab-kali/zaphod-cli/releases/latest/download/zaphod-windows.exe.sha256 -OutFile zaphod-windows.exe.sha256
$expected = (Get-Content .\zaphod-windows.exe.sha256).Split(' ')[0]
$actual = (Get-FileHash .\zaphod-windows.exe -Algorithm SHA256).Hash.ToLower()
if ($actual -ne $expected) { throw "checksum mismatch" }
New-Item -ItemType Directory -Force "$HOME\bin"
Move-Item .\zaphod-windows.exe "$HOME\bin\zaphod.exe"Add $HOME\bin to your PATH if it is not already there.
Then confirm the binary is available:
zaphod --version
zaphod --helpZaphod is not published to crates.io yet. Install from a local checkout:
cargo install --path .Then confirm the binary is available:
zaphod --helpInside a Git repository with two existing local branches:
zaphod init feature/ui
zaphod status
zaphod switchinit uses the current branch as one side of the pair. You can also name both
branches explicitly:
zaphod pair feature/api feature/ui
zaphod status
zaphod switchExample status output:
Pair: default
Current: feature/api
Other: feature/ui
Worktree: clean
Git state: ready
Switch: allowed
If the worktree is dirty, switching is refused:
Pair: default
Current: feature/api
Other: feature/ui
Worktree: dirty
Git state: ready
Switch: refused (worktree has uncommitted changes)
Pair your main integration branch with the branch you are actively changing:
zaphod pair main feature/search
zaphod status
zaphod switchThis keeps the relationship explicit while still letting Git own the actual branch state.
Pair two branches that represent different sides of the same task:
zaphod pair feature/api feature/ui --name search
zaphod status --name search
zaphod switch --name searchThis is useful when backend and frontend changes move together, but should stay reviewable as separate branches.
Pair an implementation branch with a review or experiment branch:
zaphod pair feature/parser review/parser-notes --name parser-reviewUse zaphod list when a repository has multiple named pairs.
Pair names are local labels stored in the repository metadata. Names may contain
letters, numbers, ., _, and -.
Good names are short and tied to the workflow:
default
api
search-ui
parser-review
Avoid names that depend on one person's machine or temporary context.
Store a branch pair in the current repository:
zaphod pair feature/api feature/uiUse --name to store more than one pair:
zaphod pair main feature/api --name apiBoth branches must already exist locally. Branch names are validated with Git's branch-name rules before Zaphod stores the pair.
Use --json to emit a structured report with the action, repository root,
metadata path, stored pair, and previous pair when an existing name is updated.
Store a branch pair using the current branch and another local branch:
zaphod init feature/ui
zaphod init feature/ui --name apiBoth branches must already exist locally. init refuses detached HEADs, invalid
branch names, missing branches, and attempts to pair the current branch with
itself.
Use --json to emit the same pair-mutation report as pair, with
"action": "initialized" for newly stored pairs.
Check whether the current repository is ready for paired-branch work:
zaphod preflight --json
zaphod preflight --agent codex --json
zaphod preflight --branch feature/api --side left --json
zaphod preflight --agent codex --require-claim --json
zaphod preflight --agent codex --require-target-claim --json
zaphod preflight --agent codex --stale-after 2h --jsonPreflight is read-only. It reports the requested pair, current branch, paired target branch, worktree state, Git operation state, switch readiness, and any refusal reasons. It exits successfully when the pair is ready and exits with an error when the repository is not ready.
Use --agent to check whether another agent has already claimed the current
pair and branch. This does not create or remove a claim; it only reports whether
claiming would be allowed. JSON output also includes target_claim readiness
for the paired branch that zaphod switch would move to, so an agent can see
target-branch claim conflicts before it attempts a switch.
Use --require-claim with --agent when an agent is resuming work and must
prove it already owns the current pair and branch claim. This is read-only: it
does not refresh the claim timestamp. JSON reports include claim_required,
claim_owned, and owned_claim fields.
Use --require-target-claim with --agent when startup checks must prove the
paired target branch is already reserved by the same agent before the agent
continues. This is useful before workflows that expect a later guarded switch
to the other branch.
Use --branch or --side when automation must prove it is about to edit the
intended branch before doing any work:
zaphod preflight --agent codex --name api --side left --jsonThe report includes an expectation section when either option is used. A
wrong branch or wrong pair side makes preflight fail before an agent starts
editing files.
When --agent is present, preflight also reports the repo-local metadata
mutation lock. If .git/zaphod/metadata.lock is present, preflight marks claim
readiness as blocked because claim would refuse to write while another Zaphod
process appears to hold the metadata lock.
Use --stale-after with --agent to mark old claim conflicts in the preflight
report. Preflight still refuses the conflict; stale reporting is only a signal
for scripts or humans to decide whether an explicit unclaim is appropriate.
The same stale marker is included on target_claim when the paired target
branch is claimed by another agent.
Fail fast when the current repository state does not match an expected branch, pair, or pair side:
zaphod assert --branch feature/api
zaphod assert --pair api
zaphod assert --side left
zaphod assert --pair api --side right --json
zaphod assert --pair api --agent codex --require-claim --json
zaphod assert --pair api --agent codex --require-target-claim --jsonIf no selector is provided, assert checks that the current branch belongs to
the default pair. --side uses the default pair unless --pair is provided.
This command is read-only and is designed for scripts and coding agents that need to prove they are in the right place before editing files.
With --agent, assert also checks claim readiness for the current pair and
branch. With --require-claim, the requested agent must already own that
claim. This is useful during long-running work because unlike preflight, it
does not require a clean worktree and does not refresh the claim timestamp.
Use --require-target-claim with --agent when the script must prove the
paired target branch is already reserved by the same agent before continuing.
This is useful after claim --target and before handing work to a script that
may later switch branches.
Claim the current pair and branch for an agent session:
zaphod claim --agent codex --pair api
zaphod claim --agent codex --pair api --json
zaphod claim --agent codex --pair api --note "editing API routes" --json
zaphod claim --agent codex --pair api --clear-note --json
zaphod claim --agent codex --pair api --target --json
zaphod claim --agent codex --pair api --side left --json
zaphod claim --agent codex --pair api --stale-after 2h --jsonclaim writes repo-local metadata under .git/zaphod/claims.toml. It refuses
when another agent has already claimed the same pair and current branch. It also
uses the same readiness checks as preflight, so it refuses when the worktree
is dirty, Git is mid-merge or mid-rebase, the current branch is outside the
pair, or the paired target branch is missing.
Agent names may contain only letters, numbers, ., _, and -.
Use --note to attach short local context to the claim. Notes are optional,
must be non-blank, cannot contain control characters, and are limited to 240
characters. Use --clear-note to remove an existing note while keeping the
claim.
Use --branch or --side when the claim should only be written from a specific
branch or pair side. This lets an agent combine "am I in the right place?" and
"claim this work" into one guarded command.
Use --target to claim the paired branch that zaphod switch would move to
without switching branches. This lets an agent reserve the other side of a
paired task before running zaphod switch --agent <name> --require-claim.
Use --stale-after to add conflict_stale to JSON conflict reports. This is a
reporting aid only: claim still refuses the conflict and never takes over or
removes another agent's claim automatically.
Refresh an existing agent session claim for the current pair and branch:
zaphod heartbeat --agent codex --pair api
zaphod heartbeat --agent codex --pair api --json
zaphod heartbeat --agent codex --pair api --note "adding tests" --json
zaphod heartbeat --agent codex --pair api --clear-note --json
zaphod heartbeat --agent codex --pair api --target --json
zaphod heartbeat --agent codex --pair api --side left --json
zaphod heartbeat --agent codex --pair api --stale-after 2h --jsonheartbeat updates the claim timestamp in .git/zaphod/claims.toml so
stale-claim checks can distinguish an active long-running session from an
abandoned one. It requires the claim to already exist, refuses if another agent
owns the same pair and branch, and does not switch branches, clean files, or
change Git history.
Unlike claim, heartbeat can run while the worktree is dirty. This lets an
agent refresh its own claim while it is actively editing files.
Use --note to replace the existing claim note while refreshing the timestamp.
Use --clear-note to remove the note while keeping the claim active. When no
note option is provided, heartbeat preserves the existing note.
Use --branch or --side to refuse the refresh unless the current branch is
still the expected branch or pair side.
Use --target to refresh an existing claim on the paired branch that
zaphod switch would move to without switching branches. This keeps a reserved
target branch fresh while the agent continues work on the current branch.
Use --stale-after to add conflict_stale to JSON conflict reports. This is a
reporting aid only: heartbeat still refuses the conflict and never takes over
or removes another agent's claim automatically.
List active agent session claims:
zaphod claims
zaphod claims --json
zaphod claims --current --json
zaphod claims --target --json
zaphod claims --conflicts-for codex --current --json
zaphod claims --pair api --side left --json
zaphod claims --agent codex --pair api --branch feature/api --json
zaphod claims --pair api --stale-after 2h --jsonUse filters when a script needs to check a specific agent, pair, branch, the
current branch, paired target branch, pair side, conflict owner, or stale-claim
window without parsing unrelated claim entries. Use --target to inspect
claims on the paired branch that zaphod switch would move to without
switching branches. Use --side left or --side right to inspect claims for a
pair side without switching branches. Durations use a positive number followed
by s, m, h, or d.
Use --conflicts-for <agent> to show claims owned by other agents. Combined
with --current, --branch, or --side, this gives agents a read-only
blocking-claim summary before they start or resume work.
Preview or remove stale or orphaned agent session claims:
zaphod prune-claims --stale-after 2h
zaphod prune-claims --pair api --stale-after 2h --json
zaphod prune-claims --current --stale-after 2h --json
zaphod prune-claims --target --stale-after 2h --json
zaphod prune-claims --pair api --side left --stale-after 2h --json
zaphod prune-claims --pair api --stale-after 2h --apply
zaphod prune-claims --orphaned --json
zaphod prune-claims --orphaned --applyprune-claims defaults to dry-run mode and does not change metadata unless
--apply is present. Use --agent, --pair, and --branch to narrow the
cleanup scope. Use --current to narrow cleanup to the current Git branch
without scripting a separate branch lookup. Use --target to narrow cleanup to
the paired branch that zaphod switch would move to without switching branches.
Use --side left or --side right to clean claims for a pair side without
switching branches. Use --orphaned to match claims for missing pairs, branches
outside their configured pair, or local branches that no longer exist. The
command only edits
.git/zaphod/claims.toml; it never switches branches, deletes files, or
changes Git history.
Release an agent session claim for the current pair and branch:
zaphod unclaim --agent codex --pair api
zaphod unclaim --agent codex --pair api --json
zaphod unclaim --agent codex --pair api --branch feature/api
zaphod unclaim --agent codex --pair api --target
zaphod unclaim --agent codex --pair api --side leftBy default, unclaim releases the matching claim for the current branch. Use
--branch to release a claim for another branch without switching to it. Use
--target to release a claim on the paired branch that zaphod switch would
move to without switching branches. Use --side to release a claim by the left
or right side of the named pair without scripting a pair-metadata lookup. This
is useful when an agent stopped early and left stale claim metadata behind.
unclaim only removes the matching claim metadata. It does not switch branches,
clean files, or alter Git history.
Emit a read-only snapshot for another agent, script, or terminal to continue from:
zaphod handoff
zaphod handoff --json
zaphod handoff --branch feature/api --side left --json
zaphod handoff --name api --agent codex --json
zaphod handoff --name api --agent codex --require-claim --json
zaphod handoff --name api --agent codex --require-target-claim --json
zaphod handoff --name api --agent codex --stale-after 2h --jsonThe handoff report includes repository root, current branch, worktree state,
Git operation state, selected pair status, active claims, and optional claim
readiness for the requested agent. When --agent is present, JSON output also
includes target_claim readiness for the paired branch that zaphod switch
would move to. It does not create claims, remove claims, or switch branches.
Use --stale-after with --agent when the receiving agent needs to know
whether a claim conflict looks abandoned. The value uses the same duration
format as other stale-claim checks: a positive number followed by s, m, h,
or d.
Use --require-claim with --agent when the receiving agent should only trust
the snapshot if that agent already owns the current pair and branch claim. This
is read-only: it does not refresh the claim timestamp or change claim metadata.
Use --require-target-claim with --agent when the receiving agent should
only trust the snapshot if that agent already owns the paired target branch
claim. This is useful after reserving the other side with claim --target
because it can run while the current worktree is dirty and does not switch
branches.
Use --branch and --side when the receiving agent should only trust a
handoff captured from a specific branch or pair side. The JSON report includes
an expectation section when either option is used. If the expectation fails,
handoff exits non-zero and reports the mismatch without creating claims,
removing claims, switching branches, or editing Git state.
When --agent is present, the handoff claim-readiness section includes the
same metadata lock state as preflight --agent.
Show the active pair status:
zaphod statusTo inspect every configured pair in the repository, use --all:
zaphod status --allFor scripts, use JSON output:
zaphod status --json
zaphod status --all --jsonJSON fields use script-friendly names:
{
"pair": "default",
"current": "feature/api",
"other": "feature/ui",
"worktree": "clean",
"git_state": "ready",
"switch_allowed": true,
"refusal_reasons": []
}When switching is refused, switch_allowed is false and
refusal_reasons contains one or more values:
{
"pair": "default",
"current": "feature/api",
"other": "feature/ui",
"worktree": "dirty",
"git_state": "ready",
"switch_allowed": false,
"refusal_reasons": ["dirty_worktree"]
}Known refusal reasons are dirty_worktree, merge_in_progress,
rebase_in_progress, and target_branch_missing.
status --all --json returns an array. Each item includes the pair's left
and right branch names, whether the pair is active for the current branch,
branch existence booleans, and switch availability details. Inactive pairs use
current_branch_not_paired as the refusal reason.
Switch to the other branch in the pair:
zaphod switchZaphod refuses to switch if the worktree is dirty, a merge is in progress, or a
rebase is in progress. It also refuses when the paired target branch no longer
exists. Use zaphod status to see the current refusal reason before switching.
To preview the target without changing branches, use --dry-run:
zaphod switch --dry-runDry-run mode applies the same safety checks as a real switch.
For agent sessions, --agent checks the target branch claim before switching:
zaphod switch --agent codex
zaphod switch --agent codex --require-claimWith --agent, Zaphod refuses to switch into the target branch when another
agent owns that pair/branch claim. With --require-claim, the requested agent
must already own the target branch claim before the switch can happen.
For scripts, use JSON output:
zaphod switch --json
zaphod switch --dry-run --json
zaphod switch --agent codex --require-claim --jsonWhen switching is refused, JSON output still reports the pair, current branch, target branch, worktree state, Git state, and refusal reasons before exiting with the same safety-refusal code.
List all branch pairs configured for the current repository:
zaphod listFor scripts, use JSON output:
zaphod list --jsonRename a branch pair label without changing either Git branch:
zaphod rename default apiZaphod refuses to overwrite an existing pair name. Pair names must contain only
letters, numbers, ., _, and -.
Use --json to emit the renamed pair and its previous name in a structured
pair-mutation report.
Diagnose Git availability, repository state, metadata health, and configured branch pairs:
zaphod doctordoctor is read-only. It exits successfully when the repository and configured
pairs look healthy, and exits with an error when it finds problems such as
corrupt metadata or missing paired branches.
For scripts, use JSON output:
zaphod doctor --json
zaphod doctor --stale-after 2h --jsonUse --stale-after to make doctor report stale agent claims as repository
health problems. This is read-only; cleanup still requires unclaim or
prune-claims --apply.
doctor also reports claim metadata that no longer matches the repository,
including claims for removed pairs, claims on branches outside the configured
pair, and claims on local branches that no longer exist.
doctor reports the repo-local metadata mutation lock as clear or locked. A
present .git/zaphod/metadata.lock makes the report unhealthy because pair and
claim write commands will refuse to run while another Zaphod process appears to
hold the lock.
Remove a branch pair:
zaphod unpairUse --name to remove a named pair:
zaphod unpair --name apiUse --json to emit the removed pair and metadata path instead of prose.
Generate shell completions to stdout:
zaphod completions bash
zaphod completions zsh
zaphod completions fish
zaphod completions powershell
zaphod completions elvishRedirect the output to the location expected by your shell.
Zaphod uses stable app-level exit codes for scripts:
0 success
1 runtime or unexpected failure
2 invalid input, missing branch, missing pair, or incompatible repository state
3 safety refusal, such as a dirty worktree or in-progress merge
4 doctor found repository, metadata, or pair health problems
Use --json-errors to emit app-level failures as JSON on stderr:
zaphod --json-errors switchExample error output:
{"error":{"exit_code":3,"kind":"switch_refused","message":"refusing to switch: worktree has uncommitted changes"}}$ git branch --show-current
feature/api
$ zaphod pair feature/api feature/ui
Paired 'default': feature/api <-> feature/ui
$ zaphod status
Pair: default
Current: feature/api
Other: feature/ui
Worktree: clean
Git state: ready
Switch: allowed
$ zaphod switch
Switched pair 'default': feature/api -> feature/ui
Zaphod stores branch-pair and agent-claim data inside the current repository
under .git/zaphod.
This keeps Zaphod state local to the repository and avoids changing global Git configuration. The metadata format is TOML so it stays readable and easy to debug.
Metadata files include a top-level schema_version. Files without this field
are treated as schema version 1 for compatibility with early releases. Files
with a newer unsupported schema version are refused with a clear error instead
of being partially interpreted.
Commands that write pair or claim metadata create a short-lived
.git/zaphod/metadata.lock directory while they load, modify, and save the
metadata. If another Zaphod process already holds that lock, the command refuses
instead of risking an overwritten update.
Metadata saves use atomic file replacement so interrupted writes do not leave
.git/zaphod/pairs.toml or .git/zaphod/claims.toml partially written.
Zaphod is a Rust CLI using:
clapfor command parsing.serde,serde_json, and TOML for metadata and machine-readable output.- Carefully wrapped
gitcommands for repository operations.
The implementation is organized around three boundaries:
- CLI parsing and command output.
- Core branch-pair and safety logic.
- Git repository adapter.
Run the local quality gate before opening a pull request:
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-featuresBuild a release binary locally:
cargo build --releaseThe binary will be written to target/release/zaphod on Unix-like systems and
target/release/zaphod.exe on Windows.
The release workflow builds binary artifacts for Linux, macOS, and Windows and
publishes them as GitHub Release assets when a v* tag is pushed. Each binary
is published with a matching .sha256 checksum file:
git tag -a v0.1.0 -m "v0.1.0"
git push origin v0.1.0Verify downloaded release assets before running them:
sha256sum -c zaphod-linux.sha256
shasum -a 256 -c zaphod-macos.sha256On Windows PowerShell:
Get-FileHash .\zaphod-windows.exe -Algorithm SHA256
Get-Content .\zaphod-windows.exe.sha256Pull requests that touch release-critical files validate the release build without publishing a GitHub Release.
Before tagging a release, run the full quality gate and manually test the release binary in a temporary Git repository.
Contributions are welcome. The most useful contributions are:
- Clear bug reports.
- Small pull requests with tests.
- Documentation improvements.
- Safety-focused edge cases around Git state.
Before opening a pull request, please make sure the project formats, lints, and tests cleanly.
Zaphod is licensed under the MIT License. See LICENSE.