Skip to main content

GitTwin

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.0

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.0
File Size Uploaded
gittwin-0.3.0.tar.gz 25.8 kB Details

Built distribution (wheel)

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

Total release size: 43.2 kB

Release files / gittwin-0.3.0.tar.gz

Download URL gittwin-0.3.0.tar.gz
Size 25.8 kB
Tags Source
SHA-256 checksum
How to use checksums
30453015e4ac7154e2c2567de66f0eed37fd4488a0dd7acd8821b51cdc5c9746
BLAKE2b-256 checksum
How to use checksums
e29a4a9d653a00326aac638bb56878fb912785229287fc32b08b46ff3bdf0170
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.0-py3-none-any.whl

Download URL gittwin-0.3.0-py3-none-any.whl
Size 17.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
52d6ffc3d1fad8b756af63130cd4a91ef07fe0dd782c90adb033363409687b5d
BLAKE2b-256 checksum
How to use checksums
4e406ff54912009b728ede7c124ff29457383b782b9c95d8dbc4e2089db92d1e
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

0.3.1

2 release files

This release

0.3.0 This release

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