Skip to main content
GitTwin

Tests PyPI version PyPI license

Run multiple git commits, branches, or tags of the same FastAPI application side by side, each in its own isolated environment — for comparison, debugging, regression testing, and demonstrations.

Status: working MVP. gittwin up actually boots real FastAPI apps today, with a live dashboard and automatic cleanup. Framework support is currently FastAPI only — see Supported frameworks below.

Why

Reproducing "it worked on commit X but not on commit Y" usually means stashing changes, checking out refs one at a time, and juggling ports, dependencies, and env files by hand. GitTwin makes that a single command:

gittwin up HEAD abc123

Stays attached with a live, color-coded dashboard while both run — press Ctrl+C and everything (processes, worktrees, envs) is cleaned up automatically, no separate teardown step needed.

Under the hood, each ref gets its own git worktree, its own uv-managed virtual environment with its own dependencies installed, and its own port — so multiple versions of your app run side by side without one clobbering another or disturbing your current branch.

Install

uv add gittwin
pip install gittwin

Usage

gittwin up <refs...>               # launch one+ refs, stays attached with a live dashboard
gittwin up <refs...> -d            # same, but detached (returns immediately, keeps running)
gittwin run <ref>                  # shortcut for `up <ref> -d`
gittwin compare <ref1> <ref2> ...  # shortcut for `up <ref1> <ref2> ... -d`
gittwin list                       # list worktrees gittwin has created
gittwin stop <ref>                 # stop one running instance
gittwin clean                      # stop everything, remove all worktrees/envs
gittwin doctor                     # check git/uv are installed, reconcile orphaned instances
gittwin about
gittwin --version

<ref> accepts any commit SHA, branch name, or tag. An invalid ref fails immediately with a clear error instead of a raw git traceback.

Example — attached session (the default)

cd my-fastapi-project
gittwin up HEAD main
HEAD -> http://127.0.0.1:8000
main -> http://127.0.0.1:8001

Press Ctrl+C to stop and clean up.
┌──────┬─────────┬────────────────────────┬───────┬─────────┐
│ Ref  │ Status  │ URL                     │ PID   │ Uptime  │
├──────┼─────────┼────────────────────────┼───────┼─────────┤
│ HEAD │ running │ http://127.0.0.1:8000  │ 18736 │ 0m 12s  │
│ main │ running │ http://127.0.0.1:8001  │ 18804 │ 0m 12s  │
└──────┴─────────┴────────────────────────┴───────┴─────────┘
^C
Stopped 2 instance(s), removed 2 worktree(s), deleted 2 env(s). Session time: 0m 47s

The table updates live and a row turns red if that instance's process dies mid-session. While provisioning (worktree/env/deps/boot), a real terminal shows an animated spinner per ref instead of the plain text lines above.

Example — detached (old fire-and-forget style)

gittwin compare HEAD abc123
# HEAD  -> http://127.0.0.1:8000
# abc123 -> http://127.0.0.1:8001
gittwin clean   # tear both down when you're done

Supported frameworks

FastAPI only, for now. gittwin detects FastAPI via a fastapi entry in your pyproject.toml dependencies, and boots your app with uvicorn <module>:<app> (convention: main:app). Flask, Django, Node.js, Docker-based apps, and other languages are on the backlog, not implemented yet.

How it works

  • Worktrees: each ref is checked out into <repo>/.gittwin/worktrees/<ref>, so multiple refs can be checked out simultaneously without one clobbering another. gittwin list shows only worktrees gittwin created — your main checkout is never listed as one of them.
  • Environments: each worktree gets its own uv-managed venv under <repo>/.gittwin/envs/<ref>, with that ref's own pyproject.toml dependencies installed into it — so different refs can depend on different (even incompatible) package versions.
  • Ports: each instance gets a free port automatically, allocated from gittwin.toml's configured range (default 8000-8999), with no collisions even when launching several refs concurrently.
  • Foreground sessions: gittwin up (without -d) blocks — the CLI process itself represents the session's lifetime. Ctrl+C, or a sent termination signal, tears everything down before it exits: process killed, worktree removed, env deleted, state cleared. Nothing is left running behind the CLI's back.
  • Orphan reconciliation: a hard kill, crash, or power loss can't be caught by a signal handler, so a stale entry can still be left behind. Every command (list/stop/clean/run/compare/up) silently prunes and cleans up any instance whose process is no longer alive before doing its own work; gittwin doctor reports how many it found.
  • Cleanup: gittwin stop <ref> / gittwin clean terminate the running process(es) and remove the worktree(s) and env(s) they used. If setup for a ref fails partway through, gittwin removes that worktree again rather than leaving a half-provisioned one behind.

Development

This project uses uv for dependency management.

uv sync --extra dev   # install package + dev deps into .venv
uv run pytest -q      # run tests
uv run gittwin about  # run the CLI

Metadata

Release files for gittwin 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gittwin 0.3.1
File Size Uploaded
gittwin-0.3.1.tar.gz 26.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gittwin 0.3.1
File Interpreter ABI Platform
gittwin-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 44.0 kB

Release files / gittwin-0.3.1.tar.gz

Download URL gittwin-0.3.1.tar.gz
Size 26.5 kB
Tags Source
SHA-256 checksum
How to use checksums
02d6590aa31514d2291c8f84bb5f4e5dc04bca1561e1c8b339ead9851142bbfb
BLAKE2b-256 checksum
How to use checksums
49aeac9677ccce8787255e5c6433ad066f9cef50c9bede029dbb19bbc4bd617f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / gittwin-0.3.1-py3-none-any.whl

Download URL gittwin-0.3.1-py3-none-any.whl
Size 17.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e74052413ed24c3a163e7efa34a0a85549a4df85a6e30b629ad5b7673d687dcd
BLAKE2b-256 checksum
How to use checksums
da3a18da76035f45c7931056061b2025fbf3db15445db8915fa78baaa268ac66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page