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 listshows 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 ownpyproject.tomldependencies 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 (default8000-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 doctorreports how many it found. - Cleanup:
gittwin stop <ref>/gittwin cleanterminate 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)
| File | Size | Uploaded | |
|---|---|---|---|
| gittwin-0.3.0.tar.gz | 25.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|