Skip to content

Commit 7a268fc

Browse files
skarimCopilotCopilotLukeghenco
authored
modify command (#72)
* git primitives for modify cmd * extract reusable TUI parts * modify cmd * recreate stack after modify * add checks to prevent other modifications while modify is applying * modify continue for resuming after resolving conflicts * fix bug with duplicate stack entries after modifying * reuse conflict resolution help msg from rebase * additional confirmation before overwriting stack on remote * fix recreate order of operations Co-authored-by: Copilot <copilot@github.com> * move base commit instead of cherry picking for fold up * check to ensure we aren't left with zero branches * unify and dedupe across view and modify tui * more detailed help instructions Co-authored-by: Copilot <copilot@github.com> * only recommend submit if stack exists on remote Co-authored-by: Copilot <copilot@github.com> * tests for modify tui, apply modifications, submit modifications * refactor submit for regular and pending modifications * rename recover to abort Co-authored-by: Copilot <copilot@github.com> * docs for modify cmd * tui styling updates * updated tui screenshot * addressing review comments * Fix 4 bugs from code review Bug 1: Move RevParseMap error check before using originalRefs. The error from git.RevParseMap() was deferred past iteration of originalRefs, which could panic on a nil map. Bug 2: Differentiate cherry-pick vs rebase conflicts in modify. Cherry-pick conflicts don't save state as 'conflict' phase, so --continue won't work. Now prints --abort-only instructions for cherry-pick conflicts. Bug 3: Unwind now cleans up branches created by renames. After restoring snapshot branches, Unwind deletes renamed branch names that don't belong to the original snapshot. Bug 4: Simplify push message in submit command. Changed from 'Pushing N branches to remote...' to 'Pushing to remote...' since individual branches may fail. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix 7 nit issues from code review 11: Add named constants for phase strings (PhaseApplying, PhaseConflict, PhasePendingSubmit) in state.go; replace remaining raw literals in state.go CheckStateGuard. 14: Fix bottomLines comment mismatch — listed 3 items but value is 2. 15: Extract magic number 88 to MinWidthForArt constant in header.go. 16: Remove unused stackview import anchor in model.go — the import is used via types.go where BranchNode is embedded. 17: Simplify CheckStackLinearity parent resolution — ActiveBaseBranch already handles skipping merged branches. 18: Fix rename undo matching any rename — add NewName check so only the specific rename being undone is matched. 20: Add TestUndoRename and TestUndoRename_DoesNotAffectOtherRenames to validate rename undo behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make cherry-pick conflicts recoverable via --continue Previously, cherry-pick conflicts during fold-down operations could only be resolved with --abort. Now they save full conflict state (phase, conflict type, fold branch/target, remaining branches) to the state file, enabling recovery via 'gh stack modify --continue'. Changes: - Add ConflictType field to StateFile (rebase or cherry_pick) - Add FoldBranch/FoldTarget fields for cherry-pick context - Add CherryPickContinue to git package (cherry-pick --continue) - Save cherry-pick conflict state in ApplyPlan with remaining branches - ContinueApply handles both rebase and cherry-pick conflicts - Unified conflict messaging in cmd/modify.go (both types show --continue) - Updated test to verify cherry-pick conflict state is saved correctly * Apply suggestions from code review Co-authored-by: Luke Ghenco <lukeghenco@github.com> Co-authored-by: Sameen Karim <skarim@github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Luke Ghenco <lukeghenco@github.com>
1 parent ad56830 commit 7a268fc

40 files changed

Lines changed: 7520 additions & 813 deletions

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,68 @@ gh stack rebase --continue
232232
gh stack rebase --abort
233233
```
234234

235+
### `gh stack modify`
236+
237+
Interactively restructure the current stack.
238+
239+
```
240+
gh stack modify [flags]
241+
```
242+
243+
Opens a terminal UI for restructuring a stack. You can rename, drop, reorder, and fold branches into adjacent ones. All the changes are staged during the preview and applied at once on save.
244+
245+
If the stack of PRs has been created on GitHub, run `gh stack submit` afterwards to push the changes and recreate the stack.
246+
247+
| Flag | Description |
248+
|------|-------------|
249+
| `--continue` | Continue after resolving conflicts |
250+
| `--abort` | Abort the modify session and restore the stack to its pre-modify state |
251+
252+
**Operations:**
253+
254+
- **Drop** (`x`): Remove a branch and its commits from the stack. Local branch and associated PR are preserved.
255+
- **Fold down** (`d`): Absorb a branch's commits into the branch below (toward trunk). Folded branch removed from stack.
256+
- **Fold up** (`u`): Absorb a branch's commits into the branch above (away from trunk). Folded branch removed from stack.
257+
- **Reorder** (`Shift+↑`/`Shift+↓`): Move a branch up (away from trunk) or down (toward trunk) in the stack.
258+
- **Rename** (`r`): Rename a branch locally and in the stack metadata.
259+
- **Undo** (`z`): Undo the last staged action.
260+
261+
**Keybindings:**
262+
263+
| Key | Action |
264+
|-----|--------|
265+
| ``/`` | Navigate branch list |
266+
| `f` | View files changed |
267+
| `c` | View commits |
268+
| `x` | Drop branch |
269+
| `r` | Rename branch |
270+
| `u/d` | Fold branch up/down |
271+
| `Shift+↑`/`Shift+↓` | Move branch up/down |
272+
| `z` | Undo last action |
273+
| `Ctrl+S` | Apply all changes |
274+
| `q`/`Esc` | Cancel and exit |
275+
| `?` | Help |
276+
277+
**Preconditions:**
278+
- Must have an active stack checked out locally
279+
- Working tree must be clean
280+
- No rebase in progress
281+
- No PR in the stack is queued for merge
282+
- Commit history must be linear
283+
284+
**Examples:**
285+
286+
```sh
287+
# Open the modify TUI
288+
gh stack modify
289+
290+
# Continue after resolving a conflict
291+
gh stack modify --continue
292+
293+
# Abort and restore to the previous state
294+
gh stack modify --abort
295+
```
296+
235297
### `gh stack sync`
236298

237299
Fetch, rebase, push, and sync PR state in a single command.

cmd/add.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/github/gh-stack/internal/branch"
88
"github.com/github/gh-stack/internal/config"
99
"github.com/github/gh-stack/internal/git"
10+
"github.com/github/gh-stack/internal/modify"
1011
"github.com/github/gh-stack/internal/stack"
1112
"github.com/spf13/cobra"
1213
)
@@ -54,6 +55,12 @@ func runAdd(cfg *config.Config, opts *addOptions, args []string) error {
5455
return ErrNotInStack
5556
}
5657
gitDir := result.GitDir
58+
59+
if err := modify.CheckStateGuard(gitDir); err != nil {
60+
cfg.Errorf("%s", err)
61+
return ErrModifyRecovery
62+
}
63+
5764
sf := result.StackFile
5865
s := result.Stack
5966
currentBranch := result.CurrentBranch

0 commit comments

Comments
 (0)