gh-stack 0.14.1 → 0.15.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 CHANGED
@@ -83,12 +83,19 @@ create <branch-name> [--description <desc>]
83
83
  Create a new git branch off the current branch and add it to
84
84
  the stack. The current branch must already be tracked.
85
85
 
86
- submit [-d|--draft] [-n|--no-edit] [-t|--title <t>] [-b|--body <b>] [--body-file <f>] [--dry-run]
86
+ submit [-d|--draft] [-n|--no-edit] [-t|--title <t>] [-b|--body <b>] [--body-file <f>] [--restack] [--dry-run]
87
87
  Push all downstack branches to GitHub, create PRs for branches
88
88
  that don't have them, number each PR title with its stack position
89
89
  `(N/M)`, and update all PR descriptions with stack visualization.
90
90
  Idempotent — safe to run repeatedly.
91
91
 
92
+ --restack: after submitting, restack the branches above the current
93
+ one onto it. submit covers DOWNSTACK (trunk → current); restack covers
94
+ UPSTACK (the children), so together they sync the whole stack from
95
+ wherever you're standing. The everyday loop: drop downstack, fix an
96
+ issue, commit, then `submit --restack` to push the fix and propagate
97
+ it up in one command. Skips cleanly when nothing sits above you.
98
+
92
99
  Self-healing: if the current branch isn't tracked in a stack yet,
93
100
  submit auto-detects the chain from trunk → current, registers (or
94
101
  reconciles into) a stack, then pushes + creates PRs for the whole
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-stack",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Stacked PR manager for squash-merge workflows",
5
5
  "author": "Kiliman <kiliman@gmail.com>",
6
6
  "license": "MIT",
@@ -4,13 +4,14 @@ import pc from "picocolors";
4
4
  import { $ } from "bun";
5
5
  import * as git from "../lib/git.ts";
6
6
  import {
7
+ buildRebaseChain,
7
8
  findStackForBranch,
8
9
  getOrderedBranches,
9
10
  readMetadata,
10
11
  writeMetadata,
11
12
  } from "../lib/metadata.ts";
12
13
  import { isAutoYes } from "../lib/ui.ts";
13
- import type { StackMetadata } from "../types.ts";
14
+ import type { Stack, StackMetadata } from "../types.ts";
14
15
  import { getPrNumber } from "../lib/github.ts";
15
16
  import { resolveOrCreateStack } from "../lib/chain.ts";
16
17
  import { reconcilePrTitles, refreshStackViz, branchToTitle } from "./update-prs.ts";
@@ -36,6 +37,8 @@ OPTIONS
36
37
  -t, --title <title> Set the PR title (current branch) — creates or updates
37
38
  -b, --body <body> Set the PR body (current branch) — creates or updates
38
39
  --body-file <f> Read PR body from a file
40
+ --restack After submitting, restack the branches above the current
41
+ one onto it (propagate a downstack fix up to children)
39
42
  --dry-run Show what would happen without doing anything
40
43
 
41
44
  Updating an existing PR: -t / -b also update the PR for the branch you're on
@@ -43,10 +46,17 @@ if it already exists — the title keeps its (N/M) stack position, and the body
43
46
  is replaced with the "Stacked on" block re-merged in. Use this instead of
44
47
  gh pr edit so the stack visualization is never clobbered.
45
48
 
49
+ Restack after submit (--restack): submit covers DOWNSTACK (trunk → current);
50
+ restack covers UPSTACK (the children above current). Together they sync the
51
+ whole stack from wherever you're standing. The common loop: drop downstack,
52
+ fix an issue, commit, then \`submit --restack\` to push the fix and propagate
53
+ it up — one command instead of two. Skips cleanly if nothing sits above you.
54
+
46
55
  EXAMPLES
47
56
  gh-stack submit # Push + create PRs interactively
48
57
  gh-stack submit -n # Push + create PRs with auto-titles
49
58
  gh-stack submit -t "My PR" -b "Description" # Explicit title and body
59
+ gh-stack submit --restack # Submit, then restack children onto it
50
60
  gh-stack submit --draft # Create new PRs as drafts
51
61
  gh-stack submit --dry-run # Preview what would happen
52
62
  `;
@@ -90,6 +100,7 @@ export default async function submit(args: string[]): Promise<void> {
90
100
 
91
101
  const draftFlag = args.includes("--draft") || args.includes("-d");
92
102
  const dryRun = args.includes("--dry-run");
103
+ const restackFlag = args.includes("--restack");
93
104
 
94
105
  // Parse value flags
95
106
  let titleFlag: string | undefined;
@@ -355,6 +366,7 @@ export default async function submit(args: string[]): Promise<void> {
355
366
  `Dry run complete: would push ${scope.length}, create ${created}, update ${existing} PR(s)`,
356
367
  ),
357
368
  );
369
+ if (restackFlag) await maybeRestack(stack, branch, true);
358
370
  return;
359
371
  }
360
372
 
@@ -388,4 +400,28 @@ export default async function submit(args: string[]): Promise<void> {
388
400
  (viz.unchanged > 0 ? `, ${viz.unchanged} unchanged` : ""),
389
401
  ),
390
402
  );
403
+
404
+ // ── --restack: propagate a downstack fix up to the children ──
405
+ // submit owns DOWNSTACK (trunk → current); restack owns UPSTACK (the branches
406
+ // above current). Running both from one branch syncs the whole stack.
407
+ if (restackFlag) await maybeRestack(stack, branch, false);
408
+ }
409
+
410
+ /**
411
+ * Chain into `restack` from the current branch after a submit, so a downstack
412
+ * fix propagates up to its children in one command. No-op (with a friendly
413
+ * note) when nothing sits above the current branch — there's nothing to rebase.
414
+ */
415
+ async function maybeRestack(stack: Stack, branch: string, dryRun: boolean): Promise<void> {
416
+ // buildRebaseChain returns the current branch + all its descendants; a length
417
+ // of 1 means it's the top of its line, so there's nothing above to restack.
418
+ if (buildRebaseChain(stack, branch).length <= 1) {
419
+ console.log();
420
+ p.log.info(pc.dim(`--restack: nothing sits above ${pc.yellow(branch)} — nothing to restack.`));
421
+ return;
422
+ }
423
+
424
+ console.log();
425
+ const { default: restack } = await import("./restack.ts");
426
+ await restack(dryRun ? ["--dry-run"] : []);
391
427
  }