@cvr/stacked 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -22
- package/bin/stacked +0 -0
- package/package.json +3 -2
- package/scripts/build.ts +8 -1
- package/skills/stacked/SKILL.md +221 -39
- package/src/commands/adopt.ts +70 -8
- package/src/commands/amend.ts +107 -0
- package/src/commands/bottom.ts +26 -8
- package/src/commands/checkout.ts +25 -6
- package/src/commands/clean.ts +106 -27
- package/src/commands/create.ts +74 -12
- package/src/commands/delete.ts +92 -24
- package/src/commands/detect.ts +152 -0
- package/src/commands/doctor.ts +124 -0
- package/src/commands/down.ts +62 -0
- package/src/commands/helpers/validate.ts +61 -0
- package/src/commands/index.ts +29 -1
- package/src/commands/init.ts +40 -0
- package/src/commands/list.ts +61 -22
- package/src/commands/log.ts +32 -6
- package/src/commands/rename.ts +49 -0
- package/src/commands/reorder.ts +93 -0
- package/src/commands/split.ts +108 -0
- package/src/commands/stacks.ts +33 -7
- package/src/commands/status.ts +55 -0
- package/src/commands/submit.ts +245 -16
- package/src/commands/sync.ts +127 -19
- package/src/commands/top.ts +24 -8
- package/src/commands/trunk.ts +37 -6
- package/src/commands/up.ts +55 -0
- package/src/errors/index.ts +30 -0
- package/src/global.d.ts +2 -0
- package/src/main.ts +70 -3
- package/src/services/Git.ts +102 -30
- package/src/services/GitHub.ts +56 -18
- package/src/services/Stack.ts +65 -58
- package/src/ui.ts +173 -0
package/README.md
CHANGED
|
@@ -10,12 +10,19 @@ Built with [Effect v4](https://effect.website) and [Bun](https://bun.sh).
|
|
|
10
10
|
bun run build # compiles binary to bin/stacked + symlinks to ~/.bun/bin/
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Setup
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
#
|
|
16
|
+
# Trunk is auto-detected (main > master > develop). Override if needed:
|
|
17
17
|
stacked trunk develop
|
|
18
18
|
|
|
19
|
+
# Install the Claude skill (optional):
|
|
20
|
+
stacked init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```sh
|
|
19
26
|
# Create a stack: branch from trunk
|
|
20
27
|
stacked create feat-auth
|
|
21
28
|
|
|
@@ -24,17 +31,26 @@ stacked create feat-auth-ui
|
|
|
24
31
|
|
|
25
32
|
# See the stack
|
|
26
33
|
stacked list
|
|
34
|
+
stacked list --json # machine-readable output
|
|
27
35
|
|
|
28
36
|
# See a specific stack by name
|
|
29
37
|
stacked list feat-auth
|
|
30
38
|
|
|
39
|
+
# Quick orientation
|
|
40
|
+
stacked status
|
|
41
|
+
stacked status --json
|
|
42
|
+
|
|
31
43
|
# List all stacks in the repo
|
|
32
44
|
stacked stacks
|
|
45
|
+
stacked stacks --json
|
|
33
46
|
|
|
34
47
|
# Navigate
|
|
35
|
-
stacked
|
|
36
|
-
stacked
|
|
48
|
+
stacked up # move up one branch
|
|
49
|
+
stacked down # move down one branch
|
|
50
|
+
stacked top # jump to top of stack
|
|
51
|
+
stacked bottom # jump to bottom of stack
|
|
37
52
|
stacked checkout feat-auth
|
|
53
|
+
stacked checkout any-branch # falls through to git for non-stacked branches
|
|
38
54
|
|
|
39
55
|
# Sync entire stack with latest trunk
|
|
40
56
|
stacked sync
|
|
@@ -42,9 +58,13 @@ stacked sync
|
|
|
42
58
|
# After editing mid-stack, rebase only children
|
|
43
59
|
stacked sync --from feat-auth
|
|
44
60
|
|
|
45
|
-
# Push all branches + create/update PRs
|
|
61
|
+
# Push all branches + create/update PRs (force-pushes by default)
|
|
46
62
|
stacked submit
|
|
47
63
|
stacked submit --draft
|
|
64
|
+
stacked submit --title "Add auth" --body "Implements OAuth2 flow"
|
|
65
|
+
stacked submit --title "Auth,Validation,Tests" # per-branch titles (comma-delimited)
|
|
66
|
+
stacked submit --only # process only the current branch
|
|
67
|
+
stacked submit --no-force # disable force-push
|
|
48
68
|
stacked submit --dry-run
|
|
49
69
|
|
|
50
70
|
# Adopt an existing branch into the stack
|
|
@@ -52,37 +72,73 @@ stacked adopt existing-branch --after feat-auth
|
|
|
52
72
|
|
|
53
73
|
# View commits per branch
|
|
54
74
|
stacked log
|
|
75
|
+
stacked log --json
|
|
76
|
+
|
|
77
|
+
# Detect existing branch chains and register as stacks
|
|
78
|
+
stacked detect
|
|
79
|
+
stacked detect --dry-run
|
|
55
80
|
|
|
56
|
-
# Remove merged branches from stacks
|
|
81
|
+
# Remove merged branches from stacks (prompts for confirmation)
|
|
57
82
|
stacked clean
|
|
58
83
|
stacked clean --dry-run
|
|
84
|
+
stacked clean --yes # skip confirmation
|
|
59
85
|
|
|
60
|
-
# Remove a branch from the stack
|
|
86
|
+
# Remove a branch from the stack (prompts for confirmation)
|
|
61
87
|
stacked delete feat-auth-ui
|
|
88
|
+
stacked delete feat-auth-ui --keep-remote # keep the remote branch
|
|
89
|
+
stacked delete feat-auth-ui --yes # skip confirmation
|
|
90
|
+
|
|
91
|
+
# Global flags (work with any command)
|
|
92
|
+
stacked --verbose list # debug output
|
|
93
|
+
stacked --quiet sync # suppress non-essential output
|
|
94
|
+
stacked --no-color list # disable colored output
|
|
95
|
+
stacked --yes clean # skip confirmation prompts
|
|
62
96
|
```
|
|
63
97
|
|
|
64
98
|
## Commands
|
|
65
99
|
|
|
66
|
-
| Command | Description
|
|
67
|
-
| ----------------- |
|
|
68
|
-
| `trunk [name]` | Get/set trunk branch
|
|
69
|
-
| `create <name>` | Create branch on top of current
|
|
70
|
-
| `list [stack]` | Show stack branches (
|
|
71
|
-
| `stacks` | List all stacks
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
80
|
-
| `
|
|
100
|
+
| Command | Description |
|
|
101
|
+
| ----------------- | ------------------------------------------------------------------------------ |
|
|
102
|
+
| `trunk [name]` | Get/set trunk branch (`--json`) |
|
|
103
|
+
| `create <name>` | Create branch on top of current (`--from`, `--json`) |
|
|
104
|
+
| `list [stack]` | Show stack branches (`--json`) |
|
|
105
|
+
| `stacks` | List all stacks (`--json`) |
|
|
106
|
+
| `status` | Show current branch, stack position, working tree state (`--json`) |
|
|
107
|
+
| `checkout <name>` | Switch to branch (falls through to git for non-stacked branches) |
|
|
108
|
+
| `up` | Move up one branch in the stack |
|
|
109
|
+
| `down` | Move down one branch in the stack |
|
|
110
|
+
| `top` | Jump to top of stack |
|
|
111
|
+
| `bottom` | Jump to bottom of stack |
|
|
112
|
+
| `sync` | Fetch + rebase stack on trunk (`--from` to start from a branch) |
|
|
113
|
+
| `detect` | Detect branch chains and register as stacks (`--dry-run`, `--json`) |
|
|
114
|
+
| `clean` | Remove merged branches + remote branches (`--dry-run`, `--json`) |
|
|
115
|
+
| `delete <name>` | Remove branch from stack + git + remote (`--keep-remote`, `--force`, `--json`) |
|
|
116
|
+
| `submit` | Push + create/update PRs (`--title`, `--body`, `--only`, `--draft`, `--json`) |
|
|
117
|
+
| `adopt <branch>` | Add existing branch to stack (`--after`, `--json`) |
|
|
118
|
+
| `log` | Show commits grouped by branch (`--json`) |
|
|
119
|
+
| `init` | Install the stacked Claude skill to ~/.claude/skills |
|
|
120
|
+
|
|
121
|
+
### Global Flags
|
|
122
|
+
|
|
123
|
+
| Flag | Description |
|
|
124
|
+
| -------------- | ----------------------------- |
|
|
125
|
+
| `--verbose` | Enable debug output |
|
|
126
|
+
| `--quiet`/`-q` | Suppress non-essential output |
|
|
127
|
+
| `--no-color` | Disable colored output |
|
|
128
|
+
| `--yes`/`-y` | Skip confirmation prompts |
|
|
81
129
|
|
|
82
130
|
## Data Model
|
|
83
131
|
|
|
84
132
|
Stack metadata lives in `.git/stacked.json`. Each branch's parent is implied by array position — `branches[0]`'s parent is trunk, `branches[n]`'s parent is `branches[n-1]`.
|
|
85
133
|
|
|
134
|
+
Trunk is auto-detected on first use by checking for `main`, `master`, or `develop` branches. Override with `stacked trunk <name>`.
|
|
135
|
+
|
|
136
|
+
## Output Conventions
|
|
137
|
+
|
|
138
|
+
- **stdout**: data output only (JSON, branch names, tree views) — safe for piping
|
|
139
|
+
- **stderr**: progress messages, spinners, success/warning/error messages
|
|
140
|
+
- All commands support `--json` for structured output
|
|
141
|
+
|
|
86
142
|
## Development
|
|
87
143
|
|
|
88
144
|
```sh
|
package/bin/stacked
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cvr/stacked",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/cevr/stacked"
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@effect/platform-bun": "4.0.0-beta.12",
|
|
36
|
-
"effect": "4.0.0-beta.12"
|
|
36
|
+
"effect": "4.0.0-beta.12",
|
|
37
|
+
"picocolors": "^1.1.1"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@changesets/changelog-github": "^0.5.2",
|
package/scripts/build.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, lstatSync, unlinkSync, symlinkSync } from "fs";
|
|
1
|
+
import { mkdirSync, lstatSync, unlinkSync, symlinkSync, readFileSync } from "fs";
|
|
2
2
|
import { dirname, join } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import * as os from "node:os";
|
|
@@ -7,6 +7,9 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
8
8
|
const rootDir = join(__dirname, "..");
|
|
9
9
|
|
|
10
|
+
const pkg = JSON.parse(readFileSync(join(rootDir, "package.json"), "utf-8"));
|
|
11
|
+
const skillContent = readFileSync(join(rootDir, "skills", "stacked", "SKILL.md"), "utf-8");
|
|
12
|
+
|
|
10
13
|
console.log("Building stacked...");
|
|
11
14
|
|
|
12
15
|
const binDir = join(rootDir, "bin");
|
|
@@ -20,6 +23,10 @@ const buildResult = await Bun.build({
|
|
|
20
23
|
entrypoints: [join(rootDir, "src/main.ts")],
|
|
21
24
|
target: "bun",
|
|
22
25
|
minify: false,
|
|
26
|
+
define: {
|
|
27
|
+
__VERSION__: JSON.stringify(pkg.version),
|
|
28
|
+
__SKILL_CONTENT__: JSON.stringify(skillContent),
|
|
29
|
+
},
|
|
23
30
|
compile: {
|
|
24
31
|
target: `bun-${platform}-${arch}`,
|
|
25
32
|
outfile: join(binDir, "stacked"),
|
package/skills/stacked/SKILL.md
CHANGED
|
@@ -16,40 +16,69 @@ What do you need?
|
|
|
16
16
|
├─ Start a new stack → §Creating a Stack
|
|
17
17
|
├─ Add branches to a stack → §Creating a Stack
|
|
18
18
|
├─ See current stack → §Viewing the Stack
|
|
19
|
+
├─ Quick orientation → §Status
|
|
19
20
|
├─ Navigate between branches → §Navigation
|
|
20
21
|
├─ Rebase after changes → §Rebasing
|
|
22
|
+
├─ Amend + auto-rebase → §Amending
|
|
21
23
|
├─ Push + create PRs → §Submitting
|
|
22
24
|
├─ Adopt existing branches → §Adopting Branches
|
|
25
|
+
├─ Detect existing branches → §Detecting Existing Branches
|
|
26
|
+
├─ Clean up merged branches → §Cleaning Up Merged Branches
|
|
23
27
|
├─ Remove a branch → §Deleting
|
|
28
|
+
├─ Reorganize stack → §Stack Management
|
|
29
|
+
├─ Check metadata health → §Doctor
|
|
30
|
+
├─ Install Claude skill → §Setup
|
|
24
31
|
└─ Troubleshooting → §Gotchas
|
|
25
32
|
```
|
|
26
33
|
|
|
27
34
|
## Quick Reference
|
|
28
35
|
|
|
29
|
-
| Command
|
|
30
|
-
|
|
|
31
|
-
| `stacked trunk [name]`
|
|
32
|
-
| `stacked create <name>`
|
|
33
|
-
| `stacked list [stack]`
|
|
34
|
-
| `stacked stacks`
|
|
35
|
-
| `stacked
|
|
36
|
-
| `stacked
|
|
37
|
-
| `stacked
|
|
38
|
-
| `stacked
|
|
39
|
-
| `stacked
|
|
40
|
-
| `stacked
|
|
41
|
-
| `stacked
|
|
42
|
-
| `stacked
|
|
43
|
-
| `stacked
|
|
36
|
+
| Command | What it does |
|
|
37
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------- |
|
|
38
|
+
| `stacked trunk [name]` | Get/set trunk branch (`--json`) |
|
|
39
|
+
| `stacked create <name>` | Create branch on top of current (`--from`, `--json`) |
|
|
40
|
+
| `stacked list [stack]` | Show stack branches (`--json`) |
|
|
41
|
+
| `stacked stacks` | List all stacks in the repo (`--json`) |
|
|
42
|
+
| `stacked status` | Show current branch, stack position, working tree state (`--json`) |
|
|
43
|
+
| `stacked checkout <name>` | Switch to branch (falls through to git for non-stacked branches) |
|
|
44
|
+
| `stacked up` | Move up one branch in the stack |
|
|
45
|
+
| `stacked down` | Move down one branch in the stack |
|
|
46
|
+
| `stacked top` | Jump to top of stack |
|
|
47
|
+
| `stacked bottom` | Jump to bottom of stack |
|
|
48
|
+
| `stacked sync` | Fetch + rebase stack on trunk (`--from`, `--dry-run`, `--json`) |
|
|
49
|
+
| `stacked detect` | Detect branch chains and register as stacks (`--dry-run`, `--json`) |
|
|
50
|
+
| `stacked clean` | Remove merged branches + remote branches (`--dry-run`, `--json`) |
|
|
51
|
+
| `stacked delete <name>` | Remove branch from stack + git + remote (`--keep-remote`, `--force`, `--dry-run`, `--json`) |
|
|
52
|
+
| `stacked submit` | Push + create/update PRs (`--title`, `--body`, `--only`, `--draft`, `--json`) |
|
|
53
|
+
| `stacked adopt <branch>` | Add existing git branch into the stack (`--after`, `--json`) |
|
|
54
|
+
| `stacked log` | Show commits grouped by branch (`--json`) |
|
|
55
|
+
| `stacked amend` | Amend current commit and rebase children (`--edit`, `--from`, `--json`) |
|
|
56
|
+
| `stacked doctor` | Check stack metadata for issues (`--fix`, `--json`) |
|
|
57
|
+
| `stacked rename <old> <new>` | Rename a stack (`--json`) |
|
|
58
|
+
| `stacked reorder <branch>` | Move a branch within the stack (`--before`, `--after`, `--json`) |
|
|
59
|
+
| `stacked split <branch>` | Split stack at a branch point (`--dry-run`, `--json`) |
|
|
60
|
+
| `stacked init` | Install the stacked Claude skill to ~/.claude/skills |
|
|
61
|
+
|
|
62
|
+
### Global Flags
|
|
63
|
+
|
|
64
|
+
| Flag | Description |
|
|
65
|
+
| -------------- | ----------------------------- |
|
|
66
|
+
| `--verbose` | Enable debug output |
|
|
67
|
+
| `--quiet`/`-q` | Suppress non-essential output |
|
|
68
|
+
| `--no-color` | Disable colored output |
|
|
69
|
+
| `--yes`/`-y` | Skip confirmation prompts |
|
|
44
70
|
|
|
45
71
|
## Setup
|
|
46
72
|
|
|
47
73
|
```sh
|
|
48
|
-
#
|
|
74
|
+
# Trunk is auto-detected (main > master > develop). Override if needed:
|
|
49
75
|
stacked trunk develop
|
|
76
|
+
|
|
77
|
+
# Install the Claude skill (optional, compiled binary only):
|
|
78
|
+
stacked init
|
|
50
79
|
```
|
|
51
80
|
|
|
52
|
-
Requires `gh` CLI installed and authenticated for `submit`.
|
|
81
|
+
Requires `gh` CLI installed and authenticated for `submit` and `clean`.
|
|
53
82
|
|
|
54
83
|
## Creating a Stack
|
|
55
84
|
|
|
@@ -75,26 +104,50 @@ stacked create hotfix --from feat-auth
|
|
|
75
104
|
## Viewing the Stack
|
|
76
105
|
|
|
77
106
|
```sh
|
|
78
|
-
stacked list # shows current stack's branches
|
|
107
|
+
stacked list # shows current stack's branches (colored tree view)
|
|
79
108
|
stacked list feat-auth # shows a specific stack by name
|
|
109
|
+
stacked list --json # machine-readable JSON output
|
|
80
110
|
stacked stacks # lists all stacks in the repo
|
|
111
|
+
stacked stacks --json # machine-readable JSON output
|
|
81
112
|
stacked log # shows commits grouped by branch
|
|
113
|
+
stacked log --json # machine-readable JSON output
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Status
|
|
117
|
+
|
|
118
|
+
Quick orientation — shows current branch, working tree state, and stack position:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
stacked status
|
|
122
|
+
# Branch: feat-auth-ui
|
|
123
|
+
# Working tree: clean
|
|
124
|
+
# Stack: feat-auth (2 of 3)
|
|
125
|
+
|
|
126
|
+
stacked status --json
|
|
127
|
+
# { "branch": "feat-auth-ui", "clean": true, "stack": { "name": "feat-auth", "position": 2, "total": 3 } }
|
|
82
128
|
```
|
|
83
129
|
|
|
84
130
|
## Navigation
|
|
85
131
|
|
|
86
132
|
```sh
|
|
87
|
-
stacked
|
|
88
|
-
stacked
|
|
89
|
-
stacked
|
|
133
|
+
stacked up # move up one branch
|
|
134
|
+
stacked down # move down one branch
|
|
135
|
+
stacked checkout feat-auth # switch to specific branch
|
|
136
|
+
stacked checkout any-branch # falls through to git for non-stacked branches
|
|
137
|
+
stacked top # jump to top of stack
|
|
138
|
+
stacked bottom # jump to bottom (trunk-adjacent)
|
|
90
139
|
```
|
|
91
140
|
|
|
141
|
+
All navigation commands support `--json` for structured output. `checkout` falls through to `git checkout` for branches not in any stack.
|
|
142
|
+
|
|
92
143
|
## Syncing / Rebasing
|
|
93
144
|
|
|
94
145
|
Fetch latest trunk and rebase the entire stack bottom-to-top:
|
|
95
146
|
|
|
96
147
|
```sh
|
|
97
148
|
stacked sync
|
|
149
|
+
stacked sync --dry-run # preview rebase plan without executing
|
|
150
|
+
stacked sync --json # structured output: { branches: [{ name, action, base }] }
|
|
98
151
|
```
|
|
99
152
|
|
|
100
153
|
After mid-stack changes, rebase only the branches above a specific point:
|
|
@@ -105,18 +158,45 @@ stacked checkout feat-auth
|
|
|
105
158
|
stacked sync --from feat-auth # rebases only children of feat-auth
|
|
106
159
|
```
|
|
107
160
|
|
|
161
|
+
**Note:** `sync` requires a clean working tree — commit or stash before running (except with `--dry-run`). On rebase conflict, the rebase is left in progress so you can resolve it:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
# On conflict:
|
|
165
|
+
git rebase --continue # after resolving conflicts
|
|
166
|
+
stacked sync --from <parent-branch> # resume syncing remaining branches
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Amending
|
|
170
|
+
|
|
171
|
+
Amend the current commit and auto-rebase child branches:
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
stacked amend # amend + rebase children
|
|
175
|
+
stacked amend --edit # open editor for commit message
|
|
176
|
+
stacked amend --from feat-auth # start rebasing from a specific branch
|
|
177
|
+
stacked amend --json # structured output
|
|
178
|
+
```
|
|
179
|
+
|
|
108
180
|
## Submitting
|
|
109
181
|
|
|
110
182
|
Push all stack branches and create/update GitHub PRs with correct base branches:
|
|
111
183
|
|
|
112
184
|
```sh
|
|
113
|
-
stacked submit
|
|
114
|
-
stacked submit --draft
|
|
115
|
-
stacked submit --
|
|
116
|
-
stacked submit --
|
|
185
|
+
stacked submit # push + create/update PRs
|
|
186
|
+
stacked submit --draft # create as draft PRs
|
|
187
|
+
stacked submit --title "Add auth" --body "OAuth2" # with title and body
|
|
188
|
+
stacked submit --title "Auth,Validation,Tests" # per-branch titles (comma-delimited)
|
|
189
|
+
stacked submit --only # process only the current branch
|
|
190
|
+
stacked submit --no-force # disable force-push
|
|
191
|
+
stacked submit --dry-run # show what would happen
|
|
192
|
+
stacked submit --json # structured JSON output
|
|
117
193
|
```
|
|
118
194
|
|
|
119
|
-
|
|
195
|
+
Force-push (with lease) is the default because stacked branches are always rebased. Use `--no-force` if you haven't rebased.
|
|
196
|
+
|
|
197
|
+
Each PR targets its parent branch (not trunk), preserving the stack structure on GitHub. PRs include auto-generated stack metadata showing position and navigation links. The metadata is refreshed on every `submit`.
|
|
198
|
+
|
|
199
|
+
**Output:** `submit` prints one line per branch to stdout: `<branch> #<number> <url> <action>`. With `--json`, outputs a structured `{ results: [...] }` array.
|
|
120
200
|
|
|
121
201
|
## Adopting Branches
|
|
122
202
|
|
|
@@ -127,24 +207,107 @@ stacked adopt existing-branch # append to top
|
|
|
127
207
|
stacked adopt existing-branch --after feat-auth # insert after specific branch
|
|
128
208
|
```
|
|
129
209
|
|
|
210
|
+
## Detecting Existing Branches
|
|
211
|
+
|
|
212
|
+
Auto-detect linear branch chains from git history and register them as stacks:
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
stacked detect # scan and register branch chains
|
|
216
|
+
stacked detect --dry-run # preview what would be registered
|
|
217
|
+
stacked detect --json # structured JSON output
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Only linear chains are detected. Forked branches (one parent with multiple children) are reported but skipped. Already-tracked branches are excluded.
|
|
221
|
+
|
|
130
222
|
## Cleaning Up Merged Branches
|
|
131
223
|
|
|
132
|
-
After PRs are merged on GitHub, clean up the local branches and stack metadata:
|
|
224
|
+
After PRs are merged on GitHub, clean up the local and remote branches and stack metadata:
|
|
133
225
|
|
|
134
226
|
```sh
|
|
135
|
-
stacked clean # removes all merged branches
|
|
227
|
+
stacked clean # removes all merged branches (prompts for confirmation)
|
|
136
228
|
stacked clean --dry-run # preview what would be removed
|
|
229
|
+
stacked clean --json # structured JSON output
|
|
230
|
+
stacked clean --yes # skip confirmation prompt
|
|
137
231
|
```
|
|
138
232
|
|
|
139
|
-
`
|
|
233
|
+
`clean` also deletes the corresponding remote branches. `list` shows merge status per branch (`[merged]`, `[closed]`, `[#N]` for open PRs). After cleaning, run `stacked sync` to rebase remaining branches.
|
|
140
234
|
|
|
141
235
|
## Deleting
|
|
142
236
|
|
|
143
237
|
```sh
|
|
144
|
-
stacked delete feat-auth-ui
|
|
145
|
-
stacked delete feat-auth-ui --force
|
|
238
|
+
stacked delete feat-auth-ui # removes from stack + deletes local + remote branch
|
|
239
|
+
stacked delete feat-auth-ui --force # skip children check
|
|
240
|
+
stacked delete feat-auth-ui --keep-remote # don't delete remote branch
|
|
241
|
+
stacked delete feat-auth-ui --dry-run # show what would happen
|
|
242
|
+
stacked delete feat-auth-ui --json # structured JSON output
|
|
146
243
|
```
|
|
147
244
|
|
|
245
|
+
Deleting a mid-stack branch with `--force` warns about potentially lost commits and recommends running `stacked sync` to rebase child branches onto the new parent.
|
|
246
|
+
|
|
247
|
+
## Stack Management
|
|
248
|
+
|
|
249
|
+
Rename, reorder, and split stacks:
|
|
250
|
+
|
|
251
|
+
```sh
|
|
252
|
+
# Rename a stack (metadata only, doesn't rename branches)
|
|
253
|
+
stacked rename old-name new-name
|
|
254
|
+
|
|
255
|
+
# Move a branch to a different position in the stack
|
|
256
|
+
stacked reorder feat-b --before feat-a
|
|
257
|
+
stacked reorder feat-b --after feat-c
|
|
258
|
+
|
|
259
|
+
# Split a stack at a branch point (branches at/above become a new stack)
|
|
260
|
+
stacked split feat-b
|
|
261
|
+
stacked split feat-b --dry-run # preview the split
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
After reordering, run `stacked sync` to rebase branches in new order.
|
|
265
|
+
|
|
266
|
+
## Doctor
|
|
267
|
+
|
|
268
|
+
Check stack metadata for issues (stale branches, missing trunk, duplicates):
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
stacked doctor # report issues
|
|
272
|
+
stacked doctor --fix # auto-fix where possible (remove stale branches, auto-detect trunk)
|
|
273
|
+
stacked doctor --json # structured output
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Error Codes
|
|
277
|
+
|
|
278
|
+
All errors include a machine-readable code for programmatic handling:
|
|
279
|
+
|
|
280
|
+
| Code | Meaning |
|
|
281
|
+
| --------------------- | ------------------------------------------- |
|
|
282
|
+
| `BRANCH_EXISTS` | Branch already exists |
|
|
283
|
+
| `BRANCH_NOT_FOUND` | Branch not found in any stack |
|
|
284
|
+
| `NOT_IN_STACK` | Current branch not tracked in a stack |
|
|
285
|
+
| `DIRTY_WORKTREE` | Working tree has uncommitted changes |
|
|
286
|
+
| `REBASE_CONFLICT` | Rebase conflict during sync/amend |
|
|
287
|
+
| `GH_NOT_INSTALLED` | `gh` CLI not installed or not authenticated |
|
|
288
|
+
| `STACK_NOT_FOUND` | Stack not found |
|
|
289
|
+
| `STACK_EXISTS` | Stack name already taken |
|
|
290
|
+
| `INVALID_BRANCH_NAME` | Branch name fails validation |
|
|
291
|
+
| `ALREADY_AT_TOP` | Already at top of stack |
|
|
292
|
+
| `ALREADY_AT_BOTTOM` | Already at bottom of stack |
|
|
293
|
+
| `TRUNK_ERROR` | Trunk-related error |
|
|
294
|
+
|
|
295
|
+
Usage errors (invalid args, bad state) exit code 2. Operational errors (git/gh failures) exit code 1.
|
|
296
|
+
|
|
297
|
+
## Idempotent Commands
|
|
298
|
+
|
|
299
|
+
`create` and `adopt` are idempotent:
|
|
300
|
+
|
|
301
|
+
- `create` succeeds silently if the branch already exists and is tracked
|
|
302
|
+
- `adopt` succeeds silently if the branch is already in the current stack (errors only if in a different stack)
|
|
303
|
+
|
|
304
|
+
## Output Conventions
|
|
305
|
+
|
|
306
|
+
- **stdout**: data output only (JSON, branch names, tree views) — safe for piping
|
|
307
|
+
- **stderr**: progress messages, spinners, success/warning/error messages
|
|
308
|
+
- All write commands support `--json` for structured output
|
|
309
|
+
- Colored output respects `NO_COLOR`, `FORCE_COLOR`, and `--no-color`
|
|
310
|
+
|
|
148
311
|
## Data Model
|
|
149
312
|
|
|
150
313
|
Stack metadata lives in `.git/stacked.json`. Each branch's parent is implied by array position:
|
|
@@ -152,6 +315,8 @@ Stack metadata lives in `.git/stacked.json`. Each branch's parent is implied by
|
|
|
152
315
|
- `branches[0]` → parent is trunk
|
|
153
316
|
- `branches[n]` → parent is `branches[n-1]`
|
|
154
317
|
|
|
318
|
+
Trunk is auto-detected on first use by checking for `main`, `master`, or `develop` branches. Override with `stacked trunk <name>`.
|
|
319
|
+
|
|
155
320
|
A repo can have multiple independent stacks. The current stack is determined by which branch you're on.
|
|
156
321
|
|
|
157
322
|
## Typical Workflow
|
|
@@ -165,25 +330,42 @@ stacked create feat-auth
|
|
|
165
330
|
stacked create feat-auth-ui
|
|
166
331
|
# ... work, commit ...
|
|
167
332
|
|
|
168
|
-
# 3.
|
|
333
|
+
# 3. Quick check
|
|
334
|
+
stacked status
|
|
335
|
+
|
|
336
|
+
# 4. Need to fix something mid-stack
|
|
169
337
|
stacked checkout feat-auth
|
|
170
338
|
# ... fix, commit ...
|
|
171
339
|
stacked sync --from feat-auth # rebase children
|
|
172
340
|
|
|
173
|
-
#
|
|
341
|
+
# 5. Sync with latest main
|
|
174
342
|
stacked sync
|
|
175
343
|
|
|
176
|
-
#
|
|
177
|
-
stacked submit --draft
|
|
344
|
+
# 6. Submit for review
|
|
345
|
+
stacked submit --draft --title "Add auth" --body "Implements OAuth2 flow"
|
|
178
346
|
|
|
179
|
-
#
|
|
347
|
+
# 7. After review, final submit
|
|
180
348
|
stacked submit
|
|
349
|
+
|
|
350
|
+
# 8. Navigate quickly
|
|
351
|
+
stacked up # go to next branch
|
|
352
|
+
stacked down # go to previous branch
|
|
181
353
|
```
|
|
182
354
|
|
|
183
355
|
## Gotchas
|
|
184
356
|
|
|
357
|
+
- `stacked sync` requires a clean working tree — commit or stash first (except `--dry-run`)
|
|
185
358
|
- `stacked sync` rebases bottom-to-top — resolve conflicts one branch at a time
|
|
186
|
-
- `stacked
|
|
359
|
+
- `stacked sync` leaves rebase in progress on conflict — resolve with `git rebase --continue`, then resume with `stacked sync --from <parent>`
|
|
360
|
+
- `stacked submit` force-pushes by default (use `--no-force` to disable)
|
|
361
|
+
- `stacked submit` and `stacked clean` require `gh` CLI authenticated (`gh auth login`)
|
|
187
362
|
- PRs target parent branches, not trunk — this is intentional for stacked review
|
|
188
|
-
-
|
|
189
|
-
-
|
|
363
|
+
- PRs include auto-generated stack metadata (position, navigation links)
|
|
364
|
+
- Trunk is auto-detected (`main` > `master` > `develop`) — use `stacked trunk <name>` to override
|
|
365
|
+
- Forked branches (one parent, multiple children) are not supported — `detect` reports them but skips
|
|
366
|
+
- `stacked delete --force` on a mid-stack branch requires `stacked sync` afterward
|
|
367
|
+
- `stacked checkout` falls through to `git checkout` for branches not in a stack
|
|
368
|
+
- Detached HEAD is detected and produces a clear error — checkout a branch first
|
|
369
|
+
- Stack file writes are atomic (write to tmp, then rename) to prevent corruption
|
|
370
|
+
- `create` and `adopt` are idempotent — safe to re-run after transient failures
|
|
371
|
+
- Use `stacked doctor` to detect and fix metadata drift (stale branches, missing trunk)
|