Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,19 +433,17 @@ gh stack view --json
Remove a stack from local tracking and delete it on GitHub. Also available as `gh stack delete`.

```
gh stack unstack [flags] [branch]
gh stack unstack [flags]
```

If no branch is specified, uses the current branch to find the stack. Deletes the stack on GitHub first, then removes local tracking. Use `--local` to only remove the local tracking entry.
You must have an active stack checked out locally. The command targets the active stack — the one that contains the currently checked out branch.

Deletes the stack on GitHub first, if it exists, then removes local tracking. Use `--local` to only remove from local tracking.

| Flag | Description |
|------|-------------|
| `--local` | Only delete the stack locally (keep it on GitHub) |

| Argument | Description |
|----------|-------------|
| `[branch]` | A branch in the stack to delete (defaults to the current branch) |

**Examples:**

```sh
Expand All @@ -454,9 +452,6 @@ gh stack unstack

# Only remove local tracking
gh stack unstack --local

# Specify a branch to identify the stack
gh stack unstack feature-auth
```

### `gh stack merge`
Expand Down
14 changes: 5 additions & 9 deletions cmd/unstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,19 @@ import (
)

type unstackOptions struct {
target string
local bool
local bool
}

func UnstackCmd(cfg *config.Config) *cobra.Command {
opts := &unstackOptions{}

cmd := &cobra.Command{
Use: "unstack [branch]",
Use: "unstack",
Aliases: []string{"delete"},
Short: "Delete a stack locally and on GitHub",
Long: "Remove a stack from local tracking and delete it on GitHub. Use --local to only remove local tracking.",
Args: cobra.MaximumNArgs(1),
Long: "Remove the current active stack from local tracking and delete it on GitHub. Use --local to only remove local tracking.",
Args: cobra.NoArgs,
Comment thread
skarim marked this conversation as resolved.
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
opts.target = args[0]
}
return runUnstack(cfg, opts)
},
}
Expand All @@ -38,7 +34,7 @@ func UnstackCmd(cfg *config.Config) *cobra.Command {
}

func runUnstack(cfg *config.Config, opts *unstackOptions) error {
result, err := loadStack(cfg, opts.target)
result, err := loadStack(cfg, "")
if err != nil {
return ErrNotInStack
}
Expand Down
35 changes: 2 additions & 33 deletions cmd/unstack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,37 +94,6 @@ func TestUnstack_Local(t *testing.T) {
assert.Empty(t, sf.Stacks)
}

func TestUnstack_WithTarget(t *testing.T) {
gitDir := t.TempDir()
restore := git.SetOps(&git.MockOps{
GitDirFn: func() (string, error) { return gitDir, nil },
CurrentBranchFn: func() (string, error) { return "unrelated", nil },
})
defer restore()

s1 := stack.Stack{
Trunk: stack.BranchRef{Branch: "main"},
Branches: []stack.BranchRef{{Branch: "b1"}, {Branch: "b2"}},
}
s2 := stack.Stack{
Trunk: stack.BranchRef{Branch: "main"},
Branches: []stack.BranchRef{{Branch: "b3"}, {Branch: "b4"}},
}
writeTwoStacks(t, gitDir, s1, s2)

cfg, outR, errR := config.NewTestConfig()
err := runUnstack(cfg, &unstackOptions{target: "b3", local: true})
output := collectOutput(cfg, outR, errR)

require.NoError(t, err)
assert.Contains(t, output, "Stack removed")

sf, err := stack.Load(gitDir)
require.NoError(t, err)
require.Len(t, sf.Stacks, 1)
assert.Equal(t, []string{"b1", "b2"}, sf.Stacks[0].BranchNames())
}

func TestUnstack_NoStackID_WarnsAndSkipsAPI(t *testing.T) {
gitDir := t.TempDir()
restore := git.SetOps(&git.MockOps{
Expand Down Expand Up @@ -225,7 +194,7 @@ func TestUnstack_API409_ShowsErrorAndStopsLocalDeletion(t *testing.T) {
}

func TestUnstack_RemovesCorrectStackByPointer(t *testing.T) {
// Two stacks share the same trunk "main". Targeting "b3" should remove
// Two stacks share the same trunk "main". Current branch "b3" should remove
// only the second stack (b3,b4), leaving the first (b1,b2) intact.
// This verifies pointer-based removal instead of branch-name-based.
gitDir := t.TempDir()
Expand All @@ -246,7 +215,7 @@ func TestUnstack_RemovesCorrectStackByPointer(t *testing.T) {
writeTwoStacks(t, gitDir, s1, s2)

cfg, outR, errR := config.NewTestConfig()
err := runUnstack(cfg, &unstackOptions{target: "b3", local: true})
err := runUnstack(cfg, &unstackOptions{local: true})
output := collectOutput(cfg, outR, errR)

require.NoError(t, err)
Expand Down
13 changes: 4 additions & 9 deletions docs/src/content/docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,21 +346,19 @@ gh stack push --remote upstream
Remove a stack from local tracking and delete it on GitHub. Also available as `gh stack delete`.

```sh
gh stack unstack [flags] [branch]
gh stack unstack [flags]
```

Deletes the stack on GitHub first, then removes it from local tracking. If the remote deletion fails, the local state is left untouched so you can retry. Use `--local` to skip the remote deletion and only remove local tracking.
You must have a branch from the stack checked out locally. The command targets the active stack — the one that contains the currently checked out branch.

Deletes the stack on GitHub first, if it exists, then removes it from local tracking. If the remote deletion fails, the local state is left untouched so you can retry. Use `--local` to skip the remote deletion and only remove local tracking.

This is useful when you need to restructure a stack — remove a branch, reorder branches, rename branches, or make other large changes. After unstacking, use `gh stack init --adopt` to re-create the stack with the desired structure.

| Flag | Description |
|------|-------------|
| `--local` | Only delete the stack locally (keep it on GitHub) |

| Argument | Description |
|----------|-------------|
| `[branch]` | A branch in the stack to identify which stack to delete (defaults to the current branch) |

**Examples:**

```sh
Expand All @@ -369,9 +367,6 @@ gh stack unstack

# Only remove local tracking
gh stack unstack --local

# Specify a branch to identify which stack
gh stack unstack feature-auth
```

### `gh stack link`
Expand Down
11 changes: 3 additions & 8 deletions skills/gh-stack/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -788,8 +788,10 @@ When a branch name is provided, the command resolves it against locally tracked

Tear down a stack so you can restructure it — remove a branch, reorder branches, rename branches, or make other large changes. After unstacking, use `gh stack init` to re-create the stack with the desired structure.

You must have a branch from the stack checked out locally. The command targets the active stack — the one that contains the currently checked out branch.

```
gh stack unstack [flags] [branch]
gh stack unstack [flags]
```

```bash
Expand All @@ -799,19 +801,12 @@ gh stack init --base main --adopt branch-2 branch-1 branch-3 # reordered

# Only remove local tracking (keep the stack on GitHub)
gh stack unstack --local

# Specify a branch to identify which stack to tear down
gh stack unstack feature-auth
```

| Flag | Description |
|------|-------------|
| `--local` | Only delete the stack locally (keep it on GitHub) |

| Argument | Description |
|----------|-------------|
| `[branch]` | A branch in the stack (defaults to the current branch) |

---

## Output conventions
Expand Down