giff
Stacked diffs for GitHub. Work on multiple dependent pull requests as a linear stack of branches, with automatic PR management and conflict-aware rebasing.
● main
│
◉ feat/auth-base PR #42 [open] ← you are here
│
◉ feat/auth-tokens PR #43 [open]
│
◉ feat/auth-middleware PR #44 [draft]
Install
From crates.io (requires Rust ≥ 1.75):
The crate is published as giffstack; the binary it ships is giff.
From a local checkout (for hacking on it):
Auth:
Set a GitHub personal access token with repo scope:
# or add it to ~/.config/giff/config.toml (run `giff init` to create the skeleton)
Usage
# Start a stack from main
# Navigate
# Open / update PRs for all frames
# See the stack with live PR status
# See where you are
# Rebase the whole stack onto updated main
# If a conflict occurs, resolve it, then:
# Open the web dashboard in your default browser (PRs, diffs, comments, all yours)
# → http://local.giffstack.com:51743 (or http://localhost:51743 if your DNS blocks it)
# Advanced
giff dashboard runs an embedded HTTP server on a localhost port and opens your
default browser to it. The same SvelteKit app that runs at giffstack.com — your
GitHub token stays in localStorage, no data leaves your machine. Ctrl-C to stop.
Configuration
~/.config/giff/config.toml (created by giff init):
[]
= "ghp_..."
= "https://api.github.com" # override for GitHub Enterprise
[]
= "main" # base branch for new stacks
= true # open PRs as drafts by default
Stack metadata is stored in .git/stacked.toml — inside .git/ so it's never committed.
How it works
Each stack frame is a regular git branch. PRs target the frame below them (not main directly), so reviewers see only the diff for that layer. On giff push, each PR description gets a small JSON block embedded in a fenced code block — this lets the stack be reconstructed from GitHub alone, with no local file required.
Contributing
Pull requests welcome. Please open an issue first for anything beyond small bug fixes.
License
MIT