gh-stack 0.19.7 → 0.19.8

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
@@ -289,7 +289,9 @@ merge --base [--dry-run] [-d|--delete-branch] [--no-push|--push]
289
289
  via GitHub (PR3 → PR2 → PR1), then enables auto-merge for the base PR
290
290
  into main. The whole stack lands on main as a single squash commit.
291
291
  All merges happen on GitHub so PRs show as "Merged", Linear tickets
292
- close automatically, and GitHub Actions fire normally. Skips already-
292
+ close automatically, and GitHub Actions fire normally. Squash commit
293
+ subjects leave out the ` (N/M)` stack position that `submit` adds to
294
+ PR titles. Skips already-
293
295
  merged PRs (safe to re-run). Waits for GitHub between merges, and
294
296
  self-heals its spurious "Head branch is out of date" (a stale PR
295
297
  head pointer after a child squash) by re-syncing and retrying. Before
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-stack",
3
- "version": "0.19.7",
3
+ "version": "0.19.8",
4
4
  "description": "Stacked PR manager for squash-merge workflows",
5
5
  "author": "Kiliman <kiliman@gmail.com>",
6
6
  "license": "MIT",
@@ -25,6 +25,7 @@ import type { Stack } from "../types.ts";
25
25
  import { confirmAction, isAutoYes } from "../lib/ui.ts";
26
26
  import { guardRemoteContent } from "../lib/remote-guard.ts";
27
27
  import { ensureNoPendingRun } from "./restack.ts";
28
+ import { stripSeqSuffix } from "./update-prs.ts";
28
29
  import {
29
30
  planMerges,
30
31
  advancePastMerged,
@@ -62,6 +63,24 @@ export function isTransientMergeError(stderr: string): boolean {
62
63
  );
63
64
  }
64
65
 
66
+ /**
67
+ * The squash commit subject for a PR titled `title`, without the ` (N/M)`
68
+ * stack position `submit` adds to PR titles, which GitHub would otherwise put
69
+ * on the trunk's commit. Null when the title has no position, so GitHub's own
70
+ * "<title> (#N)" stands.
71
+ */
72
+ export function squashSubject(title: string, prNumber: number): string | null {
73
+ const base = stripSeqSuffix(title);
74
+ return base === title.trimEnd() ? null : `${base} (#${prNumber})`;
75
+ }
76
+
77
+ /** `gh pr merge` args setting the squash subject (see squashSubject); empty to keep GitHub's. */
78
+ async function squashSubjectArgs(prNumber: number): Promise<string[]> {
79
+ const title = (await getPrInfo(prNumber))?.title;
80
+ const subject = title ? squashSubject(title, prNumber) : null;
81
+ return subject ? ["--subject", subject] : [];
82
+ }
83
+
65
84
  /**
66
85
  * Squash-merge a PR, healing the two timing artifacts a top-down cascade
67
86
  * provokes — neither of which is a real conflict:
@@ -87,8 +106,9 @@ async function squashMergeWithRetry(
87
106
  s: ReturnType<typeof p.spinner>,
88
107
  ): Promise<{ ok: true } | { ok: false; stderr: string; genuine: boolean }> {
89
108
  const maxAttempts = MERGE_RETRY_BACKOFF_MS.length + 1;
109
+ const subject = await squashSubjectArgs(prNumber);
90
110
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
91
- const ghArgs = ["pr", "merge", String(prNumber), "--squash"];
111
+ const ghArgs = ["pr", "merge", String(prNumber), "--squash", ...subject];
92
112
  if (deleteFlag) ghArgs.push("--delete-branch");
93
113
 
94
114
  const proc = Bun.spawn(["gh", ...ghArgs], { stdout: "pipe", stderr: "pipe" });
@@ -423,7 +443,8 @@ OPTIONS
423
443
  : "Single branch stack — enabling auto-merge on GitHub.",
424
444
  );
425
445
  if (!dryRun) {
426
- const auto = await $`gh pr merge ${basePr} --squash --auto`.nothrow().quiet();
446
+ const subject = await squashSubjectArgs(basePr);
447
+ const auto = await $`gh pr merge ${basePr} --squash --auto ${subject}`.nothrow().quiet();
427
448
  if (auto.exitCode === 0) {
428
449
  p.log.success(`Auto-merge enabled for PR #${basePr}`);
429
450
  } else {
@@ -712,6 +733,7 @@ OPTIONS
712
733
 
713
734
  const enableAutoMerge = async (): Promise<{ ok: boolean; stderr: string }> => {
714
735
  const ghArgs = ["pr", "merge", String(basePr), "--squash", "--auto"];
736
+ if (basePr != null) ghArgs.push(...(await squashSubjectArgs(basePr)));
715
737
  if (deleteFlag) ghArgs.push("--delete-branch");
716
738
  const proc = Bun.spawn(["gh", ...ghArgs], { stdout: "pipe", stderr: "pipe" });
717
739
  const stderr = await new Response(proc.stderr).text();