ShadowGit

August 23, 2026 ยท View on GitHub

Invisible, Git-backed checkpoints for any directory.

CI Python 3.12+ License: MIT

ShadowGit creates content-addressed filesystem snapshots in a private Git object store. It does not touch the workspace's .git, invoke the Git CLI, or require the directory to be a repository.

It is designed for coding agents, migration tools, refactoring systems, IDEs, and automation that needs a fast recovery point before changing files.

ShadowGit is a focused library, not a Git replacement: it gives a process a private, local safety net while leaving the workspace's normal Git repository untouched.

When to use it

Use ShadowGit when a tool needs to make risky filesystem changes and offer a reliable undo point, especially when the directory may not be a Git repository. It is a good fit for agent tool calls, code migrations, IDE features, and automated refactoring. Use ordinary Git when you need collaboration, remotes, merges, or a human-maintained project history.

Why ShadowGit?

  • Invisible: the default store lives under .shadowgit, separate from the workspace's own repository.
  • Conservative: restore quarantines unexpected files instead of deleting them.
  • Crash recoverable: a durable journal and private recovery commit are written before restore changes the workspace.
  • Filesystem faithful: byte content, symbolic links, and executable modes are represented using native Git tree entries.
  • Incremental: unchanged files reuse blobs through a persistent stat cache.
  • Embeddable: the Python API never spawns a subprocess.
  • Machine friendly: every CLI operation supports structured JSON output.

Install

# Install the repository checkout as a command-line tool
pipx install git+https://github.com/josephsenior/ShadowGit.git

For library use:

python -m pip install git+https://github.com/josephsenior/ShadowGit.git

ShadowGit requires Python 3.12 or newer.

Command line

# Capture and inspect checkpoints
shadowgit -C ./project snapshot --label "before migration"
shadowgit -C ./project list
shadowgit -C ./project diff <before-sha> <after-sha>
shadowgit -C ./project verify

# Restore only after explicit confirmation
shadowgit -C ./project restore <sha> --yes

# Inspect or recover an interrupted restore
shadowgit -C ./project status --json
shadowgit -C ./project recover --yes

# Retain only the newest ten snapshots
shadowgit -C ./project prune --keep-last 10 --yes --json

Use --store PATH to put the private object store outside the workspace and --json for stable structured output.

Python API

from shadowgit import ShadowRepo

repo = ShadowRepo("./project")
before = repo.snapshot("before refactor")

# Make changes, then restore if needed.
quarantine = repo.restore(before)

if repo.pending_recovery:
    repo.recover()

Custom ignore policy is supported without coupling ShadowGit to an agent:

from shadowgit import ShadowRepo, build_ignore_matcher

workspace = "./project"
repo = ShadowRepo(workspace, ignore=build_ignore_matcher(workspace))

Restore safety model

Before modifying the workspace, ShadowGit:

  1. captures the current state in a private recovery ref;
  2. fsyncs a restore journal containing the target and recovery SHAs;
  3. quarantines unexpected files and path-type conflicts;
  4. restores files without following symlinked parent directories;
  5. removes the journal and recovery ref only after success.

If the process is interrupted, mutating operations fail closed until recover() or shadowgit recover --yes restores the pre-operation state. Opening a repository never performs recovery automatically.

Ignored paths and reserved roots such as .git and .shadowgit are never modified during restore.

Pruning semantics

Git commit IDs include their parent IDs. Removing snapshots from the middle of history therefore requires rewriting retained descendants. prune() returns a PruneResult.rewritten mapping from every retained old SHA to its current SHA; the CLI emits the same mapping as JSON. Pruning removes snapshots from the reachable ShadowGit history. Unreachable Git objects may remain until the underlying object database is compacted by a future storage-maintenance pass.

Platform notes

Git tracks one portable permission bit: executable versus non-executable. ShadowGit preserves that bit on POSIX systems. Windows does not expose an equivalent chmod bit, so content is restored while executable-mode application is skipped. Symlink creation on Windows requires an account or Developer Mode configuration permitted to create symbolic links.

Scope

ShadowGit is a local filesystem checkpoint engine. It intentionally does not implement remotes, merges, branches for collaboration, or a replacement Git CLI. Its object store is private implementation state and should not be used as the workspace's normal Git repository.

Development

python -m pip install -e ".[test]"
ruff check src tests
ruff format --check src tests
pytest
python -m build

The test configuration includes the src/ layout, so python -m pytest also works directly from a fresh checkout after installing the test dependencies.

Project status

ShadowGit is currently alpha software. The restore and pruning APIs are covered by cross-platform CI, but consumers should still pin a version and exercise their own filesystem and symlink policies before using it in a production recovery workflow.

See CONTRIBUTING.md and SECURITY.md.

Origin and license

ShadowGit originated in and is battle-tested by Grinta. It is released under the MIT License.