| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [View Raw Code] [Original HTTPS Page] |
gh-stack minimizes state. It stores only the metadata it needs, using standard Git facilities—git-config and plain files in .git/—rather than maintaining its own object store or custom database. This is a deliberate trade-off: simplicity and speed over robustness and history.
Everything lives inside .git/, so nothing is committed to your repository or shared between clones.
gh-stack uses three storage mechanisms:
The core data model lives in .git/config as standard Git configuration keys:
[stack]
trunk = main
[branch "feature-auth"]
stackParent = main
stackPR = 123
stackForkPoint = abc123def456...| Key | Value | Purpose |
|---|---|---|
| stack.trunk | Branch name | The base branch for all stacks (usually main) |
| branch.<name>.stackParent | Branch name | Parent branch in the stack hierarchy |
| branch.<name>.stackPR | Integer | Associated GitHub PR number |
| branch.<name>.stackForkPoint | Commit SHA | Where the branch originally diverged from its parent |
| Command | Writes |
|---|---|
| init | stack.trunk |
| create, adopt | stackParent, stackForkPoint |
| submit, link | stackPR |
| sync | Updates stackParent, stackForkPoint during rebase operations |
| orphan | Removes stackParent, stackPR, stackForkPoint |
| unlink | Removes stackPR |
| undo | Restores any of the above from a snapshot |
Why git-config? It requires zero parsing code. Git's config machinery handles escaping, sections, quoting, and file locking. Reading and writing are single subprocess calls (git config --get / git config key value). Users can inspect and repair state with git config --edit or any text editor.
What this costs us:
When a multi-branch rebase is interrupted by a conflict, gh-stack saves the operation state so you can resume with gh stack continue or cancel with gh stack abort.
File: .git/STACK_CASCADE_STATE Format: JSON (indented, human-readable)
{
"current": "feature-b",
"pending": ["feature-c", "feature-d"],
"original_head": "abc123...",
"operation": "submit",
"update_only": false,
"web": false,
"push_only": false,
"branches": ["feature-a", "feature-b", "feature-c", "feature-d"],
"stash_ref": "def456..."
}| Field | Purpose |
|---|---|
| current | Branch where the conflict occurred |
| pending | Remaining branches to rebase |
| original_head | HEAD before the operation started (for abort) |
| operation | "cascade" or "submit" ("cascade" is used by the restack command) |
| stash_ref | Commit hash of auto-stashed uncommitted changes |
| branches | Full branch list (submit only; used to rebuild the set for push/PR phases) |
| update_only, web, push_only | Submit-specific flags preserved across continue |
This is an ephemeral, single-operation state file. It is not designed to survive beyond the operation that created it.
Before any destructive operation, gh-stack captures a snapshot of every affected branch's state. This provides multi-level undo without requiring a full state history.
Directory: .git/stack-undo/ Archive: .git/stack-undo/done/ Format: JSON files named {timestamp}-{operation}.json
{
"timestamp": "2026-02-05T12:00:00Z",
"operation": "cascade",
"command": "gh stack restack",
"original_head": "abc123...",
"stash_ref": "",
"branches": {
"feature-auth": {
"sha": "def456...",
"stack_parent": "main",
"stack_pr": 123,
"stack_fork_point": "789abc..."
}
},
"deleted_branches": {}
}flowchart TD
subgraph commands [Commands]
init[init]
create[create / adopt]
submit[submit / link]
restack[restack / sync]
undoCmd[undo]
end
subgraph storage [".git/"]
config["config — stack metadata"]
state["STACK_CASCADE_STATE — operation recovery"]
snapshots["stack-undo/ — undo history"]
end
init -->|set trunk| config
create -->|set parent, fork point| config
submit -->|set PR number| config
restack -->|on conflict| state
restack -->|before start| snapshots
undoCmd -->|restore from| snapshots
undoCmd -->|restore| config
| Concern | Package | Notes |
|---|---|---|
| Stack metadata (git-config) | internal/config | Direct exec.Command("git", "config", ...) calls |
| Cascade state | internal/state | json.MarshalIndent / json.Unmarshal + os.WriteFile |
| Undo snapshots | internal/undo | Same JSON approach, plus directory listing and pruning |
| Git operations | internal/git | Uses safeexec.LookPath with sync.Once caching |
| Tree construction | internal/tree | Builds in-memory tree from config for traversal |
Note that internal/config and internal/git both execute Git subprocesses, but use slightly different patterns. internal/git uses safeexec.LookPath to prevent PATH injection (important on Windows); internal/config calls exec.Command("git", ...) directly. This is a known inconsistency.
| Back | FazBrowse Home | New Git URL |