safegit

command module
v0.29.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 42 Imported by: 0

README

safegit

safegit is a git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository. It is built for repositories that several AI agent sessions work in at the same time, where standard git races on .git/index and files leak between commits. Its output is ordinary git commits, so teammates, CI and code review tools see nothing unusual.

The problem

When multiple AI agent sessions share a single git repository, they race on .git/index. Two agents staging files at the same time produce mixed commits -- files from one agent leak into another's commit, or writes are silently lost. Standard git has no built-in isolation for this scenario.

The solution

safegit wraps git plumbing commands behind a two-phase commit pipeline that keeps every invocation isolated. Per-invocation temporary index files prevent staging races. Ref updates use per-ref locks with compare-and-swap (CAS) retry, so concurrent commits to the same branch serialize correctly. An append-only operation log records every mutation. The output is standard git commits -- teammates, CI, and code review tools see nothing unusual.

A deliberate subset

safegit does not promise full git support and never will. It implements a small, opinionated subset of git's functionality, chosen for agent-heavy workflows. When a git feature, command, flag, or edge case is judged actively harmful or irrelevant for that workflow, safegit deliberately omits it and never looks back. Every such omission is recorded in docs/divergences.md, unapologetically. safegit is for agents, not for all humans.

Install

From source (requires the Go version go.mod declares -- currently 1.25.7):

go install github.com/smm-h/safegit@v0

@v0, not @latest: safegit issues no 1.x tags, so @latest cannot resolve to a real release. Pin an exact version (@v0.28.0) when you need one.

Pre-built binaries are available on GitHub Releases via goreleaser.

Quick start

cd your-repo
safegit commit -m "add feature X" -- src/foo.go src/bar.go
safegit push --refs head

safegit auto-initializes on first use (creates .git/safegit/); a --dry-run deliberately does not, so previewing in a fresh repository writes nothing at all. Use safegit doctor --action uninstall to remove safegit from a repository -- a repository-wide operation that lists every path it will remove, including the state of worktrees other than the one you are standing in, before asking you to confirm.

Commands

Command Description
commit stage and commit specified files in a single atomic operation
mv move tracked paths and commit the moves with their records in one operation
merge-continue conclude a merge git stopped before committing. Every conflicted path is named with --resolve (or in a --resolve-file), and safegit writes the merge commit itself: HEAD plus the MERGE_HEAD line as parents; the merge's whole staged result as its tree, so a path the merge staged cleanly is never dropped; git's own message draft with its comment block stripped, or -m; the repository's commit-msg hook run and safegit's trailers injected; and the merge's whole state-file set removed afterwards, so a later commit is not refused. An empty merge needs no flag -- a merge commit records its parents whether or not the tree changed. Two merge shapes only raw git can start are REFUSED, each naming git's own 'merge --continue' and '--abort': an OCTOPUS, because every check safegit makes over a merge is written against two sides, and a content conflict git recorded with no AUTO_MERGE, which is a non-default strategy's signature and leaves the marker verification nothing to read
cherry-pick-continue conclude a cherry-pick git stopped before committing. Every conflicted path is named with --resolve (or in a --resolve-file), and safegit writes the commit itself: one parent, the AUTHOR preserved from the commit being applied while the committer is you, git's own message draft with its comment block stripped or -m, the repository's commit-msg hook run, and the cherry-pick's state files removed afterwards. A QUEUED sequence -- git cherry-pick <a> <b>, which only raw git can start, since safegit's cherry-pick applies one commit -- is REFUSED, naming git's own 'cherry-pick --continue' and '--abort': the queue is part of the state a conclusion removes, so finishing one step of it would throw the rest away
revert-continue conclude a revert git stopped before committing. Every conflicted path is named with --resolve (or in a --resolve-file). For a SINGLE revert safegit writes the commit itself: one parent, YOU as both author and committer -- a revert is your own new change, not the reverted commit author's, which is git's own division and the opposite of what a cherry-pick does -- git's own message draft with its comment block stripped or -m, the repository's commit-msg hook run, and the revert's state files removed afterwards. Note the stage keywords: a revert applies an INVERSE patch, so theirs is what the reverted commit's parent held -- resolving to theirs keeps the revert, resolving to ours keeps the commit being reverted. A QUEUED sequence -- git revert <a> <b>, which only raw git can start, since safegit's revert undoes one commit -- is REFUSED, naming git's own 'revert --continue' and '--abort': the queue is part of the state a conclusion removes, so finishing one step of it would throw the rest away
switch switch to another BRANCH, guarded twice before git runs: the worktree operation lock, held for the whole command, and then a check for uncommitted work. The command line is a deliberate subset of git's: an existing branch name, or -c to create one, and nothing else. A tag, an object name or any other commit-ish is refused, because switching onto one detaches HEAD -- the state safegit's commit, conclusion and undo paths all refuse -- and the refusal names the raw-git command for the rare deliberate case. --detach, -C, --force/--discard-changes, --orphan and --merge are refused, each naming why. There is NO file mode and no 'safegit checkout': git's checkout of files over the working tree destroys uncommitted work with no record anywhere, so safegit does not implement it at all
merge merge one branch into the current one, and author the result: safegit decides the fast-forward itself and moves the ref under compare-and-swap, or runs git's merge machinery with --no-ff --no-commit and commits the staged result through its own pipeline -- so a merge safegit performed carries safegit's trailers, ran the repository's commit-msg hook and is reversible with 'safegit undo'. A merge git stops on a conflict parks, and 'safegit merge-continue' concludes it; 'safegit merge --continue' is refused and names that command. The command line is a deliberate subset of git's: exactly one branch (no octopus), no strategy selection, no --squash, no --edit and no --autostash. --no-commit computes the merge and leaves it parked even when it is clean. On an UNBORN branch -- one with no commits yet -- a merge can only be a fast-forward, so --no-ff and --no-commit are both refused there before git runs, with the reason
rebase rebase the current branch onto upstream, guarded before git runs: the worktree operation lock -- held for the whole rebase, an interactive one's editor session included, so a second safegit process in this worktree waits that long -- then a check for uncommitted work, and then a refusal to rebase over ANOTHER operation git already has in flight (a rebase over a parked revert exits 0 and strands that revert's state files behind it, blocking every later commit). That last check is kind-scoped: it refuses an in-flight state that is not a REBASE, so a rebase's own --continue, --abort and --skip pass by construction. Those three stay git's either way: safegit has no verb that finishes a rebase. A rebase on an UNBORN branch -- one with no commits yet, so with nothing to replay -- is refused before git runs too, by a separate check beside that one. The command line is a deliberate subset of git's: exactly one upstream, --onto, -i, --autostash and --rebase-merges (whose optional value is attached only). The apply backend and its patch options, --exec and --root are refused, each naming why
reset reset HEAD with guards that prevent accidental data loss. The worktree operation lock is taken for EVERY reset, because every reset moves HEAD; the uncommitted-work check applies to the modes that WRITE working-tree files -- --hard, --merge and --keep -- while --soft and --mixed move only the ref and the index. Which is which is derived from safegit's git classification table, never re-read from the argument list here. The command line is a deliberate subset of git's: one of the five modes with a commit. The PATHSPEC form is refused -- it writes the shared index entry by entry, which is the one file safegit's design keeps out of -- and so is --patch
bisect binary search through commits to find a bug. The worktree operation lock is taken for EVERY invocation; the uncommitted-work check applies to the STEPPING subcommands (start, good, bad, old, new, skip, run, replay, reset), each of which checks another commit out, and not to the reporting ones (terms, log, view). Which is which is derived from safegit's git classification table, never kept as a list here -- and so is which subcommands may be typed at all: a word outside that vocabulary is refused before git runs, as is every option. 'bisect start' on an UNBORN branch is refused before git runs as well: there is no range of commits to search there
push push refs to remote with pre-pre-push hooks and automatic retry
pull fetch from a remote and merge what was fetched, with the merge strategy stated explicitly: --merge-strategy is required and has no default, so a pull never depends on git's own configuration to decide whether it may create a merge commit. The merge step is safegit's own -- the same one 'safegit merge' performs -- so a pull that cannot fast-forward produces a commit carrying safegit's trailers, run through the repository's commit-msg hook and reversible with 'safegit undo'; a pull that can fast-forward moves the ref under compare-and-swap and puts the index and the working tree in step with it. A merge git stops on a conflict parks, and 'safegit merge-continue' concludes it. --rebase is refused and names the two commands that do it
doctor run diagnostic health checks on the repository and optionally repair issues
cherry-pick apply ONE commit onto the current branch, and author the result: git computes the pick with --no-commit and safegit commits the staged result through its own pipeline -- so a pick safegit performed carries safegit's trailers, ran the repository's commit-msg hook and is reversible with 'safegit undo'. The AUTHOR is preserved from the commit being applied and the committer is you, which is git's own division. A pick git stops on a conflict parks -- safegit writes CHERRY_PICK_HEAD itself, because 'git cherry-pick --no-commit' does not -- and 'safegit cherry-pick-continue' concludes it; 'safegit cherry-pick --continue' is refused and names that command. The command line is a deliberate subset of git's: exactly one commit named as a commit (a range or any other revision set is refused, because a range hands the operation to git's sequencer even when it holds one commit), no --edit, no --ff, no --commit, no --cleanup, no signing and no empty-commit flags. --abort and --quit stay plain passthroughs, because they author nothing and are the way out of a state safegit must not refuse over; --no-commit is forwarded to git too, but it COMPUTES, so it takes the same refusal the restructured form does over an operation git already has in flight
revert revert ONE commit by applying its inverse patch, and author the result: git computes the inverse with --no-commit and safegit commits the staged result through its own pipeline -- so a revert safegit performed carries safegit's trailers, ran the repository's commit-msg hook, is reversible with 'safegit undo', and declares the INVERSE of every move record the reverted commit declared. YOU are recorded as both author and committer, because a revert is your own new change rather than the reverted author's; that is git's own division and the opposite of what a cherry-pick does. A revert git stops on a conflict parks, and 'safegit revert-continue' concludes it; 'safegit revert --continue' is refused and names that command. The command line is a deliberate subset of git's: exactly one commit named as a commit (a range or any other revision set is refused, because a range hands the operation to git's sequencer even when it holds one commit), no --edit, no --commit, no --cleanup and no signing. --abort and --quit stay plain passthroughs, because they author nothing and are the way out of a state safegit must not refuse over; --no-commit is forwarded to git too, but it COMPUTES, so it takes the same refusal the restructured form does over an operation git already has in flight
undo reverse the last safegit-authored operation using the oplog -- a commit, an mv, an amend, a reword, a merge, pull, cherry-pick or revert safegit's own commit pipeline authored, or a conclusion (merge-continue, cherry-pick-continue, revert-continue). A fast-forward is REFUSED rather than reversed: the tip it moved onto is a commit git created and safegit never rolls a branch back over one. It moves a REF and never the working tree
unlock release one of safegit's OWN lock files -- a per-ref lock, this worktree's operation lock, or the repository-wide rewrite lock -- left behind by a safegit process that was killed while holding it. It has nothing to do with git's .git/index.lock or any other lock git takes for itself. A lock whose holder is still alive is refused; ordinarily nothing needs this command, because a stale lock is reclaimed automatically by the next contender and 'safegit doctor --action fix' sweeps them, so it is the last-resort path for a filesystem where that reclamation cannot work
scan search git history for regex pattern matches across all objects and working tree files, scanning blobs, commit messages, tag annotations, and trailers with optional scope filtering and commit range selection
version print safegit version, Go runtime version, and git version
backup push, list, and restore per-branch history backups held in the tool-owned refs/backups namespace on a remote, so uncommitted-to-the-world work survives a lost machine without ever touching refs/heads
backup backup push the current branch to its backup slot refs/backups/ on the remote, after fetching that slot and refusing when it holds commits your history does not contain; the push is pinned with --force-with-lease to the exact SHA that was just observed (or to "this ref must not exist" for a first backup), so a concurrent backup from another machine is rejected rather than clobbered; plain git equivalent: git push --force-with-lease=refs/backups/: HEAD:refs/backups/
backup list list every backup slot present on the remote with the branch name and the commit each slot points at, so you can see which branches are backed up from which machine before restoring one; plain git equivalent: git ls-remote 'refs/backups/*'
backup restore fetch the current branch's backup slot from the remote and fast-forward the branch onto it, refusing when the local branch carries commits the backup does not contain so no local work is ever discarded; plain git equivalent: git fetch refs/backups/ && git merge --ff-only FETCH_HEAD
config show, get, or set safegit configuration key-value pairs
config show show all configuration values currently in effect for this repository, including built-in defaults and any user overrides from the .git/safegit/config.json file, printed as key-value pairs to stdout for inspection and debugging purposes
config get get the current value of a single configuration key from the .git/safegit/config.json file, printing the raw value to stdout so it can be captured by scripts or used in automation pipelines
config set set a configuration key to a new value in the .git/safegit/config.json file, creating the file if it does not exist yet, and persisting the change for all future safegit invocations in this repository
hook manage pre-pre-push hook scripts that run before every push
hook list list every pre-pre-push hook location safegit knows about, with its origin, its path and whether it is executable, so you can audit which checks run before every push. Three origins are shown: local, the tool-owned live store under the repository's common .git/safegit/hooks that hook install writes to and every worktree shares; tracked, the hooks the CHECKOUT provides in .safegit/hooks, which run because they are in that directory whether or not git tracks them, so cloning a repository and pushing from that checkout runs the repository's scripts; and legacy, the pre-migration location in git's own .git/hooks, which nothing runs any more and safegit hook migrate relocates. Non-executable and non-hook entries are listed too, because the hook an operator is asking about is usually the one that is NOT running
hook run run all installed pre-pre-push hooks (or a single named hook) immediately without performing an actual push, so you can verify that all configured hooks pass before committing to a real push operation. Discovery's own verdicts about the checkout reach here too: exit 24 while a hook is still in the pre-migration .git/hooks location, and exit 25 when a discovered hook is not executable, in EITHER store -- never a silent skip and never a 'no hooks to run', because a command whose whole purpose is to say whether the checks pass must not exit 0 because a check was passed over
hook install install a pre-pre-push hook by copying a script file into the live store under the repository's common .git/safegit/hooks directory and making it executable, so that safegit push runs it before any network I/O occurs. The store is keyed on the common git dir, so a hook installed from a linked worktree is the same hook every worktree of the repository runs. An existing destination is refused rather than overwritten
hook remove remove one hook from the tool-owned live store under the repository's common .git/safegit/hooks directory by name, naming either the store-relative path such as pre-pre-push.d/20-lint or just the base name, so a hook can be retired or replaced without deleting files by hand; a name that resolves only to a hook the checkout provides in .safegit/hooks is refused, because removing that one means deleting the file and committing that, and a name carried by both stores removes the live one and says the other still runs
hook migrate move safegit's hooks out of git's own .git/hooks directory into the tool-owned .git/safegit/hooks store, relocating the pre-pre-push file and the pre-pre-push.d directory unconditionally because those two names are the only ones safegit ever wrote there, and reporting success with an explanation when there is nothing to move. Both ends are under the repository's COMMON git dir, which is git's own hook directory in every worktree, so migration run from a linked worktree relocates the repository's hooks
author audit and rewrite commit author/committer identity — list all identities, check against expected values, and rewrite name or email across history
author list list all distinct author and committer identities across the entire commit history, showing name, email, role, and commit count for each unique identity — useful for auditing repositories with multiple contributors or detecting unwanted identity variations such as typos, old email addresses, or bot accounts that should be consolidated before a rewrite
author check check that all commits use the expected author and committer identity by scanning every commit in the repository history, reporting any deviations with the exact commit hashes and mismatched fields, and suggesting the corresponding safegit author rewrite command to fix each deviation found
author rewrite rewrite author and committer name or email across all commit history using git filter-branch style rewriting, replacing every occurrence of the old identity with the new one in both author and committer fields while preserving timestamps, commit messages, tree contents, and parent relationships so the rewritten history is otherwise identical to the original
scrub surgically rewrite git history to remove or replace sensitive content: file and match rewrite the commits, trees and blobs of a range the caller selects (--from or --entire-history), run applies a recipe of such operations in one coordinated pass, and verify only reads -- it confirms that the patterns named on its command line are absent from the whole object store
scrub file replace or remove a specific file across every commit in a SELECTED RANGE of history -- --from or --entire-history, one of which is required -- rewriting each affected commit tree to either substitute the file's contents with those of a sanitized file or delete it entirely from every snapshot in that range. A --delete also removes the move records naming that path, whole, since the path they refer to is being erased; a --replace-with edits no message, because the path still exists and a record naming it is still true
scrub match replace every occurrence of a regex pattern in the blobs, commit messages and tag annotations of a SELECTED RANGE of history -- --from or --entire-history, one of which is required -- rewriting commit trees so that sensitive values like secrets and credentials are removed from every snapshot in that range. A move record is rewritten as a record rather than as text: the substitution applies to the decoded paths and the pair is re-encoded, so the output always parses, a pattern written against the escaped spelling matches nothing, and a substitution whose result would no longer be a move is refused before any ref moves
scrub run execute a multi-operation scrub recipe from a TOML file, applying all pattern replacements and file removals across history in a single coordinated pass with topological commit ordering, overlap detection between operations, and automatic verification that no matched content survives in the rewritten object store — use --diff to preview all changes as unified diffs before committing to the rewrite
scrub verify confirm that the patterns named on the command line -- repeatable --pattern regexes, the operations of a scrub recipe file, or both -- are absent from every object in the git object store, scanning blobs, commit messages, and tag annotations and reporting detailed per-pattern pass or fail results with match locations for any violations found

Tree-mutating commands (switch, pull, merge, rebase, reset, bisect, cherry-pick, revert) run behind two coordination guards -- the worktree operation lock, then the uncommitted-work check -- and each validates its command line against an explicit allowlist first, so an option safegit has not considered is refused rather than passed to git. Some add a third check on top: the forms that COMPUTE an operation (merge, pull, and cherry-pick and revert in both their own and their forwarded --no-commit shape) refuse over any operation git already has in flight, and rebase refuses over one that is not itself a rebase -- so a rebase's own --continue still reaches git.

Every commit safegit makes is safegit's own. merge, cherry-pick, revert and pull use git only to compute a result and then write the commit through safegit's pipeline: trailered, commit-msg-hooked, recorded, and reversible with safegit undo. Each of them applies ONE thing, and a multi-commit or revision-range command line is refused naming the sequential form. safegit rebase is the one declared exception, where git replays and authors, always.

Branch navigation is safegit switch (a branch name, or -c to make one). There is no safegit checkout and no file-restoration mode at all: git's checkout -- <path> destroys uncommitted work with no record anywhere, so it is inexpressible here rather than refused.

When git parks a merge, cherry-pick or revert on a conflict, safegit finishes it rather than git: merge-continue, cherry-pick-continue and revert-continue take one --resolve 'path=ours|theirs|worktree|delete' per conflicted path and write the commit themselves, refusing a declaration that does not match the conflict, content that still holds a conflict block, or a working-tree write that would destroy a hand edit no side of the conflict accounts for.

How it works

The commit pipeline has two phases. Phase A (parallel-safe) resolves the tip of the target branch, creates a temporary index seeded from it, stages the requested files, and builds the tree and commit objects -- all without touching the shared .git/index. Phase B acquires a per-ref lock, re-reads the tip to confirm it has not moved, and updates the ref with a compare-and-swap. If it did move, the pipeline retries from Phase A against the new tip (re-parenting the commit) with random jitter to avoid thundering-herd stampedes under heavy concurrency.

The parent is resolved BEFORE the index rather than after the tree: the other order lets another session's commit land in between and produces a commit whose tree is based on the old tip but whose parent is the new one, silently dropping that session's files.

See docs/architecture.md for the full architecture specification.

Configuration

Run safegit config show to view every setting, safegit config get <key> to read one, and safegit config set <key> <value> to change one.

Key Default Description
commit.casMaxAttempts 5 Max CAS retry attempts for ref updates
commit.autoBumpParent (unset, and an unset one is a refusal) Whether a commit in a submodule also commits the parent's moved gitlink
lock.acquireTimeoutSeconds 30 Timeout waiting for a lock
hooks.preprepush.timeoutSeconds 1800 Timeout for pre-pre-push hook execution
push.retryAttempts 3 Number of push retry attempts

Those five are the whole key set: anything else is an unknown-key error. There is no oplog size or rotation setting -- the operation log is append-only and complete by design, and nothing truncates it.

Configuration is stored in .git/safegit/config.json. Remove the entire .git/safegit/ directory to return to vanilla git -- or run safegit doctor --action uninstall, which does it for the whole repository (every worktree's state directory plus the shared store) and enumerates every path before it asks for confirmation.

Known limitations

  • Same-machine concurrency only. Lock staleness detection uses PID liveness checks and hostname comparison. On network filesystems (NFS, CIFS), safegit doctor warns about reduced lock atomicity guarantees. Cross-machine lock reclaim is refused when the hostname doesn't match.
  • The filesystem must support hard links and flock(2). A lock is published by writing its record to a temporary sibling and link(2)-ing it into place, so the published file is complete the instant it exists; and a stale lock is reclaimed only under an exclusive flock on the lock file, with an inode identity re-check, so two contenders can never both "reclaim" the same lock. Where flock does not work, nothing is reclaimed at all: contenders time out and safegit unlock <name> is the recovery path.
  • PID reuse. On Linux, safegit records the holder's process start identity from /proc in the lock file and compares it against the current start time of whatever holds that PID, so a recycled PID is detected and a live holder is never mistaken for one. On other platforms the comparison is unavailable and fails closed: a reused PID keeps an orphan lock looking alive, and safegit unlock refuses to clear a lock whose holder appears alive, so such a lock has to be removed by hand from .git/safegit/locks/. Where the holder really is gone, safegit unlock refs/heads/main clears it.
  • Linux and macOS only. Windows is not supported and is not built: the release binaries cover linux and darwin on amd64 and arm64, and safegit uses Unix-only syscalls for locking, signals and process management. WSL (Windows Subsystem for Linux) works, since it runs the Linux binary natively.

License

MIT

Documentation

Overview

Command safegit wraps git, giving each commit its own temporary index and retrying ref updates on conflict, so concurrent agents share one repository.

Directories

Path Synopsis
internal
commit
Amend and Reword implement tip-commit rewriting with CAS safety.
Amend and Reword implement tip-commit rewriting with CAS safety.
conflict
Package conflict reads and reproduces what git recorded about a conflicted path, so a conclusion can verify a resolution against what git actually wrote instead of against a guess.
Package conflict reads and reproduces what git recorded about a conflicted path, so a conclusion can verify a resolution against what git actually wrote instead of against a guess.
coord
Package coord implements the coordination layer that prevents concurrent agents from corrupting the working tree by guarding tree-mutating operations.
Package coord implements the coordination layer that prevents concurrent agents from corrupting the working tree by guarding tree-mutating operations.
exitcode
Package exitcode is safegit's single registry of process exit codes.
Package exitcode is safegit's single registry of process exit codes.
filelock
Package filelock provides file locking for append operations, using flock(2) for advisory locking.
Package filelock provides file locking for append operations, using flock(2) for advisory locking.
git
Package git wraps os/exec calls to the git binary and is the sole interface through which safegit interacts with git plumbing commands.
Package git wraps os/exec calls to the git binary and is the sole interface through which safegit interacts with git plumbing commands.
gitexec
Package gitexec is safegit's single git-execution boundary.
Package gitexec is safegit's single git-execution boundary.
gitversion
Package gitversion parses the installed git's version and compares it against the per-feature floors safegit depends on.
Package gitversion parses the installed git's version and compares it against the per-feature floors safegit depends on.
hooks
Package hooks discovers and executes pre-pre-push hooks that run before any network I/O, solving the SSH timeout problem when checks are long-running.
Package hooks discovers and executes pre-pre-push hooks that run before any network I/O, solving the SSH timeout problem when checks are long-running.
index
Package index manages per-invocation temporary git indexes so each safegit invocation stages into its own index seeded from HEAD, avoiding contention.
Package index manages per-invocation temporary git indexes so each safegit invocation stages into its own index seeded from HEAD, avoiding contention.
lock
Package lock provides ref-lock primitives for concurrent ref updates using atomic lock file creation (link(2) of a fully-written temporary sibling) and exponential backoff polling.
Package lock provides ref-lock primitives for concurrent ref updates using atomic lock file creation (link(2) of a fully-written temporary sibling) and exponential backoff polling.
oplog
Package oplog implements the append-only JSONL operation log that records every mutating operation for undo support and audit trail purposes.
Package oplog implements the append-only JSONL operation log that records every mutating operation for undo support and audit trail purposes.
procutil
Package procutil provides cross-platform process liveness checks.
Package procutil provides cross-platform process liveness checks.
repo
Package repo manages the .git/safegit/ data directory including initialization, configuration loading, validation, and path helpers for all state files.
Package repo manages the .git/safegit/ data directory including initialization, configuration loading, validation, and path helpers for all state files.
scan
Package scan iterates all git objects (reachable and unreachable) and matches patterns against their textual content for history scrubbing.
Package scan iterates all git objects (reachable and unreachable) and matches patterns against their textual content for history scrubbing.
sequencer
Package sequencer is the single authority on the state git writes into the git directory while an operation is in flight -- a conflicted merge, a cherry-pick or revert (single or queued), a rebase, or a mailbox application.
Package sequencer is the single authority on the state git writes into the git directory while an operation is in flight -- a conflicted merge, a cherry-pick or revert (single or queued), a rebase, or a mailbox application.
stage
Package stage implements hunk-level staging against temporary indexes by parsing unified diffs and applying selective patches for partial-file commits.
Package stage implements hunk-level staging against temporary indexes by parsing unified diffs and applying selective patches for partial-file commits.
submodule
Package submodule enumerates initialized and deinitialized git submodules, detects parent repos, checks for nesting, and resolves paths through symlinks.
Package submodule enumerates initialized and deinitialized git submodules, detects parent repos, checks for nesting, and resolves paths through symlinks.
testutil
Package testutil provides shared test helpers for creating temporary git repos, used across internal/*_test.go packages to avoid duplicating boilerplate.
Package testutil provides shared test helpers for creating temporary git repos, used across internal/*_test.go packages to avoid duplicating boilerplate.
trailer
Package trailer reads and writes the key-value metadata lines at the end of a commit message: the session attribution safegit injects, and the move records a commit declares.
Package trailer reads and writes the key-value metadata lines at the end of a commit message: the session attribution safegit injects, and the move records a commit declares.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL