dubstack 0.2.0 → 0.3.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/dist/index.js +96 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ import { createRequire } from "module";
|
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
|
+
// src/commands/checkout.ts
|
|
9
|
+
import search from "@inquirer/search";
|
|
10
|
+
|
|
8
11
|
// src/lib/errors.ts
|
|
9
12
|
var DubError = class extends Error {
|
|
10
13
|
constructor(message) {
|
|
@@ -201,6 +204,18 @@ async function commitStaged(message, cwd) {
|
|
|
201
204
|
);
|
|
202
205
|
}
|
|
203
206
|
}
|
|
207
|
+
async function listBranches(cwd) {
|
|
208
|
+
try {
|
|
209
|
+
const { stdout } = await execa(
|
|
210
|
+
"git",
|
|
211
|
+
["branch", "--format=%(refname:short)"],
|
|
212
|
+
{ cwd }
|
|
213
|
+
);
|
|
214
|
+
return stdout.trim().split("\n").filter(Boolean);
|
|
215
|
+
} catch {
|
|
216
|
+
throw new DubError("Failed to list branches.");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
204
219
|
|
|
205
220
|
// src/lib/state.ts
|
|
206
221
|
import * as crypto from "crypto";
|
|
@@ -315,6 +330,76 @@ function topologicalOrder(stack) {
|
|
|
315
330
|
return result;
|
|
316
331
|
}
|
|
317
332
|
|
|
333
|
+
// src/commands/checkout.ts
|
|
334
|
+
function getTrackedBranches(state) {
|
|
335
|
+
const names = /* @__PURE__ */ new Set();
|
|
336
|
+
for (const stack of state.stacks) {
|
|
337
|
+
for (const branch of stack.branches) {
|
|
338
|
+
names.add(branch.name);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return [...names].sort();
|
|
342
|
+
}
|
|
343
|
+
function getValidBranches(tracked, local) {
|
|
344
|
+
const localSet = new Set(local);
|
|
345
|
+
return tracked.filter((b) => localSet.has(b));
|
|
346
|
+
}
|
|
347
|
+
async function checkout(name, cwd) {
|
|
348
|
+
await checkoutBranch(name, cwd);
|
|
349
|
+
return { branch: name };
|
|
350
|
+
}
|
|
351
|
+
async function interactiveCheckout(cwd) {
|
|
352
|
+
const state = await readState(cwd);
|
|
353
|
+
const trackedBranches = getTrackedBranches(state);
|
|
354
|
+
const localBranches = await listBranches(cwd);
|
|
355
|
+
const validBranches = getValidBranches(trackedBranches, localBranches);
|
|
356
|
+
if (validBranches.length === 0) {
|
|
357
|
+
throw new DubError(
|
|
358
|
+
"No valid tracked branches found. Run 'dub create' first."
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
let currentBranch = null;
|
|
362
|
+
try {
|
|
363
|
+
currentBranch = await getCurrentBranch(cwd);
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
const controller = new AbortController();
|
|
367
|
+
const onKeypress = (_str, key) => {
|
|
368
|
+
if (key && key.name === "escape") {
|
|
369
|
+
controller.abort();
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
process.stdin.on("keypress", onKeypress);
|
|
373
|
+
try {
|
|
374
|
+
const selected = await search(
|
|
375
|
+
{
|
|
376
|
+
message: "Checkout a branch (autocomplete or arrow keys)",
|
|
377
|
+
source(term) {
|
|
378
|
+
const filtered = term ? validBranches.filter(
|
|
379
|
+
(b) => b.toLowerCase().includes(term.toLowerCase())
|
|
380
|
+
) : validBranches;
|
|
381
|
+
return filtered.map((name) => ({
|
|
382
|
+
name,
|
|
383
|
+
value: name,
|
|
384
|
+
disabled: name === currentBranch ? "(current)" : false
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
{ signal: controller.signal }
|
|
389
|
+
);
|
|
390
|
+
return checkout(selected, cwd);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (error instanceof Error) {
|
|
393
|
+
if (error.name === "ExitPromptError" || error.name === "AbortError" || error.name === "AbortPromptError") {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
throw error;
|
|
398
|
+
} finally {
|
|
399
|
+
process.stdin.off("keypress", onKeypress);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
318
403
|
// src/lib/undo-log.ts
|
|
319
404
|
import * as fs2 from "fs";
|
|
320
405
|
import * as path2 from "path";
|
|
@@ -1076,6 +1161,17 @@ Examples:
|
|
|
1076
1161
|
$ dub submit --dry-run Preview what would happen`
|
|
1077
1162
|
).action(runSubmit);
|
|
1078
1163
|
program.command("ss").description("Submit the current stack (alias for submit)").option("--dry-run", "Print what would happen without executing").action(runSubmit);
|
|
1164
|
+
program.command("co").argument("[branch]", "Branch to checkout (interactive if omitted)").description("Checkout a branch (interactive picker if no name given)").action(async (branch) => {
|
|
1165
|
+
if (branch) {
|
|
1166
|
+
const result = await checkout(branch, process.cwd());
|
|
1167
|
+
console.log(chalk.green(`\u2714 Switched to '${result.branch}'`));
|
|
1168
|
+
} else {
|
|
1169
|
+
const result = await interactiveCheckout(process.cwd());
|
|
1170
|
+
if (result) {
|
|
1171
|
+
console.log(chalk.green(`\u2714 Switched to '${result.branch}'`));
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1079
1175
|
async function runSubmit(options) {
|
|
1080
1176
|
const result = await submit(process.cwd(), options.dryRun ?? false);
|
|
1081
1177
|
if (result.pushed.length > 0) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lib/errors.ts","../src/lib/git.ts","../src/lib/state.ts","../src/lib/undo-log.ts","../src/commands/create.ts","../src/commands/init.ts","../src/commands/log.ts","../src/commands/restack.ts","../src/commands/submit.ts","../src/lib/github.ts","../src/lib/pr-body.ts","../src/commands/undo.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * DubStack CLI — manage stacked diffs with ease.\n *\n * A local-first tool for managing chains of dependent git branches\n * (stacked diffs) without manually dealing with complex rebase chains.\n *\n * @example\n * ```bash\n * dub init # Initialize in current repo\n * dub create feat/my-branch # Create stacked branch\n * dub log # View stack tree\n * dub restack # Rebase stack onto updated parent\n * dub undo # Undo last dub operation\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createRequire } from \"node:module\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { create } from \"./commands/create\";\nimport { init } from \"./commands/init\";\nimport { log } from \"./commands/log\";\nimport { restack, restackContinue } from \"./commands/restack\";\nimport { submit } from \"./commands/submit\";\nimport { undo } from \"./commands/undo\";\nimport { DubError } from \"./lib/errors\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n\t.name(\"dub\")\n\t.description(\"Manage stacked diffs (dependent git branches) with ease\")\n\t.version(version);\n\nprogram\n\t.command(\"init\")\n\t.description(\"Initialize DubStack in the current git repository\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub init Initialize DubStack, creating .git/dubstack/ and updating .gitignore`,\n\t)\n\t.action(async () => {\n\t\tconst result = await init(process.cwd());\n\t\tif (result.status === \"created\") {\n\t\t\tconsole.log(chalk.green(\"✔ DubStack initialized\"));\n\t\t} else {\n\t\t\tconsole.log(chalk.yellow(\"⚠ DubStack already initialized\"));\n\t\t}\n\t});\n\nprogram\n\t.command(\"create\")\n\t.argument(\"<branch-name>\", \"Name of the new branch to create\")\n\t.description(\"Create a new branch stacked on top of the current branch\")\n\t.option(\"-m, --message <message>\", \"Commit staged changes with this message\")\n\t.option(\"-a, --all\", \"Stage all changes before committing (requires -m)\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub create feat/api Create branch only\n $ dub create feat/api -m \"feat: add API\" Create branch + commit staged\n $ dub create feat/api -am \"feat: add API\" Stage all + create + commit`,\n\t)\n\t.action(\n\t\tasync (\n\t\t\tbranchName: string,\n\t\t\toptions: { message?: string; all?: boolean },\n\t\t) => {\n\t\t\tconst result = await create(branchName, process.cwd(), {\n\t\t\t\tmessage: options.message,\n\t\t\t\tall: options.all,\n\t\t\t});\n\t\t\tif (result.committed) {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created '${result.branch}' on '${result.parent}' • ${result.committed}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created branch '${result.branch}' on top of '${result.parent}'`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command(\"log\")\n\t.description(\"Display an ASCII tree of the current stack\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub log Show the branch tree with current branch highlighted`,\n\t)\n\t.action(async () => {\n\t\tconst output = await log(process.cwd());\n\t\t// Apply chalk styling to the output\n\t\tconst styled = output\n\t\t\t.replace(/\\*(.+?) \\(Current\\)\\*/g, chalk.bold.cyan(\"$1 (Current)\"))\n\t\t\t.replace(/⚠ \\(missing\\)/g, chalk.yellow(\"⚠ (missing)\"));\n\t\tconsole.log(styled);\n\t});\n\nprogram\n\t.command(\"restack\")\n\t.description(\"Rebase all branches in the stack onto their updated parents\")\n\t.option(\"--continue\", \"Continue restacking after resolving conflicts\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub restack Rebase the current stack\n $ dub restack --continue Continue after resolving conflicts`,\n\t)\n\t.action(async (options: { continue?: boolean }) => {\n\t\tconst result = options.continue\n\t\t\t? await restackContinue(process.cwd())\n\t\t\t: await restack(process.cwd());\n\n\t\tif (result.status === \"up-to-date\") {\n\t\t\tconsole.log(chalk.green(\"✔ Stack is already up to date\"));\n\t\t} else if (result.status === \"conflict\") {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(`⚠ Conflict while restacking '${result.conflictBranch}'`),\n\t\t\t);\n\t\t\tconsole.log(\n\t\t\t\tchalk.dim(\n\t\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\tchalk.green(`✔ Restacked ${result.rebased.length} branch(es)`),\n\t\t\t);\n\t\t\tfor (const branch of result.rebased) {\n\t\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t\t}\n\t\t}\n\t});\n\nprogram\n\t.command(\"undo\")\n\t.description(\"Undo the last dub create or dub restack operation\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub undo Roll back the last dub operation`,\n\t)\n\t.action(async () => {\n\t\tconst result = await undo(process.cwd());\n\t\tconsole.log(chalk.green(`✔ Undid '${result.undone}': ${result.details}`));\n\t});\n\nprogram\n\t.command(\"submit\")\n\t.description(\n\t\t\"Push branches and create/update GitHub PRs for the current stack\",\n\t)\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub submit Push and create/update PRs\n $ dub submit --dry-run Preview what would happen`,\n\t)\n\t.action(runSubmit);\n\nprogram\n\t.command(\"ss\")\n\t.description(\"Submit the current stack (alias for submit)\")\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.action(runSubmit);\n\nasync function runSubmit(options: { dryRun?: boolean }) {\n\tconst result = await submit(process.cwd(), options.dryRun ?? false);\n\n\tif (result.pushed.length > 0) {\n\t\tconsole.log(\n\t\t\tchalk.green(\n\t\t\t\t`✔ Pushed ${result.pushed.length} branch(es), created ${result.created.length} PR(s), updated ${result.updated.length} PR(s)`,\n\t\t\t),\n\t\t);\n\t\tfor (const branch of [...result.created, ...result.updated]) {\n\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t}\n\t}\n}\n\nasync function main() {\n\ttry {\n\t\tawait program.parseAsync(process.argv);\n\t} catch (error) {\n\t\tif (error instanceof DubError) {\n\t\t\tconsole.error(chalk.red(`✖ ${error.message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nmain();\n","/**\n * Base error class for all user-facing DubStack errors.\n *\n * The CLI entry point catches instances of this class and prints\n * a clean, colored error message. Unknown errors get a full stack trace.\n *\n * @example\n * ```ts\n * throw new DubError(\"Branch 'feat/x' already exists\")\n * ```\n */\nexport class DubError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"DubError\";\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/**\n * Checks whether the given directory is inside a git repository.\n * @returns `true` if inside a git worktree, `false` otherwise. Never throws.\n */\nexport async function isGitRepo(cwd: string): Promise<boolean> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--is-inside-work-tree\"],\n\t\t\t{ cwd },\n\t\t);\n\t\treturn stdout.trim() === \"true\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Returns the absolute path to the repository root.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getRepoRoot(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n\t\t\tcwd,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the name of the currently checked-out branch.\n * @throws {DubError} If HEAD is detached or the repo has no commits.\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--abbrev-ref\", \"HEAD\"],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst branch = stdout.trim();\n\t\tif (branch === \"HEAD\") {\n\t\t\tthrow new DubError(\n\t\t\t\t\"HEAD is detached. Checkout a branch before running this command.\",\n\t\t\t);\n\t\t}\n\t\treturn branch;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(\n\t\t\t\"Repository has no commits. Make at least one commit first.\",\n\t\t);\n\t}\n}\n\n/**\n * Checks whether a branch with the given name exists locally.\n * @returns `true` if the branch exists, `false` otherwise. Never throws.\n */\nexport async function branchExists(\n\tname: string,\n\tcwd: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"rev-parse\", \"--verify\", `refs/heads/${name}`], {\n\t\t\tcwd,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a new branch and switches to it.\n * @throws {DubError} If a branch with that name already exists.\n */\nexport async function createBranch(name: string, cwd: string): Promise<void> {\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\tawait execa(\"git\", [\"checkout\", \"-b\", name], { cwd });\n}\n\n/**\n * Switches to an existing branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function checkoutBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"checkout\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Deletes a local branch forcefully. Used by undo to remove created branches.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function deleteBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"branch\", \"-D\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Failed to delete branch '${name}'. It may not exist.`);\n\t}\n}\n\n/**\n * Force-moves a branch pointer to a specific commit SHA.\n * Used by undo to reset branches to their pre-operation tips.\n */\nexport async function forceBranchTo(\n\tname: string,\n\tsha: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tconst current = await getCurrentBranch(cwd).catch(() => null);\n\t\tif (current === name) {\n\t\t\tawait execa(\"git\", [\"reset\", \"--hard\", sha], { cwd });\n\t\t} else {\n\t\t\tawait execa(\"git\", [\"branch\", \"-f\", name, sha], { cwd });\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to reset branch '${name}' to ${sha}.`);\n\t}\n}\n\n/**\n * Checks whether the working tree is clean (no uncommitted changes).\n * @returns `true` if clean (no output from `git status --porcelain`).\n */\nexport async function isWorkingTreeClean(cwd: string): Promise<boolean> {\n\tconst { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n\treturn stdout.trim() === \"\";\n}\n\n/**\n * Performs `git rebase --onto` to move a branch from one base to another.\n *\n * @param newBase - The commit/branch to rebase onto\n * @param oldBase - The old base commit to replay from\n * @param branch - The branch being rebased\n * @throws {DubError} If a merge conflict occurs during rebase\n */\nexport async function rebaseOnto(\n\tnewBase: string,\n\toldBase: string,\n\tbranch: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--onto\", newBase, oldBase, branch], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Conflict while restacking '${branch}'.\\n` +\n\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t);\n\t}\n}\n\n/**\n * Continues an in-progress rebase after conflicts have been resolved.\n * @throws {DubError} If the rebase continue fails.\n */\nexport async function rebaseContinue(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--continue\"], {\n\t\t\tcwd,\n\t\t\tenv: { GIT_EDITOR: \"true\" },\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Failed to continue rebase. Ensure all conflicts are resolved and staged.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the merge-base (common ancestor) commit SHA of two branches.\n */\nexport async function getMergeBase(\n\ta: string,\n\tb: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"merge-base\", a, b], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Could not find common ancestor between '${a}' and '${b}'.`,\n\t\t);\n\t}\n}\n\n/**\n * Returns the commit SHA at the tip of a branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function getBranchTip(name: string, cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", name], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Returns the subject line of the most recent commit on a branch.\n * @throws {DubError} If the branch has no commits.\n */\nexport async function getLastCommitMessage(\n\tbranch: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"log\", \"-1\", \"--format=%s\", branch],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst message = stdout.trim();\n\t\tif (!message) {\n\t\t\tthrow new DubError(`Branch '${branch}' has no commits.`);\n\t\t}\n\t\treturn message;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to read commit message for '${branch}'.`);\n\t}\n}\n\n/**\n * Pushes a branch to origin with `--force-with-lease`.\n * @throws {DubError} If the push fails.\n */\nexport async function pushBranch(branch: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"push\", \"--force-with-lease\", \"origin\", branch], {\n\t\t\tcwd,\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Failed to push '${branch}'. The remote ref may have been updated by someone else.`,\n\t\t);\n\t}\n}\n\n/**\n * Stages all changes (tracked, untracked, and deletions).\n * @throws {DubError} If git add fails.\n */\nexport async function stageAll(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"add\", \"-A\"], { cwd });\n\t} catch {\n\t\tthrow new DubError(\"Failed to stage changes.\");\n\t}\n}\n\n/**\n * Checks whether there are staged changes ready to commit.\n *\n * `git diff --cached --quiet` exits with code 1 when changes exist\n * and code 0 when there are none.\n */\nexport async function hasStagedChanges(cwd: string): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"diff\", \"--cached\", \"--quiet\"], { cwd });\n\t\treturn false;\n\t} catch (error: unknown) {\n\t\tconst exitCode = (error as { exitCode?: number }).exitCode;\n\t\tif (exitCode === 1) return true;\n\t\tthrow new DubError(\"Failed to check staged changes.\");\n\t}\n}\n\n/**\n * Commits currently staged changes with the given message.\n * @throws {DubError} If the commit fails (e.g., nothing staged, hook rejection).\n */\nexport async function commitStaged(\n\tmessage: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"commit\", \"-m\", message], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Commit failed. Ensure there are staged changes and git hooks pass.`,\n\t\t);\n\t}\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport { getRepoRoot } from \"./git\";\n\n/** A branch within a stack. */\nexport interface Branch {\n\t/** Branch name, e.g. \"feat/api-endpoint\" */\n\tname: string;\n\t/** Set to \"root\" for the base branch (e.g. main). Omitted for children. */\n\ttype?: \"root\";\n\t/** Name of the parent branch. `null` only for root branches. */\n\tparent: string | null;\n\t/** GitHub PR number. Populated after `dub submit`. */\n\tpr_number: number | null;\n\t/** GitHub PR URL. Populated after `dub submit`. */\n\tpr_link: string | null;\n}\n\n/** A stack of dependent branches. */\nexport interface Stack {\n\t/** Unique identifier for this stack. */\n\tid: string;\n\t/** Ordered list of branches in the stack. */\n\tbranches: Branch[];\n}\n\n/** Root state persisted to `.git/dubstack/state.json`. */\nexport interface DubState {\n\t/** All tracked stacks in this repository. */\n\tstacks: Stack[];\n}\n\n/**\n * Returns the absolute path to the dubstack state file.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getStatePath(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\", \"state.json\");\n}\n\n/**\n * Returns the absolute path to the dubstack directory inside `.git`.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getDubDir(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\");\n}\n\n/**\n * Reads and parses the dubstack state file.\n * @throws {DubError} If the state file is missing or contains invalid JSON.\n */\nexport async function readState(cwd: string): Promise<DubState> {\n\tconst statePath = await getStatePath(cwd);\n\tif (!fs.existsSync(statePath)) {\n\t\tthrow new DubError(\"DubStack is not initialized. Run 'dub init' first.\");\n\t}\n\ttry {\n\t\tconst raw = fs.readFileSync(statePath, \"utf-8\");\n\t\treturn JSON.parse(raw) as DubState;\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"State file is corrupted. Delete .git/dubstack and run 'dub init' to re-initialize.\",\n\t\t);\n\t}\n}\n\n/**\n * Writes the dubstack state to disk.\n * Creates the parent directory if it doesn't exist.\n */\nexport async function writeState(state: DubState, cwd: string): Promise<void> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\tif (!fs.existsSync(dir)) {\n\t\tfs.mkdirSync(dir, { recursive: true });\n\t}\n\tfs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\n/**\n * Initializes the dubstack state directory and file.\n * Idempotent — returns `\"already_exists\"` if already initialized.\n *\n * @returns `\"created\"` if freshly initialized, `\"already_exists\"` if state file already present.\n */\nexport async function initState(\n\tcwd: string,\n): Promise<\"created\" | \"already_exists\"> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\n\tif (fs.existsSync(statePath)) {\n\t\treturn \"already_exists\";\n\t}\n\n\tfs.mkdirSync(dir, { recursive: true });\n\tconst emptyState: DubState = { stacks: [] };\n\tfs.writeFileSync(statePath, `${JSON.stringify(emptyState, null, 2)}\\n`);\n\treturn \"created\";\n}\n\n/**\n * Returns state, auto-initializing if not yet set up.\n * Only catches the \"not initialized\" error — corrupt state still throws.\n */\nexport async function ensureState(cwd: string): Promise<DubState> {\n\ttry {\n\t\treturn await readState(cwd);\n\t} catch (error) {\n\t\tif (\n\t\t\terror instanceof DubError &&\n\t\t\terror.message.includes(\"not initialized\")\n\t\t) {\n\t\t\tawait initState(cwd);\n\t\t\treturn await readState(cwd);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Finds the stack containing a given branch.\n * @returns The matching stack, or `undefined` if the branch isn't tracked.\n */\nexport function findStackForBranch(\n\tstate: DubState,\n\tname: string,\n): Stack | undefined {\n\treturn state.stacks.find((stack) =>\n\t\tstack.branches.some((b) => b.name === name),\n\t);\n}\n\n/**\n * Adds a child branch to the state, linking it to its parent.\n *\n * Decision tree:\n * 1. If `child` already exists in any stack → throws `DubError` (no duplicates)\n * 2. If `parent` is found in an existing stack → appends child to that stack\n * 3. If `parent` is not in any stack → creates a new stack with parent as root\n *\n * @param state - The state to mutate (modified in place)\n * @param child - Name of the new branch\n * @param parent - Name of the parent branch\n * @throws {DubError} If child branch already exists in state\n */\nexport function addBranchToStack(\n\tstate: DubState,\n\tchild: string,\n\tparent: string,\n): void {\n\tif (findStackForBranch(state, child)) {\n\t\tthrow new DubError(`Branch '${child}' is already tracked in a stack.`);\n\t}\n\n\tconst childBranch: Branch = {\n\t\tname: child,\n\t\tparent,\n\t\tpr_number: null,\n\t\tpr_link: null,\n\t};\n\tconst existingStack = findStackForBranch(state, parent);\n\n\tif (existingStack) {\n\t\texistingStack.branches.push(childBranch);\n\t} else {\n\t\tconst rootBranch: Branch = {\n\t\t\tname: parent,\n\t\t\ttype: \"root\",\n\t\t\tparent: null,\n\t\t\tpr_number: null,\n\t\t\tpr_link: null,\n\t\t};\n\t\tstate.stacks.push({\n\t\t\tid: crypto.randomUUID(),\n\t\t\tbranches: [rootBranch, childBranch],\n\t\t});\n\t}\n}\n\n/**\n * Returns branches in topological (BFS) order starting from the root.\n * Useful for operations that need to process parent branches before children.\n */\nexport function topologicalOrder(stack: Stack): Branch[] {\n\tconst result: Branch[] = [];\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return result;\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst queue = [root];\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift();\n\t\tif (!current) break;\n\t\tresult.push(current);\n\t\tconst children = childMap.get(current.name) ?? [];\n\t\tqueue.push(...children);\n\t}\n\n\treturn result;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport type { DubState } from \"./state\";\nimport { getDubDir } from \"./state\";\n\n/**\n * Snapshot of system state before a mutation, used by `dub undo`.\n * Only one undo level is supported — each new mutation overwrites the previous snapshot.\n */\nexport interface UndoEntry {\n\t/** Which command created this snapshot. */\n\toperation: \"create\" | \"restack\";\n\t/** ISO timestamp of when the snapshot was taken. */\n\ttimestamp: string;\n\t/** The branch user was on before the operation. */\n\tpreviousBranch: string;\n\t/** Full copy of state.json before mutation. */\n\tpreviousState: DubState;\n\t/** Map of branch name → commit SHA before mutation. */\n\tbranchTips: Record<string, string>;\n\t/** Branches created by this operation (to be deleted on undo). */\n\tcreatedBranches: string[];\n}\n\nasync function getUndoPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"undo.json\");\n}\n\n/**\n * Saves an undo entry to disk. Overwrites any previous entry (1 level only).\n */\nexport async function saveUndoEntry(\n\tentry: UndoEntry,\n\tcwd: string,\n): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tfs.writeFileSync(undoPath, `${JSON.stringify(entry, null, 2)}\\n`);\n}\n\n/**\n * Reads the most recent undo entry.\n * @throws {DubError} If no undo entry exists.\n */\nexport async function readUndoEntry(cwd: string): Promise<UndoEntry> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (!fs.existsSync(undoPath)) {\n\t\tthrow new DubError(\"Nothing to undo.\");\n\t}\n\tconst raw = fs.readFileSync(undoPath, \"utf-8\");\n\treturn JSON.parse(raw) as UndoEntry;\n}\n\n/**\n * Deletes the undo entry file. Called after a successful undo.\n */\nexport async function clearUndoEntry(cwd: string): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (fs.existsSync(undoPath)) {\n\t\tfs.unlinkSync(undoPath);\n\t}\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcommitStaged,\n\tcreateBranch,\n\tgetCurrentBranch,\n\thasStagedChanges,\n\tstageAll,\n} from \"../lib/git\";\nimport { addBranchToStack, ensureState, writeState } from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface CreateOptions {\n\tmessage?: string;\n\tall?: boolean;\n}\n\ninterface CreateResult {\n\tbranch: string;\n\tparent: string;\n\tcommitted?: string;\n}\n\n/**\n * Creates a new branch stacked on top of the current branch.\n *\n * When `-m` is provided, also commits staged changes on the new branch.\n * When `-a` is provided, stages all changes first (requires `-m`).\n *\n * @param name - Name of the new branch to create\n * @param cwd - Working directory (auto-initializes if needed)\n * @param options - Optional message and all flags\n * @returns The created branch name, its parent, and committed message if applicable\n * @throws {DubError} If branch exists, HEAD is detached, -a without -m, or nothing to commit\n */\nexport async function create(\n\tname: string,\n\tcwd: string,\n\toptions?: CreateOptions,\n): Promise<CreateResult> {\n\tif (options?.all && !options.message) {\n\t\tthrow new DubError(\"'-a' requires '-m'. Pass a commit message.\");\n\t}\n\n\tconst state = await ensureState(cwd);\n\tconst parent = await getCurrentBranch(cwd);\n\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\n\tif (options?.message) {\n\t\tif (options.all) {\n\t\t\tawait stageAll(cwd);\n\t\t}\n\n\t\tif (!(await hasStagedChanges(cwd))) {\n\t\t\tconst hint = options.all\n\t\t\t\t? \"No changes to commit.\"\n\t\t\t\t: \"No staged changes. Stage files with 'git add' or use '-a' to stage all.\";\n\t\t\tthrow new DubError(hint);\n\t\t}\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"create\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: parent,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips: {},\n\t\t\tcreatedBranches: [name],\n\t\t},\n\t\tcwd,\n\t);\n\n\tawait createBranch(name, cwd);\n\taddBranchToStack(state, name, parent);\n\tawait writeState(state, cwd);\n\n\tif (options?.message) {\n\t\ttry {\n\t\t\tawait commitStaged(options.message, cwd);\n\t\t} catch (error) {\n\t\t\tconst reason = error instanceof DubError ? error.message : String(error);\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${name}' was created but commit failed: ${reason}. Run 'dub undo' to clean up.`,\n\t\t\t);\n\t\t}\n\t\treturn { branch: name, parent, committed: options.message };\n\t}\n\n\treturn { branch: name, parent };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getRepoRoot, isGitRepo } from \"../lib/git\";\nimport { initState } from \"../lib/state\";\n\ninterface InitResult {\n\tstatus: \"created\" | \"already_exists\";\n\tgitignoreUpdated: boolean;\n}\n\n/**\n * Initializes DubStack in the current git repository.\n *\n * Creates `.git/dubstack/state.json` with an empty state and ensures\n * `.git/dubstack` is listed in `.gitignore`. Idempotent — safe to run\n * multiple times.\n *\n * @param cwd - Working directory (must be inside a git repo)\n * @returns Status indicating whether state was created or already existed\n * @throws {DubError} If not inside a git repository\n */\nexport async function init(cwd: string): Promise<InitResult> {\n\tif (!(await isGitRepo(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n\n\tconst status = await initState(cwd);\n\tconst repoRoot = await getRepoRoot(cwd);\n\tconst gitignorePath = path.join(repoRoot, \".gitignore\");\n\tconst entry = \".git/dubstack\";\n\tlet gitignoreUpdated = false;\n\n\tif (fs.existsSync(gitignorePath)) {\n\t\tconst content = fs.readFileSync(gitignorePath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tif (!lines.some((line) => line.trim() === entry)) {\n\t\t\tconst separator = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n\t\t\tfs.writeFileSync(gitignorePath, `${content}${separator}${entry}\\n`);\n\t\t\tgitignoreUpdated = true;\n\t\t}\n\t} else {\n\t\tfs.writeFileSync(gitignorePath, `${entry}\\n`);\n\t\tgitignoreUpdated = true;\n\t}\n\n\treturn { status, gitignoreUpdated };\n}\n","import { branchExists, getCurrentBranch } from \"../lib/git\";\nimport type { Branch, Stack } from \"../lib/state\";\nimport { readState } from \"../lib/state\";\n\n/**\n * Renders an ASCII tree view of all tracked stacks.\n *\n * Highlights the current branch, marks branches missing from git,\n * and handles multiple stacks separated by blank lines.\n *\n * @param cwd - Working directory (must be inside an initialized dubstack repo)\n * @returns Formatted ASCII tree string (no ANSI colors — caller adds chalk)\n * @throws {DubError} If not initialized\n */\nexport async function log(cwd: string): Promise<string> {\n\tconst state = await readState(cwd);\n\n\tif (state.stacks.length === 0) {\n\t\treturn \"No stacks. Run 'dub create' to start.\";\n\t}\n\n\tlet currentBranch: string | null = null;\n\ttry {\n\t\tcurrentBranch = await getCurrentBranch(cwd);\n\t} catch {\n\t\t// Detached HEAD or empty repo — no branch highlighted\n\t}\n\n\tconst sections: string[] = [];\n\n\tfor (const stack of state.stacks) {\n\t\tconst tree = await renderStack(stack, currentBranch, cwd);\n\t\tsections.push(tree);\n\t}\n\n\treturn sections.join(\"\\n\\n\");\n}\n\nasync function renderStack(\n\tstack: Stack,\n\tcurrentBranch: string | null,\n\tcwd: string,\n): Promise<string> {\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return \"\";\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\tawait renderNode(root, currentBranch, childMap, \"\", true, true, lines, cwd);\n\treturn lines.join(\"\\n\");\n}\n\nasync function renderNode(\n\tbranch: Branch,\n\tcurrentBranch: string | null,\n\tchildMap: Map<string, Branch[]>,\n\tprefix: string,\n\tisRoot: boolean,\n\tisLast: boolean,\n\tlines: string[],\n\tcwd: string,\n): Promise<void> {\n\tlet label: string;\n\tconst exists = await branchExists(branch.name, cwd);\n\n\tif (isRoot) {\n\t\tlabel = `(${branch.name})`;\n\t} else if (branch.name === currentBranch) {\n\t\tlabel = `*${branch.name} (Current)*`;\n\t} else if (!exists) {\n\t\tlabel = `${branch.name} ⚠ (missing)`;\n\t} else {\n\t\tlabel = branch.name;\n\t}\n\n\tif (isRoot) {\n\t\tlines.push(label);\n\t} else {\n\t\tconst connector = isLast ? \"└─ \" : \"├─ \";\n\t\tlines.push(`${prefix}${connector}${label}`);\n\t}\n\n\tconst children = childMap.get(branch.name) ?? [];\n\tconst childPrefix = isRoot ? \" \" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\n\tfor (let i = 0; i < children.length; i++) {\n\t\tconst isChildLast = i === children.length - 1;\n\t\tawait renderNode(\n\t\t\tchildren[i],\n\t\t\tcurrentBranch,\n\t\t\tchildMap,\n\t\t\tchildPrefix,\n\t\t\tfalse,\n\t\t\tisChildLast,\n\t\t\tlines,\n\t\t\tcwd,\n\t\t);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcheckoutBranch,\n\tgetBranchTip,\n\tgetCurrentBranch,\n\tgetMergeBase,\n\trebaseContinue as gitRebaseContinue,\n\tisWorkingTreeClean,\n\trebaseOnto,\n} from \"../lib/git\";\nimport {\n\tgetDubDir,\n\treadState,\n\ttype Stack,\n\ttopologicalOrder,\n} from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface RestackStep {\n\tbranch: string;\n\tparent: string;\n\tparentOldTip: string;\n\tstatus: \"pending\" | \"done\" | \"skipped\" | \"conflicted\";\n}\n\ninterface RestackProgress {\n\toriginalBranch: string;\n\tsteps: RestackStep[];\n}\n\ninterface RestackResult {\n\tstatus: \"success\" | \"conflict\" | \"up-to-date\";\n\trebased: string[];\n\tconflictBranch?: string;\n}\n\n/**\n * Rebases all branches in the current stack onto their updated parents.\n *\n * Uses a snapshot-before-rebase strategy: captures every branch's tip\n * BEFORE starting any rebases, then uses `git rebase --onto <parent_new_tip>\n * <parent_old_tip> <child>`. This prevents the duplication bug where a child\n * replays its parent's already-rebased commits.\n *\n * On conflict, writes progress to `restack-progress.json` so\n * `dub restack --continue` can resume.\n *\n * @param cwd - Working directory\n * @returns Result with status, list of rebased branches, and optional conflict branch\n * @throws {DubError} If not initialized, dirty tree, not in a stack, or branch missing\n */\nexport async function restack(cwd: string): Promise<RestackResult> {\n\tconst state = await readState(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before restacking.\",\n\t\t);\n\t}\n\n\tconst originalBranch = await getCurrentBranch(cwd);\n\tconst targetStacks = getTargetStacks(state.stacks, originalBranch);\n\n\tif (targetStacks.length === 0) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${originalBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst allBranches = targetStacks.flatMap((s) => s.branches);\n\tfor (const branch of allBranches) {\n\t\tif (!(await branchExists(branch.name, cwd))) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${branch.name}' is tracked in state but no longer exists in git.\\n` +\n\t\t\t\t\t\" Remove it from the stack or recreate it before restacking.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Snapshot all branch tips BEFORE building steps or rebasing\n\tconst branchTips: Record<string, string> = {};\n\tfor (const branch of allBranches) {\n\t\tbranchTips[branch.name] = await getBranchTip(branch.name, cwd);\n\t}\n\n\tconst steps = await buildRestackSteps(targetStacks, cwd);\n\n\tif (steps.length === 0) {\n\t\treturn { status: \"up-to-date\", rebased: [] };\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"restack\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: originalBranch,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips,\n\t\t\tcreatedBranches: [],\n\t\t},\n\t\tcwd,\n\t);\n\n\tconst progress: RestackProgress = { originalBranch, steps };\n\tawait writeProgress(progress, cwd);\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\n/**\n * Continues a restack after conflict resolution.\n *\n * Reads the saved progress file, finishes the in-progress rebase,\n * then resumes with remaining branches.\n *\n * @param cwd - Working directory\n * @throws {DubError} If no restack is in progress\n */\nexport async function restackContinue(cwd: string): Promise<RestackResult> {\n\tconst progress = await readProgress(cwd);\n\n\tif (!progress) {\n\t\tthrow new DubError(\"No restack in progress. Run 'dub restack' to start.\");\n\t}\n\n\tawait gitRebaseContinue(cwd);\n\n\tconst conflictedStep = progress.steps.find((s) => s.status === \"conflicted\");\n\tif (conflictedStep) {\n\t\tconflictedStep.status = \"done\";\n\t}\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\nasync function executeRestackSteps(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<RestackResult> {\n\tconst rebased: string[] = [];\n\n\tfor (const step of progress.steps) {\n\t\tif (step.status !== \"pending\") {\n\t\t\tif (step.status === \"done\") rebased.push(step.branch);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst parentNewTip = await getBranchTip(step.parent, cwd);\n\t\tif (parentNewTip === step.parentOldTip) {\n\t\t\tstep.status = \"skipped\";\n\t\t\tawait writeProgress(progress, cwd);\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tawait rebaseOnto(parentNewTip, step.parentOldTip, step.branch, cwd);\n\t\t\tstep.status = \"done\";\n\t\t\trebased.push(step.branch);\n\t\t\tawait writeProgress(progress, cwd);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DubError && error.message.includes(\"Conflict\")) {\n\t\t\t\tstep.status = \"conflicted\";\n\t\t\t\tawait writeProgress(progress, cwd);\n\t\t\t\treturn { status: \"conflict\", rebased, conflictBranch: step.branch };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tawait clearProgress(cwd);\n\tawait checkoutBranch(progress.originalBranch, cwd);\n\n\tconst allSkipped = progress.steps.every(\n\t\t(s) => s.status === \"skipped\" || s.status === \"done\",\n\t);\n\treturn {\n\t\tstatus: rebased.length === 0 && allSkipped ? \"up-to-date\" : \"success\",\n\t\trebased,\n\t};\n}\n\nfunction getTargetStacks(stacks: Stack[], currentBranch: string): Stack[] {\n\t// If current branch is a root of any stacks, restack all of them\n\tconst rootStacks = stacks.filter((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch && b.type === \"root\"),\n\t);\n\tif (rootStacks.length > 0) return rootStacks;\n\n\t// Otherwise, find the stack containing the current branch\n\tconst stack = stacks.find((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch),\n\t);\n\treturn stack ? [stack] : [];\n}\n\nasync function buildRestackSteps(\n\tstacks: Stack[],\n\tcwd: string,\n): Promise<RestackStep[]> {\n\tconst steps: RestackStep[] = [];\n\n\tfor (const stack of stacks) {\n\t\tconst ordered = topologicalOrder(stack);\n\t\tfor (const branch of ordered) {\n\t\t\tif (branch.type === \"root\" || !branch.parent) continue;\n\t\t\tconst mergeBase = await getMergeBase(branch.parent, branch.name, cwd);\n\t\t\tsteps.push({\n\t\t\t\tbranch: branch.name,\n\t\t\t\tparent: branch.parent,\n\t\t\t\tparentOldTip: mergeBase,\n\t\t\t\tstatus: \"pending\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn steps;\n}\n\nasync function getProgressPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"restack-progress.json\");\n}\n\nasync function writeProgress(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tfs.writeFileSync(progressPath, `${JSON.stringify(progress, null, 2)}\\n`);\n}\n\nasync function readProgress(cwd: string): Promise<RestackProgress | null> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (!fs.existsSync(progressPath)) return null;\n\tconst raw = fs.readFileSync(progressPath, \"utf-8\");\n\treturn JSON.parse(raw) as RestackProgress;\n}\n\nasync function clearProgress(cwd: string): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (fs.existsSync(progressPath)) {\n\t\tfs.unlinkSync(progressPath);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getCurrentBranch, getLastCommitMessage, pushBranch } from \"../lib/git\";\nimport {\n\tcheckGhAuth,\n\tcreatePr,\n\tensureGhInstalled,\n\tgetPr,\n\ttype PrInfo,\n\tupdatePrBody,\n} from \"../lib/github\";\nimport {\n\tbuildMetadataBlock,\n\tbuildStackTable,\n\tcomposePrBody,\n} from \"../lib/pr-body\";\nimport {\n\ttype Branch,\n\tfindStackForBranch,\n\treadState,\n\ttopologicalOrder,\n\twriteState,\n} from \"../lib/state\";\n\ninterface SubmitResult {\n\tpushed: string[];\n\tcreated: string[];\n\tupdated: string[];\n}\n\n/**\n * Pushes branches in the current stack and creates/updates GitHub PRs.\n *\n * @param cwd - Working directory\n * @param dryRun - If true, prints what would happen without executing\n * @throws {DubError} If not in a stack, on root branch, stack is non-linear, or gh errors\n */\nexport async function submit(\n\tcwd: string,\n\tdryRun: boolean,\n): Promise<SubmitResult> {\n\tawait ensureGhInstalled();\n\tawait checkGhAuth();\n\n\tconst state = await readState(cwd);\n\tconst currentBranch = await getCurrentBranch(cwd);\n\tconst stack = findStackForBranch(state, currentBranch);\n\n\tif (!stack) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${currentBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst ordered = topologicalOrder(stack);\n\tconst currentEntry = ordered.find((b) => b.name === currentBranch);\n\tif (currentEntry?.type === \"root\") {\n\t\tthrow new DubError(\n\t\t\t\"Cannot submit from a root branch. Checkout a stack branch first.\",\n\t\t);\n\t}\n\n\tconst nonRootBranches = ordered.filter((b) => b.type !== \"root\");\n\n\tvalidateLinearStack(ordered);\n\n\tconst result: SubmitResult = { pushed: [], created: [], updated: [] };\n\tconst prMap = new Map<string, PrInfo>();\n\n\tfor (const branch of nonRootBranches) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would push ${branch.name}`);\n\t\t} else {\n\t\t\tawait pushBranch(branch.name, cwd);\n\t\t}\n\t\tresult.pushed.push(branch.name);\n\t}\n\n\tfor (const branch of nonRootBranches) {\n\t\tconst base = branch.parent as string;\n\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would check/create PR: ${branch.name} → ${base}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst existing = await getPr(branch.name, cwd);\n\t\tif (existing) {\n\t\t\tprMap.set(branch.name, existing);\n\t\t\tresult.updated.push(branch.name);\n\t\t} else {\n\t\t\tconst title = await getLastCommitMessage(branch.name, cwd);\n\t\t\tconst tmpFile = writeTempBody(\"\");\n\t\t\ttry {\n\t\t\t\tconst created = await createPr(branch.name, base, title, tmpFile, cwd);\n\t\t\t\tprMap.set(branch.name, created);\n\t\t\t\tresult.created.push(branch.name);\n\t\t\t} finally {\n\t\t\t\tcleanupTempFile(tmpFile);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!dryRun) {\n\t\tawait updateAllPrBodies(nonRootBranches, prMap, stack.id, cwd);\n\n\t\tfor (const branch of nonRootBranches) {\n\t\t\tconst pr = prMap.get(branch.name);\n\t\t\tif (pr) {\n\t\t\t\tconst stateBranch = stack.branches.find((b) => b.name === branch.name);\n\t\t\t\tif (stateBranch) {\n\t\t\t\t\tstateBranch.pr_number = pr.number;\n\t\t\t\t\tstateBranch.pr_link = pr.url;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tawait writeState(state, cwd);\n\t}\n\n\treturn result;\n}\n\nfunction validateLinearStack(ordered: Branch[]): void {\n\tconst childCount = new Map<string, number>();\n\tfor (const branch of ordered) {\n\t\tif (branch.parent) {\n\t\t\tchildCount.set(branch.parent, (childCount.get(branch.parent) ?? 0) + 1);\n\t\t}\n\t}\n\tfor (const [parent, count] of childCount) {\n\t\tif (count > 1) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${parent}' has ${count} children. ` +\n\t\t\t\t\t\"Branching stacks are not supported by submit. \" +\n\t\t\t\t\t\"Ensure each branch has at most one child.\",\n\t\t\t);\n\t\t}\n\t}\n}\n\nasync function updateAllPrBodies(\n\tbranches: Branch[],\n\tprMap: Map<string, PrInfo>,\n\tstackId: string,\n\tcwd: string,\n): Promise<void> {\n\tconst tableEntries = new Map<string, { number: number; title: string }>();\n\tfor (const branch of branches) {\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (pr) {\n\t\t\ttableEntries.set(branch.name, { number: pr.number, title: pr.title });\n\t\t}\n\t}\n\n\tfor (let i = 0; i < branches.length; i++) {\n\t\tconst branch = branches[i];\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (!pr) continue;\n\n\t\tconst prevPr =\n\t\t\ti > 0 ? (prMap.get(branches[i - 1].name)?.number ?? null) : null;\n\t\tconst nextPr =\n\t\t\ti < branches.length - 1\n\t\t\t\t? (prMap.get(branches[i + 1].name)?.number ?? null)\n\t\t\t\t: null;\n\n\t\tconst stackTable = buildStackTable(branches, tableEntries, branch.name);\n\t\tconst metadataBlock = buildMetadataBlock(\n\t\t\tstackId,\n\t\t\tpr.number,\n\t\t\tprevPr,\n\t\t\tnextPr,\n\t\t\tbranch.name,\n\t\t);\n\n\t\tconst existingBody = pr.body;\n\t\tconst finalBody = composePrBody(existingBody, stackTable, metadataBlock);\n\n\t\tconst tmpFile = writeTempBody(finalBody);\n\t\ttry {\n\t\t\tawait updatePrBody(pr.number, tmpFile, cwd);\n\t\t} finally {\n\t\t\tcleanupTempFile(tmpFile);\n\t\t}\n\t}\n}\n\nfunction writeTempBody(content: string): string {\n\tconst tmpDir = os.tmpdir();\n\tconst tmpFile = path.join(tmpDir, `dubstack-body-${Date.now()}.md`);\n\tfs.writeFileSync(tmpFile, content);\n\treturn tmpFile;\n}\n\nfunction cleanupTempFile(filePath: string): void {\n\ttry {\n\t\tfs.unlinkSync(filePath);\n\t} catch {\n\t\t// Best-effort cleanup\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/** Details of a GitHub Pull Request. */\nexport interface PrInfo {\n\tnumber: number;\n\turl: string;\n\ttitle: string;\n\tbody: string;\n}\n\n/**\n * Ensures the `gh` CLI is installed and available in PATH.\n * @throws {DubError} If `gh` is not found.\n */\nexport async function ensureGhInstalled(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"--version\"]);\n\t} catch {\n\t\tthrow new DubError(\"gh CLI not found. Install it: https://cli.github.com\");\n\t}\n}\n\n/**\n * Ensures the user is authenticated with `gh`.\n * @throws {DubError} If not authenticated.\n */\nexport async function checkGhAuth(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"auth\", \"status\"]);\n\t} catch {\n\t\tthrow new DubError(\"Not authenticated with GitHub. Run 'gh auth login'.\");\n\t}\n}\n\n/**\n * Fetches the open PR for a given head branch, if one exists.\n * @returns The PR info, or `null` if no open PR exists for that branch.\n */\nexport async function getPr(\n\tbranch: string,\n\tcwd: string,\n): Promise<PrInfo | null> {\n\tconst { stdout } = await execa(\n\t\t\"gh\",\n\t\t[\n\t\t\t\"pr\",\n\t\t\t\"list\",\n\t\t\t\"--head\",\n\t\t\tbranch,\n\t\t\t\"--state\",\n\t\t\t\"open\",\n\t\t\t\"--json\",\n\t\t\t\"number,url,title,body\",\n\t\t\t\"--jq\",\n\t\t\t\".[0]\",\n\t\t],\n\t\t{ cwd },\n\t);\n\n\tconst trimmed = stdout.trim();\n\tif (!trimmed || trimmed === \"null\") return null;\n\n\ttry {\n\t\treturn JSON.parse(trimmed) as PrInfo;\n\t} catch {\n\t\tthrow new DubError(`Failed to parse PR info for branch '${branch}'.`);\n\t}\n}\n\n/**\n * Creates a new PR and returns its info.\n *\n * Parses the PR number from the URL printed to stdout by `gh pr create`,\n * avoiding an extra API round-trip.\n *\n * @param branch - Head branch\n * @param base - Base branch the PR merges into\n * @param title - PR title\n * @param bodyFile - Absolute path to a file containing the PR body\n */\nexport async function createPr(\n\tbranch: string,\n\tbase: string,\n\ttitle: string,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<PrInfo> {\n\tlet stdout: string;\n\ttry {\n\t\tconst result = await execa(\n\t\t\t\"gh\",\n\t\t\t[\n\t\t\t\t\"pr\",\n\t\t\t\t\"create\",\n\t\t\t\t\"--head\",\n\t\t\t\tbranch,\n\t\t\t\t\"--base\",\n\t\t\t\tbase,\n\t\t\t\t\"--title\",\n\t\t\t\ttitle,\n\t\t\t\t\"--body-file\",\n\t\t\t\tbodyFile,\n\t\t\t],\n\t\t\t{ cwd },\n\t\t);\n\t\tstdout = result.stdout;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to create PR for '${branch}': ${message}`);\n\t}\n\n\tconst url = stdout.trim();\n\tconst numberMatch = url.match(/\\/pull\\/(\\d+)$/);\n\tif (!numberMatch) {\n\t\tthrow new DubError(`Unexpected output from 'gh pr create': ${url}`);\n\t}\n\n\treturn {\n\t\tnumber: Number.parseInt(numberMatch[1], 10),\n\t\turl,\n\t\ttitle,\n\t\tbody: \"\",\n\t};\n}\n\n/**\n * Updates a PR's body using a file.\n * @param prNumber - The PR number to update\n * @param bodyFile - Absolute path to a file containing the new body\n */\nexport async function updatePrBody(\n\tprNumber: number,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\n\t\t\t\"gh\",\n\t\t\t[\"pr\", \"edit\", String(prNumber), \"--body-file\", bodyFile],\n\t\t\t{ cwd },\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to update PR #${prNumber}: ${message}`);\n\t}\n}\n","import type { Branch } from \"./state\";\n\ninterface StackEntry {\n\tnumber: number;\n\ttitle: string;\n}\n\nconst DUBSTACK_START = \"<!-- dubstack:start -->\";\nconst DUBSTACK_END = \"<!-- dubstack:end -->\";\nconst METADATA_START = \"<!-- dubstack-metadata\";\nconst METADATA_END = \"-->\";\n\n/**\n * Builds the visible stack navigation table wrapped in dubstack markers.\n *\n * @param orderedBranches - Non-root branches in topological order\n * @param prMap - Map of branch name → PR number + title\n * @param currentBranch - The branch to mark with 👈\n */\nexport function buildStackTable(\n\torderedBranches: Branch[],\n\tprMap: Map<string, StackEntry>,\n\tcurrentBranch: string,\n): string {\n\tconst lines = orderedBranches.map((branch) => {\n\t\tconst entry = prMap.get(branch.name);\n\t\tif (!entry) return `- ${branch.name}`;\n\t\tconst marker = branch.name === currentBranch ? \" 👈\" : \"\";\n\t\treturn `- #${entry.number} ${entry.title}${marker}`;\n\t});\n\n\treturn [\n\t\tDUBSTACK_START,\n\t\t\"---\",\n\t\t\"### 🥞 DubStack\",\n\t\t...lines,\n\t\tDUBSTACK_END,\n\t].join(\"\\n\");\n}\n\n/**\n * Builds the hidden metadata HTML comment block.\n */\nexport function buildMetadataBlock(\n\tstackId: string,\n\tprNumber: number,\n\tprevPr: number | null,\n\tnextPr: number | null,\n\tbranch: string,\n): string {\n\tconst metadata = {\n\t\tstack_id: stackId,\n\t\tpr_number: prNumber,\n\t\tprev_pr: prevPr,\n\t\tnext_pr: nextPr,\n\t\tbranch,\n\t};\n\treturn `${METADATA_START}\\n${JSON.stringify(metadata, null, 2)}\\n${METADATA_END}`;\n}\n\n/**\n * Strips all DubStack-generated sections from a PR body.\n * Preserves user-written content. Returns body unchanged if no markers exist.\n */\nexport function stripDubstackSections(body: string): string {\n\tlet result = body;\n\n\tconst startIdx = result.indexOf(DUBSTACK_START);\n\tconst endIdx = result.indexOf(DUBSTACK_END);\n\tif (startIdx !== -1 && endIdx !== -1) {\n\t\tresult =\n\t\t\tresult.slice(0, startIdx) + result.slice(endIdx + DUBSTACK_END.length);\n\t}\n\n\tconst metaStart = result.indexOf(METADATA_START);\n\tif (metaStart !== -1) {\n\t\tconst metaEnd = result.indexOf(\n\t\t\tMETADATA_END,\n\t\t\tmetaStart + METADATA_START.length,\n\t\t);\n\t\tif (metaEnd !== -1) {\n\t\t\tresult =\n\t\t\t\tresult.slice(0, metaStart) +\n\t\t\t\tresult.slice(metaEnd + METADATA_END.length);\n\t\t}\n\t}\n\n\treturn result.trimEnd();\n}\n\n/**\n * Composes the final PR body by combining user content with DubStack sections.\n *\n * @param existingBody - The existing PR body (may contain stale DubStack sections)\n * @param stackTable - Output of `buildStackTable`\n * @param metadataBlock - Output of `buildMetadataBlock`\n */\nexport function composePrBody(\n\texistingBody: string,\n\tstackTable: string,\n\tmetadataBlock: string,\n): string {\n\tconst userContent = stripDubstackSections(existingBody);\n\tconst parts = [userContent, stackTable, metadataBlock].filter(Boolean);\n\treturn parts.join(\"\\n\\n\");\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tcheckoutBranch,\n\tdeleteBranch,\n\tforceBranchTo,\n\tgetCurrentBranch,\n\tisWorkingTreeClean,\n} from \"../lib/git\";\nimport { writeState } from \"../lib/state\";\nimport { clearUndoEntry, readUndoEntry } from \"../lib/undo-log\";\n\ninterface UndoResult {\n\tundone: \"create\" | \"restack\";\n\tdetails: string;\n}\n\n/**\n * Undoes the last `dub create` or `dub restack` operation.\n *\n * Reversal strategy:\n * - **create**: Deletes the created branch, restores state, checks out the previous branch.\n * - **restack**: Resets every rebased branch to its pre-rebase tip via `git branch -f`,\n * restores state, checks out the previous branch.\n *\n * Only one level of undo is supported. After undo, the undo entry is cleared.\n *\n * @param cwd - Working directory\n * @returns What was undone and a human-readable summary\n * @throws {DubError} If nothing to undo or working tree is dirty\n */\nexport async function undo(cwd: string): Promise<UndoResult> {\n\tconst entry = await readUndoEntry(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before undoing.\",\n\t\t);\n\t}\n\n\tconst currentBranch = await getCurrentBranch(cwd);\n\n\tif (entry.operation === \"create\") {\n\t\t// If we're on a branch that's about to be deleted, switch away first\n\t\tconst needsCheckout = entry.createdBranches.includes(currentBranch);\n\t\tif (needsCheckout) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tfor (const branch of entry.createdBranches) {\n\t\t\tawait deleteBranch(branch, cwd);\n\t\t}\n\n\t\tif (!needsCheckout && currentBranch !== entry.previousBranch) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tawait writeState(entry.previousState, cwd);\n\t\tawait clearUndoEntry(cwd);\n\n\t\treturn {\n\t\t\tundone: \"create\",\n\t\t\tdetails: `Deleted branch${entry.createdBranches.length > 1 ? \"es\" : \"\"} '${entry.createdBranches.join(\"', '\")}'`,\n\t\t};\n\t}\n\n\t// restack undo: reset all branches to their pre-rebase tips\n\t// First checkout a safe branch so we don't conflict with force-moves\n\tawait checkoutBranch(entry.previousBranch, cwd);\n\n\tfor (const [name, sha] of Object.entries(entry.branchTips)) {\n\t\tif (name === entry.previousBranch) continue; // skip the branch we're on\n\t\tawait forceBranchTo(name, sha, cwd);\n\t}\n\n\t// Now force the branch we're on (if it was tracked)\n\tif (entry.branchTips[entry.previousBranch]) {\n\t\tawait forceBranchTo(\n\t\t\tentry.previousBranch,\n\t\t\tentry.branchTips[entry.previousBranch],\n\t\t\tcwd,\n\t\t);\n\t}\n\n\tawait writeState(entry.previousState, cwd);\n\tawait clearUndoEntry(cwd);\n\n\treturn {\n\t\tundone: \"restack\",\n\t\tdetails: `Reset ${Object.keys(entry.branchTips).length} branches to pre-restack state`,\n\t};\n}\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","fs","path","execa","execa","require"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/checkout.ts","../src/lib/errors.ts","../src/lib/git.ts","../src/lib/state.ts","../src/lib/undo-log.ts","../src/commands/create.ts","../src/commands/init.ts","../src/commands/log.ts","../src/commands/restack.ts","../src/commands/submit.ts","../src/lib/github.ts","../src/lib/pr-body.ts","../src/commands/undo.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * DubStack CLI — manage stacked diffs with ease.\n *\n * A local-first tool for managing chains of dependent git branches\n * (stacked diffs) without manually dealing with complex rebase chains.\n *\n * @example\n * ```bash\n * dub init # Initialize in current repo\n * dub create feat/my-branch # Create stacked branch\n * dub log # View stack tree\n * dub restack # Rebase stack onto updated parent\n * dub undo # Undo last dub operation\n * ```\n *\n * @packageDocumentation\n */\n\nimport { createRequire } from \"node:module\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { checkout, interactiveCheckout } from \"./commands/checkout\";\nimport { create } from \"./commands/create\";\nimport { init } from \"./commands/init\";\nimport { log } from \"./commands/log\";\nimport { restack, restackContinue } from \"./commands/restack\";\nimport { submit } from \"./commands/submit\";\nimport { undo } from \"./commands/undo\";\nimport { DubError } from \"./lib/errors\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n\t.name(\"dub\")\n\t.description(\"Manage stacked diffs (dependent git branches) with ease\")\n\t.version(version);\n\nprogram\n\t.command(\"init\")\n\t.description(\"Initialize DubStack in the current git repository\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub init Initialize DubStack, creating .git/dubstack/ and updating .gitignore`,\n\t)\n\t.action(async () => {\n\t\tconst result = await init(process.cwd());\n\t\tif (result.status === \"created\") {\n\t\t\tconsole.log(chalk.green(\"✔ DubStack initialized\"));\n\t\t} else {\n\t\t\tconsole.log(chalk.yellow(\"⚠ DubStack already initialized\"));\n\t\t}\n\t});\n\nprogram\n\t.command(\"create\")\n\t.argument(\"<branch-name>\", \"Name of the new branch to create\")\n\t.description(\"Create a new branch stacked on top of the current branch\")\n\t.option(\"-m, --message <message>\", \"Commit staged changes with this message\")\n\t.option(\"-a, --all\", \"Stage all changes before committing (requires -m)\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub create feat/api Create branch only\n $ dub create feat/api -m \"feat: add API\" Create branch + commit staged\n $ dub create feat/api -am \"feat: add API\" Stage all + create + commit`,\n\t)\n\t.action(\n\t\tasync (\n\t\t\tbranchName: string,\n\t\t\toptions: { message?: string; all?: boolean },\n\t\t) => {\n\t\t\tconst result = await create(branchName, process.cwd(), {\n\t\t\t\tmessage: options.message,\n\t\t\t\tall: options.all,\n\t\t\t});\n\t\t\tif (result.committed) {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created '${result.branch}' on '${result.parent}' • ${result.committed}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green(\n\t\t\t\t\t\t`✔ Created branch '${result.branch}' on top of '${result.parent}'`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n\nprogram\n\t.command(\"log\")\n\t.description(\"Display an ASCII tree of the current stack\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub log Show the branch tree with current branch highlighted`,\n\t)\n\t.action(async () => {\n\t\tconst output = await log(process.cwd());\n\t\t// Apply chalk styling to the output\n\t\tconst styled = output\n\t\t\t.replace(/\\*(.+?) \\(Current\\)\\*/g, chalk.bold.cyan(\"$1 (Current)\"))\n\t\t\t.replace(/⚠ \\(missing\\)/g, chalk.yellow(\"⚠ (missing)\"));\n\t\tconsole.log(styled);\n\t});\n\nprogram\n\t.command(\"restack\")\n\t.description(\"Rebase all branches in the stack onto their updated parents\")\n\t.option(\"--continue\", \"Continue restacking after resolving conflicts\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub restack Rebase the current stack\n $ dub restack --continue Continue after resolving conflicts`,\n\t)\n\t.action(async (options: { continue?: boolean }) => {\n\t\tconst result = options.continue\n\t\t\t? await restackContinue(process.cwd())\n\t\t\t: await restack(process.cwd());\n\n\t\tif (result.status === \"up-to-date\") {\n\t\t\tconsole.log(chalk.green(\"✔ Stack is already up to date\"));\n\t\t} else if (result.status === \"conflict\") {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(`⚠ Conflict while restacking '${result.conflictBranch}'`),\n\t\t\t);\n\t\t\tconsole.log(\n\t\t\t\tchalk.dim(\n\t\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconsole.log(\n\t\t\t\tchalk.green(`✔ Restacked ${result.rebased.length} branch(es)`),\n\t\t\t);\n\t\t\tfor (const branch of result.rebased) {\n\t\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t\t}\n\t\t}\n\t});\n\nprogram\n\t.command(\"undo\")\n\t.description(\"Undo the last dub create or dub restack operation\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub undo Roll back the last dub operation`,\n\t)\n\t.action(async () => {\n\t\tconst result = await undo(process.cwd());\n\t\tconsole.log(chalk.green(`✔ Undid '${result.undone}': ${result.details}`));\n\t});\n\nprogram\n\t.command(\"submit\")\n\t.description(\n\t\t\"Push branches and create/update GitHub PRs for the current stack\",\n\t)\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.addHelpText(\n\t\t\"after\",\n\t\t`\nExamples:\n $ dub submit Push and create/update PRs\n $ dub submit --dry-run Preview what would happen`,\n\t)\n\t.action(runSubmit);\n\nprogram\n\t.command(\"ss\")\n\t.description(\"Submit the current stack (alias for submit)\")\n\t.option(\"--dry-run\", \"Print what would happen without executing\")\n\t.action(runSubmit);\n\nprogram\n\t.command(\"co\")\n\t.argument(\"[branch]\", \"Branch to checkout (interactive if omitted)\")\n\t.description(\"Checkout a branch (interactive picker if no name given)\")\n\t.action(async (branch?: string) => {\n\t\tif (branch) {\n\t\t\tconst result = await checkout(branch, process.cwd());\n\t\t\tconsole.log(chalk.green(`✔ Switched to '${result.branch}'`));\n\t\t} else {\n\t\t\tconst result = await interactiveCheckout(process.cwd());\n\t\t\tif (result) {\n\t\t\t\tconsole.log(chalk.green(`✔ Switched to '${result.branch}'`));\n\t\t\t}\n\t\t}\n\t});\n\nasync function runSubmit(options: { dryRun?: boolean }) {\n\tconst result = await submit(process.cwd(), options.dryRun ?? false);\n\n\tif (result.pushed.length > 0) {\n\t\tconsole.log(\n\t\t\tchalk.green(\n\t\t\t\t`✔ Pushed ${result.pushed.length} branch(es), created ${result.created.length} PR(s), updated ${result.updated.length} PR(s)`,\n\t\t\t),\n\t\t);\n\t\tfor (const branch of [...result.created, ...result.updated]) {\n\t\t\tconsole.log(chalk.dim(` ↳ ${branch}`));\n\t\t}\n\t}\n}\n\nasync function main() {\n\ttry {\n\t\tawait program.parseAsync(process.argv);\n\t} catch (error) {\n\t\tif (error instanceof DubError) {\n\t\t\tconsole.error(chalk.red(`✖ ${error.message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nmain();\n","import search from \"@inquirer/search\";\nimport { DubError } from \"../lib/errors\";\nimport { checkoutBranch, getCurrentBranch, listBranches } from \"../lib/git\";\nimport { type DubState, readState } from \"../lib/state\";\n\n/**\n * Returns a sorted, deduplicated list of branch names tracked by DubStack.\n * Root branches that appear in multiple stacks are included only once.\n */\nexport function getTrackedBranches(state: DubState): string[] {\n\tconst names = new Set<string>();\n\tfor (const stack of state.stacks) {\n\t\tfor (const branch of stack.branches) {\n\t\t\tnames.add(branch.name);\n\t\t}\n\t}\n\treturn [...names].sort();\n}\n\n/**\n * Filters tracked branches against the list of actual local git branches.\n * Removes any branches that are tracked in state but have been deleted locally.\n */\nexport function getValidBranches(tracked: string[], local: string[]): string[] {\n\tconst localSet = new Set(local);\n\treturn tracked.filter((b) => localSet.has(b));\n}\n\n/**\n * Checks out the named branch.\n *\n * @param name - Branch to switch to\n * @param cwd - Working directory\n * @returns The checked-out branch name\n * @throws {DubError} If the branch does not exist\n */\nexport async function checkout(\n\tname: string,\n\tcwd: string,\n): Promise<{ branch: string }> {\n\tawait checkoutBranch(name, cwd);\n\treturn { branch: name };\n}\n\n/**\n * Launches an interactive search prompt listing DubStack-tracked branches.\n *\n * The current branch is shown but disabled. The user can type to filter,\n * use arrow keys to navigate, and press Enter to checkout.\n *\n * @param cwd - Working directory\n * @returns The checked-out branch, or `null` if the user cancelled (Ctrl+C)\n * @throws {DubError} If not initialized or no tracked branches exist\n */\nexport async function interactiveCheckout(\n\tcwd: string,\n): Promise<{ branch: string } | null> {\n\tconst state = await readState(cwd);\n\tconst trackedBranches = getTrackedBranches(state);\n\tconst localBranches = await listBranches(cwd);\n\n\tconst validBranches = getValidBranches(trackedBranches, localBranches);\n\n\tif (validBranches.length === 0) {\n\t\tthrow new DubError(\n\t\t\t\"No valid tracked branches found. Run 'dub create' first.\",\n\t\t);\n\t}\n\n\tlet currentBranch: string | null = null;\n\ttry {\n\t\tcurrentBranch = await getCurrentBranch(cwd);\n\t} catch {\n\t\t// Detached HEAD — no branch marked as current\n\t}\n\n\t// Setup AbortController for Esc key support\n\tconst controller = new AbortController();\n\n\t// Listen for keypress events to handle Esc\n\tconst onKeypress = (_str: string, key: { name: string; ctrl: boolean }) => {\n\t\tif (key && key.name === \"escape\") {\n\t\t\tcontroller.abort();\n\t\t}\n\t};\n\tprocess.stdin.on(\"keypress\", onKeypress);\n\n\ttry {\n\t\tconst selected = await search(\n\t\t\t{\n\t\t\t\tmessage: \"Checkout a branch (autocomplete or arrow keys)\",\n\t\t\t\tsource(term: string | undefined) {\n\t\t\t\t\tconst filtered = term\n\t\t\t\t\t\t? validBranches.filter((b) =>\n\t\t\t\t\t\t\t\tb.toLowerCase().includes(term.toLowerCase()),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: validBranches;\n\n\t\t\t\t\treturn filtered.map((name) => ({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tvalue: name,\n\t\t\t\t\t\tdisabled: name === currentBranch ? \"(current)\" : false,\n\t\t\t\t\t}));\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ signal: controller.signal },\n\t\t);\n\n\t\treturn checkout(selected, cwd);\n\t} catch (error: unknown) {\n\t\tif (error instanceof Error) {\n\t\t\tif (\n\t\t\t\terror.name === \"ExitPromptError\" ||\n\t\t\t\terror.name === \"AbortError\" ||\n\t\t\t\terror.name === \"AbortPromptError\"\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tprocess.stdin.off(\"keypress\", onKeypress);\n\t}\n}\n","/**\n * Base error class for all user-facing DubStack errors.\n *\n * The CLI entry point catches instances of this class and prints\n * a clean, colored error message. Unknown errors get a full stack trace.\n *\n * @example\n * ```ts\n * throw new DubError(\"Branch 'feat/x' already exists\")\n * ```\n */\nexport class DubError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"DubError\";\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/**\n * Checks whether the given directory is inside a git repository.\n * @returns `true` if inside a git worktree, `false` otherwise. Never throws.\n */\nexport async function isGitRepo(cwd: string): Promise<boolean> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--is-inside-work-tree\"],\n\t\t\t{ cwd },\n\t\t);\n\t\treturn stdout.trim() === \"true\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Returns the absolute path to the repository root.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getRepoRoot(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n\t\t\tcwd,\n\t\t});\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the name of the currently checked-out branch.\n * @throws {DubError} If HEAD is detached or the repo has no commits.\n */\nexport async function getCurrentBranch(cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"rev-parse\", \"--abbrev-ref\", \"HEAD\"],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst branch = stdout.trim();\n\t\tif (branch === \"HEAD\") {\n\t\t\tthrow new DubError(\n\t\t\t\t\"HEAD is detached. Checkout a branch before running this command.\",\n\t\t\t);\n\t\t}\n\t\treturn branch;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(\n\t\t\t\"Repository has no commits. Make at least one commit first.\",\n\t\t);\n\t}\n}\n\n/**\n * Checks whether a branch with the given name exists locally.\n * @returns `true` if the branch exists, `false` otherwise. Never throws.\n */\nexport async function branchExists(\n\tname: string,\n\tcwd: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"rev-parse\", \"--verify\", `refs/heads/${name}`], {\n\t\t\tcwd,\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a new branch and switches to it.\n * @throws {DubError} If a branch with that name already exists.\n */\nexport async function createBranch(name: string, cwd: string): Promise<void> {\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\tawait execa(\"git\", [\"checkout\", \"-b\", name], { cwd });\n}\n\n/**\n * Switches to an existing branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function checkoutBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"checkout\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Deletes a local branch forcefully. Used by undo to remove created branches.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function deleteBranch(name: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"branch\", \"-D\", name], { cwd });\n\t} catch {\n\t\tthrow new DubError(`Failed to delete branch '${name}'. It may not exist.`);\n\t}\n}\n\n/**\n * Force-moves a branch pointer to a specific commit SHA.\n * Used by undo to reset branches to their pre-operation tips.\n */\nexport async function forceBranchTo(\n\tname: string,\n\tsha: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tconst current = await getCurrentBranch(cwd).catch(() => null);\n\t\tif (current === name) {\n\t\t\tawait execa(\"git\", [\"reset\", \"--hard\", sha], { cwd });\n\t\t} else {\n\t\t\tawait execa(\"git\", [\"branch\", \"-f\", name, sha], { cwd });\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to reset branch '${name}' to ${sha}.`);\n\t}\n}\n\n/**\n * Checks whether the working tree is clean (no uncommitted changes).\n * @returns `true` if clean (no output from `git status --porcelain`).\n */\nexport async function isWorkingTreeClean(cwd: string): Promise<boolean> {\n\tconst { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n\treturn stdout.trim() === \"\";\n}\n\n/**\n * Performs `git rebase --onto` to move a branch from one base to another.\n *\n * @param newBase - The commit/branch to rebase onto\n * @param oldBase - The old base commit to replay from\n * @param branch - The branch being rebased\n * @throws {DubError} If a merge conflict occurs during rebase\n */\nexport async function rebaseOnto(\n\tnewBase: string,\n\toldBase: string,\n\tbranch: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--onto\", newBase, oldBase, branch], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Conflict while restacking '${branch}'.\\n` +\n\t\t\t\t\" Resolve conflicts, stage changes, then run: dub restack --continue\",\n\t\t);\n\t}\n}\n\n/**\n * Continues an in-progress rebase after conflicts have been resolved.\n * @throws {DubError} If the rebase continue fails.\n */\nexport async function rebaseContinue(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"rebase\", \"--continue\"], {\n\t\t\tcwd,\n\t\t\tenv: { GIT_EDITOR: \"true\" },\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"Failed to continue rebase. Ensure all conflicts are resolved and staged.\",\n\t\t);\n\t}\n}\n\n/**\n * Returns the merge-base (common ancestor) commit SHA of two branches.\n */\nexport async function getMergeBase(\n\ta: string,\n\tb: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"merge-base\", a, b], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Could not find common ancestor between '${a}' and '${b}'.`,\n\t\t);\n\t}\n}\n\n/**\n * Returns the commit SHA at the tip of a branch.\n * @throws {DubError} If the branch does not exist.\n */\nexport async function getBranchTip(name: string, cwd: string): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\"git\", [\"rev-parse\", name], { cwd });\n\t\treturn stdout.trim();\n\t} catch {\n\t\tthrow new DubError(`Branch '${name}' not found.`);\n\t}\n}\n\n/**\n * Returns the subject line of the most recent commit on a branch.\n * @throws {DubError} If the branch has no commits.\n */\nexport async function getLastCommitMessage(\n\tbranch: string,\n\tcwd: string,\n): Promise<string> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"log\", \"-1\", \"--format=%s\", branch],\n\t\t\t{ cwd },\n\t\t);\n\t\tconst message = stdout.trim();\n\t\tif (!message) {\n\t\t\tthrow new DubError(`Branch '${branch}' has no commits.`);\n\t\t}\n\t\treturn message;\n\t} catch (error) {\n\t\tif (error instanceof DubError) throw error;\n\t\tthrow new DubError(`Failed to read commit message for '${branch}'.`);\n\t}\n}\n\n/**\n * Pushes a branch to origin with `--force-with-lease`.\n * @throws {DubError} If the push fails.\n */\nexport async function pushBranch(branch: string, cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"push\", \"--force-with-lease\", \"origin\", branch], {\n\t\t\tcwd,\n\t\t});\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Failed to push '${branch}'. The remote ref may have been updated by someone else.`,\n\t\t);\n\t}\n}\n\n/**\n * Stages all changes (tracked, untracked, and deletions).\n * @throws {DubError} If git add fails.\n */\nexport async function stageAll(cwd: string): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"add\", \"-A\"], { cwd });\n\t} catch {\n\t\tthrow new DubError(\"Failed to stage changes.\");\n\t}\n}\n\n/**\n * Checks whether there are staged changes ready to commit.\n *\n * `git diff --cached --quiet` exits with code 1 when changes exist\n * and code 0 when there are none.\n */\nexport async function hasStagedChanges(cwd: string): Promise<boolean> {\n\ttry {\n\t\tawait execa(\"git\", [\"diff\", \"--cached\", \"--quiet\"], { cwd });\n\t\treturn false;\n\t} catch (error: unknown) {\n\t\tconst exitCode = (error as { exitCode?: number }).exitCode;\n\t\tif (exitCode === 1) return true;\n\t\tthrow new DubError(\"Failed to check staged changes.\");\n\t}\n}\n\n/**\n * Commits currently staged changes with the given message.\n * @throws {DubError} If the commit fails (e.g., nothing staged, hook rejection).\n */\nexport async function commitStaged(\n\tmessage: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\"git\", [\"commit\", \"-m\", message], { cwd });\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t`Commit failed. Ensure there are staged changes and git hooks pass.`,\n\t\t);\n\t}\n}\n\n/**\n * Returns a list of all local branch names.\n */\nexport async function listBranches(cwd: string): Promise<string[]> {\n\ttry {\n\t\tconst { stdout } = await execa(\n\t\t\t\"git\",\n\t\t\t[\"branch\", \"--format=%(refname:short)\"],\n\t\t\t{ cwd },\n\t\t);\n\t\treturn stdout.trim().split(\"\\n\").filter(Boolean);\n\t} catch {\n\t\tthrow new DubError(\"Failed to list branches.\");\n\t}\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport { getRepoRoot } from \"./git\";\n\n/** A branch within a stack. */\nexport interface Branch {\n\t/** Branch name, e.g. \"feat/api-endpoint\" */\n\tname: string;\n\t/** Set to \"root\" for the base branch (e.g. main). Omitted for children. */\n\ttype?: \"root\";\n\t/** Name of the parent branch. `null` only for root branches. */\n\tparent: string | null;\n\t/** GitHub PR number. Populated after `dub submit`. */\n\tpr_number: number | null;\n\t/** GitHub PR URL. Populated after `dub submit`. */\n\tpr_link: string | null;\n}\n\n/** A stack of dependent branches. */\nexport interface Stack {\n\t/** Unique identifier for this stack. */\n\tid: string;\n\t/** Ordered list of branches in the stack. */\n\tbranches: Branch[];\n}\n\n/** Root state persisted to `.git/dubstack/state.json`. */\nexport interface DubState {\n\t/** All tracked stacks in this repository. */\n\tstacks: Stack[];\n}\n\n/**\n * Returns the absolute path to the dubstack state file.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getStatePath(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\", \"state.json\");\n}\n\n/**\n * Returns the absolute path to the dubstack directory inside `.git`.\n * @throws {DubError} If not inside a git repository.\n */\nexport async function getDubDir(cwd: string): Promise<string> {\n\tconst root = await getRepoRoot(cwd);\n\treturn path.join(root, \".git\", \"dubstack\");\n}\n\n/**\n * Reads and parses the dubstack state file.\n * @throws {DubError} If the state file is missing or contains invalid JSON.\n */\nexport async function readState(cwd: string): Promise<DubState> {\n\tconst statePath = await getStatePath(cwd);\n\tif (!fs.existsSync(statePath)) {\n\t\tthrow new DubError(\"DubStack is not initialized. Run 'dub init' first.\");\n\t}\n\ttry {\n\t\tconst raw = fs.readFileSync(statePath, \"utf-8\");\n\t\treturn JSON.parse(raw) as DubState;\n\t} catch {\n\t\tthrow new DubError(\n\t\t\t\"State file is corrupted. Delete .git/dubstack and run 'dub init' to re-initialize.\",\n\t\t);\n\t}\n}\n\n/**\n * Writes the dubstack state to disk.\n * Creates the parent directory if it doesn't exist.\n */\nexport async function writeState(state: DubState, cwd: string): Promise<void> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\tif (!fs.existsSync(dir)) {\n\t\tfs.mkdirSync(dir, { recursive: true });\n\t}\n\tfs.writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\n/**\n * Initializes the dubstack state directory and file.\n * Idempotent — returns `\"already_exists\"` if already initialized.\n *\n * @returns `\"created\"` if freshly initialized, `\"already_exists\"` if state file already present.\n */\nexport async function initState(\n\tcwd: string,\n): Promise<\"created\" | \"already_exists\"> {\n\tconst statePath = await getStatePath(cwd);\n\tconst dir = path.dirname(statePath);\n\n\tif (fs.existsSync(statePath)) {\n\t\treturn \"already_exists\";\n\t}\n\n\tfs.mkdirSync(dir, { recursive: true });\n\tconst emptyState: DubState = { stacks: [] };\n\tfs.writeFileSync(statePath, `${JSON.stringify(emptyState, null, 2)}\\n`);\n\treturn \"created\";\n}\n\n/**\n * Returns state, auto-initializing if not yet set up.\n * Only catches the \"not initialized\" error — corrupt state still throws.\n */\nexport async function ensureState(cwd: string): Promise<DubState> {\n\ttry {\n\t\treturn await readState(cwd);\n\t} catch (error) {\n\t\tif (\n\t\t\terror instanceof DubError &&\n\t\t\terror.message.includes(\"not initialized\")\n\t\t) {\n\t\t\tawait initState(cwd);\n\t\t\treturn await readState(cwd);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Finds the stack containing a given branch.\n * @returns The matching stack, or `undefined` if the branch isn't tracked.\n */\nexport function findStackForBranch(\n\tstate: DubState,\n\tname: string,\n): Stack | undefined {\n\treturn state.stacks.find((stack) =>\n\t\tstack.branches.some((b) => b.name === name),\n\t);\n}\n\n/**\n * Adds a child branch to the state, linking it to its parent.\n *\n * Decision tree:\n * 1. If `child` already exists in any stack → throws `DubError` (no duplicates)\n * 2. If `parent` is found in an existing stack → appends child to that stack\n * 3. If `parent` is not in any stack → creates a new stack with parent as root\n *\n * @param state - The state to mutate (modified in place)\n * @param child - Name of the new branch\n * @param parent - Name of the parent branch\n * @throws {DubError} If child branch already exists in state\n */\nexport function addBranchToStack(\n\tstate: DubState,\n\tchild: string,\n\tparent: string,\n): void {\n\tif (findStackForBranch(state, child)) {\n\t\tthrow new DubError(`Branch '${child}' is already tracked in a stack.`);\n\t}\n\n\tconst childBranch: Branch = {\n\t\tname: child,\n\t\tparent,\n\t\tpr_number: null,\n\t\tpr_link: null,\n\t};\n\tconst existingStack = findStackForBranch(state, parent);\n\n\tif (existingStack) {\n\t\texistingStack.branches.push(childBranch);\n\t} else {\n\t\tconst rootBranch: Branch = {\n\t\t\tname: parent,\n\t\t\ttype: \"root\",\n\t\t\tparent: null,\n\t\t\tpr_number: null,\n\t\t\tpr_link: null,\n\t\t};\n\t\tstate.stacks.push({\n\t\t\tid: crypto.randomUUID(),\n\t\t\tbranches: [rootBranch, childBranch],\n\t\t});\n\t}\n}\n\n/**\n * Returns branches in topological (BFS) order starting from the root.\n * Useful for operations that need to process parent branches before children.\n */\nexport function topologicalOrder(stack: Stack): Branch[] {\n\tconst result: Branch[] = [];\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return result;\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst queue = [root];\n\twhile (queue.length > 0) {\n\t\tconst current = queue.shift();\n\t\tif (!current) break;\n\t\tresult.push(current);\n\t\tconst children = childMap.get(current.name) ?? [];\n\t\tqueue.push(...children);\n\t}\n\n\treturn result;\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"./errors\";\nimport type { DubState } from \"./state\";\nimport { getDubDir } from \"./state\";\n\n/**\n * Snapshot of system state before a mutation, used by `dub undo`.\n * Only one undo level is supported — each new mutation overwrites the previous snapshot.\n */\nexport interface UndoEntry {\n\t/** Which command created this snapshot. */\n\toperation: \"create\" | \"restack\";\n\t/** ISO timestamp of when the snapshot was taken. */\n\ttimestamp: string;\n\t/** The branch user was on before the operation. */\n\tpreviousBranch: string;\n\t/** Full copy of state.json before mutation. */\n\tpreviousState: DubState;\n\t/** Map of branch name → commit SHA before mutation. */\n\tbranchTips: Record<string, string>;\n\t/** Branches created by this operation (to be deleted on undo). */\n\tcreatedBranches: string[];\n}\n\nasync function getUndoPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"undo.json\");\n}\n\n/**\n * Saves an undo entry to disk. Overwrites any previous entry (1 level only).\n */\nexport async function saveUndoEntry(\n\tentry: UndoEntry,\n\tcwd: string,\n): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tfs.writeFileSync(undoPath, `${JSON.stringify(entry, null, 2)}\\n`);\n}\n\n/**\n * Reads the most recent undo entry.\n * @throws {DubError} If no undo entry exists.\n */\nexport async function readUndoEntry(cwd: string): Promise<UndoEntry> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (!fs.existsSync(undoPath)) {\n\t\tthrow new DubError(\"Nothing to undo.\");\n\t}\n\tconst raw = fs.readFileSync(undoPath, \"utf-8\");\n\treturn JSON.parse(raw) as UndoEntry;\n}\n\n/**\n * Deletes the undo entry file. Called after a successful undo.\n */\nexport async function clearUndoEntry(cwd: string): Promise<void> {\n\tconst undoPath = await getUndoPath(cwd);\n\tif (fs.existsSync(undoPath)) {\n\t\tfs.unlinkSync(undoPath);\n\t}\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcommitStaged,\n\tcreateBranch,\n\tgetCurrentBranch,\n\thasStagedChanges,\n\tstageAll,\n} from \"../lib/git\";\nimport { addBranchToStack, ensureState, writeState } from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface CreateOptions {\n\tmessage?: string;\n\tall?: boolean;\n}\n\ninterface CreateResult {\n\tbranch: string;\n\tparent: string;\n\tcommitted?: string;\n}\n\n/**\n * Creates a new branch stacked on top of the current branch.\n *\n * When `-m` is provided, also commits staged changes on the new branch.\n * When `-a` is provided, stages all changes first (requires `-m`).\n *\n * @param name - Name of the new branch to create\n * @param cwd - Working directory (auto-initializes if needed)\n * @param options - Optional message and all flags\n * @returns The created branch name, its parent, and committed message if applicable\n * @throws {DubError} If branch exists, HEAD is detached, -a without -m, or nothing to commit\n */\nexport async function create(\n\tname: string,\n\tcwd: string,\n\toptions?: CreateOptions,\n): Promise<CreateResult> {\n\tif (options?.all && !options.message) {\n\t\tthrow new DubError(\"'-a' requires '-m'. Pass a commit message.\");\n\t}\n\n\tconst state = await ensureState(cwd);\n\tconst parent = await getCurrentBranch(cwd);\n\n\tif (await branchExists(name, cwd)) {\n\t\tthrow new DubError(`Branch '${name}' already exists.`);\n\t}\n\n\tif (options?.message) {\n\t\tif (options.all) {\n\t\t\tawait stageAll(cwd);\n\t\t}\n\n\t\tif (!(await hasStagedChanges(cwd))) {\n\t\t\tconst hint = options.all\n\t\t\t\t? \"No changes to commit.\"\n\t\t\t\t: \"No staged changes. Stage files with 'git add' or use '-a' to stage all.\";\n\t\t\tthrow new DubError(hint);\n\t\t}\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"create\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: parent,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips: {},\n\t\t\tcreatedBranches: [name],\n\t\t},\n\t\tcwd,\n\t);\n\n\tawait createBranch(name, cwd);\n\taddBranchToStack(state, name, parent);\n\tawait writeState(state, cwd);\n\n\tif (options?.message) {\n\t\ttry {\n\t\t\tawait commitStaged(options.message, cwd);\n\t\t} catch (error) {\n\t\t\tconst reason = error instanceof DubError ? error.message : String(error);\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${name}' was created but commit failed: ${reason}. Run 'dub undo' to clean up.`,\n\t\t\t);\n\t\t}\n\t\treturn { branch: name, parent, committed: options.message };\n\t}\n\n\treturn { branch: name, parent };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getRepoRoot, isGitRepo } from \"../lib/git\";\nimport { initState } from \"../lib/state\";\n\ninterface InitResult {\n\tstatus: \"created\" | \"already_exists\";\n\tgitignoreUpdated: boolean;\n}\n\n/**\n * Initializes DubStack in the current git repository.\n *\n * Creates `.git/dubstack/state.json` with an empty state and ensures\n * `.git/dubstack` is listed in `.gitignore`. Idempotent — safe to run\n * multiple times.\n *\n * @param cwd - Working directory (must be inside a git repo)\n * @returns Status indicating whether state was created or already existed\n * @throws {DubError} If not inside a git repository\n */\nexport async function init(cwd: string): Promise<InitResult> {\n\tif (!(await isGitRepo(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Not a git repository. Run this command inside a git repo.\",\n\t\t);\n\t}\n\n\tconst status = await initState(cwd);\n\tconst repoRoot = await getRepoRoot(cwd);\n\tconst gitignorePath = path.join(repoRoot, \".gitignore\");\n\tconst entry = \".git/dubstack\";\n\tlet gitignoreUpdated = false;\n\n\tif (fs.existsSync(gitignorePath)) {\n\t\tconst content = fs.readFileSync(gitignorePath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tif (!lines.some((line) => line.trim() === entry)) {\n\t\t\tconst separator = content.endsWith(\"\\n\") ? \"\" : \"\\n\";\n\t\t\tfs.writeFileSync(gitignorePath, `${content}${separator}${entry}\\n`);\n\t\t\tgitignoreUpdated = true;\n\t\t}\n\t} else {\n\t\tfs.writeFileSync(gitignorePath, `${entry}\\n`);\n\t\tgitignoreUpdated = true;\n\t}\n\n\treturn { status, gitignoreUpdated };\n}\n","import { branchExists, getCurrentBranch } from \"../lib/git\";\nimport type { Branch, Stack } from \"../lib/state\";\nimport { readState } from \"../lib/state\";\n\n/**\n * Renders an ASCII tree view of all tracked stacks.\n *\n * Highlights the current branch, marks branches missing from git,\n * and handles multiple stacks separated by blank lines.\n *\n * @param cwd - Working directory (must be inside an initialized dubstack repo)\n * @returns Formatted ASCII tree string (no ANSI colors — caller adds chalk)\n * @throws {DubError} If not initialized\n */\nexport async function log(cwd: string): Promise<string> {\n\tconst state = await readState(cwd);\n\n\tif (state.stacks.length === 0) {\n\t\treturn \"No stacks. Run 'dub create' to start.\";\n\t}\n\n\tlet currentBranch: string | null = null;\n\ttry {\n\t\tcurrentBranch = await getCurrentBranch(cwd);\n\t} catch {\n\t\t// Detached HEAD or empty repo — no branch highlighted\n\t}\n\n\tconst sections: string[] = [];\n\n\tfor (const stack of state.stacks) {\n\t\tconst tree = await renderStack(stack, currentBranch, cwd);\n\t\tsections.push(tree);\n\t}\n\n\treturn sections.join(\"\\n\\n\");\n}\n\nasync function renderStack(\n\tstack: Stack,\n\tcurrentBranch: string | null,\n\tcwd: string,\n): Promise<string> {\n\tconst root = stack.branches.find((b) => b.type === \"root\");\n\tif (!root) return \"\";\n\n\tconst childMap = new Map<string, Branch[]>();\n\tfor (const branch of stack.branches) {\n\t\tif (branch.parent) {\n\t\t\tconst children = childMap.get(branch.parent) ?? [];\n\t\t\tchildren.push(branch);\n\t\t\tchildMap.set(branch.parent, children);\n\t\t}\n\t}\n\n\tconst lines: string[] = [];\n\tawait renderNode(root, currentBranch, childMap, \"\", true, true, lines, cwd);\n\treturn lines.join(\"\\n\");\n}\n\nasync function renderNode(\n\tbranch: Branch,\n\tcurrentBranch: string | null,\n\tchildMap: Map<string, Branch[]>,\n\tprefix: string,\n\tisRoot: boolean,\n\tisLast: boolean,\n\tlines: string[],\n\tcwd: string,\n): Promise<void> {\n\tlet label: string;\n\tconst exists = await branchExists(branch.name, cwd);\n\n\tif (isRoot) {\n\t\tlabel = `(${branch.name})`;\n\t} else if (branch.name === currentBranch) {\n\t\tlabel = `*${branch.name} (Current)*`;\n\t} else if (!exists) {\n\t\tlabel = `${branch.name} ⚠ (missing)`;\n\t} else {\n\t\tlabel = branch.name;\n\t}\n\n\tif (isRoot) {\n\t\tlines.push(label);\n\t} else {\n\t\tconst connector = isLast ? \"└─ \" : \"├─ \";\n\t\tlines.push(`${prefix}${connector}${label}`);\n\t}\n\n\tconst children = childMap.get(branch.name) ?? [];\n\tconst childPrefix = isRoot ? \" \" : `${prefix}${isLast ? \" \" : \"│ \"}`;\n\n\tfor (let i = 0; i < children.length; i++) {\n\t\tconst isChildLast = i === children.length - 1;\n\t\tawait renderNode(\n\t\t\tchildren[i],\n\t\t\tcurrentBranch,\n\t\t\tchildMap,\n\t\t\tchildPrefix,\n\t\t\tfalse,\n\t\t\tisChildLast,\n\t\t\tlines,\n\t\t\tcwd,\n\t\t);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport {\n\tbranchExists,\n\tcheckoutBranch,\n\tgetBranchTip,\n\tgetCurrentBranch,\n\tgetMergeBase,\n\trebaseContinue as gitRebaseContinue,\n\tisWorkingTreeClean,\n\trebaseOnto,\n} from \"../lib/git\";\nimport {\n\tgetDubDir,\n\treadState,\n\ttype Stack,\n\ttopologicalOrder,\n} from \"../lib/state\";\nimport { saveUndoEntry } from \"../lib/undo-log\";\n\ninterface RestackStep {\n\tbranch: string;\n\tparent: string;\n\tparentOldTip: string;\n\tstatus: \"pending\" | \"done\" | \"skipped\" | \"conflicted\";\n}\n\ninterface RestackProgress {\n\toriginalBranch: string;\n\tsteps: RestackStep[];\n}\n\ninterface RestackResult {\n\tstatus: \"success\" | \"conflict\" | \"up-to-date\";\n\trebased: string[];\n\tconflictBranch?: string;\n}\n\n/**\n * Rebases all branches in the current stack onto their updated parents.\n *\n * Uses a snapshot-before-rebase strategy: captures every branch's tip\n * BEFORE starting any rebases, then uses `git rebase --onto <parent_new_tip>\n * <parent_old_tip> <child>`. This prevents the duplication bug where a child\n * replays its parent's already-rebased commits.\n *\n * On conflict, writes progress to `restack-progress.json` so\n * `dub restack --continue` can resume.\n *\n * @param cwd - Working directory\n * @returns Result with status, list of rebased branches, and optional conflict branch\n * @throws {DubError} If not initialized, dirty tree, not in a stack, or branch missing\n */\nexport async function restack(cwd: string): Promise<RestackResult> {\n\tconst state = await readState(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before restacking.\",\n\t\t);\n\t}\n\n\tconst originalBranch = await getCurrentBranch(cwd);\n\tconst targetStacks = getTargetStacks(state.stacks, originalBranch);\n\n\tif (targetStacks.length === 0) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${originalBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst allBranches = targetStacks.flatMap((s) => s.branches);\n\tfor (const branch of allBranches) {\n\t\tif (!(await branchExists(branch.name, cwd))) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${branch.name}' is tracked in state but no longer exists in git.\\n` +\n\t\t\t\t\t\" Remove it from the stack or recreate it before restacking.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Snapshot all branch tips BEFORE building steps or rebasing\n\tconst branchTips: Record<string, string> = {};\n\tfor (const branch of allBranches) {\n\t\tbranchTips[branch.name] = await getBranchTip(branch.name, cwd);\n\t}\n\n\tconst steps = await buildRestackSteps(targetStacks, cwd);\n\n\tif (steps.length === 0) {\n\t\treturn { status: \"up-to-date\", rebased: [] };\n\t}\n\n\tawait saveUndoEntry(\n\t\t{\n\t\t\toperation: \"restack\",\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpreviousBranch: originalBranch,\n\t\t\tpreviousState: structuredClone(state),\n\t\t\tbranchTips,\n\t\t\tcreatedBranches: [],\n\t\t},\n\t\tcwd,\n\t);\n\n\tconst progress: RestackProgress = { originalBranch, steps };\n\tawait writeProgress(progress, cwd);\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\n/**\n * Continues a restack after conflict resolution.\n *\n * Reads the saved progress file, finishes the in-progress rebase,\n * then resumes with remaining branches.\n *\n * @param cwd - Working directory\n * @throws {DubError} If no restack is in progress\n */\nexport async function restackContinue(cwd: string): Promise<RestackResult> {\n\tconst progress = await readProgress(cwd);\n\n\tif (!progress) {\n\t\tthrow new DubError(\"No restack in progress. Run 'dub restack' to start.\");\n\t}\n\n\tawait gitRebaseContinue(cwd);\n\n\tconst conflictedStep = progress.steps.find((s) => s.status === \"conflicted\");\n\tif (conflictedStep) {\n\t\tconflictedStep.status = \"done\";\n\t}\n\n\treturn executeRestackSteps(progress, cwd);\n}\n\nasync function executeRestackSteps(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<RestackResult> {\n\tconst rebased: string[] = [];\n\n\tfor (const step of progress.steps) {\n\t\tif (step.status !== \"pending\") {\n\t\t\tif (step.status === \"done\") rebased.push(step.branch);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst parentNewTip = await getBranchTip(step.parent, cwd);\n\t\tif (parentNewTip === step.parentOldTip) {\n\t\t\tstep.status = \"skipped\";\n\t\t\tawait writeProgress(progress, cwd);\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tawait rebaseOnto(parentNewTip, step.parentOldTip, step.branch, cwd);\n\t\t\tstep.status = \"done\";\n\t\t\trebased.push(step.branch);\n\t\t\tawait writeProgress(progress, cwd);\n\t\t} catch (error) {\n\t\t\tif (error instanceof DubError && error.message.includes(\"Conflict\")) {\n\t\t\t\tstep.status = \"conflicted\";\n\t\t\t\tawait writeProgress(progress, cwd);\n\t\t\t\treturn { status: \"conflict\", rebased, conflictBranch: step.branch };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tawait clearProgress(cwd);\n\tawait checkoutBranch(progress.originalBranch, cwd);\n\n\tconst allSkipped = progress.steps.every(\n\t\t(s) => s.status === \"skipped\" || s.status === \"done\",\n\t);\n\treturn {\n\t\tstatus: rebased.length === 0 && allSkipped ? \"up-to-date\" : \"success\",\n\t\trebased,\n\t};\n}\n\nfunction getTargetStacks(stacks: Stack[], currentBranch: string): Stack[] {\n\t// If current branch is a root of any stacks, restack all of them\n\tconst rootStacks = stacks.filter((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch && b.type === \"root\"),\n\t);\n\tif (rootStacks.length > 0) return rootStacks;\n\n\t// Otherwise, find the stack containing the current branch\n\tconst stack = stacks.find((s) =>\n\t\ts.branches.some((b) => b.name === currentBranch),\n\t);\n\treturn stack ? [stack] : [];\n}\n\nasync function buildRestackSteps(\n\tstacks: Stack[],\n\tcwd: string,\n): Promise<RestackStep[]> {\n\tconst steps: RestackStep[] = [];\n\n\tfor (const stack of stacks) {\n\t\tconst ordered = topologicalOrder(stack);\n\t\tfor (const branch of ordered) {\n\t\t\tif (branch.type === \"root\" || !branch.parent) continue;\n\t\t\tconst mergeBase = await getMergeBase(branch.parent, branch.name, cwd);\n\t\t\tsteps.push({\n\t\t\t\tbranch: branch.name,\n\t\t\t\tparent: branch.parent,\n\t\t\t\tparentOldTip: mergeBase,\n\t\t\t\tstatus: \"pending\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn steps;\n}\n\nasync function getProgressPath(cwd: string): Promise<string> {\n\tconst dubDir = await getDubDir(cwd);\n\treturn path.join(dubDir, \"restack-progress.json\");\n}\n\nasync function writeProgress(\n\tprogress: RestackProgress,\n\tcwd: string,\n): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tfs.writeFileSync(progressPath, `${JSON.stringify(progress, null, 2)}\\n`);\n}\n\nasync function readProgress(cwd: string): Promise<RestackProgress | null> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (!fs.existsSync(progressPath)) return null;\n\tconst raw = fs.readFileSync(progressPath, \"utf-8\");\n\treturn JSON.parse(raw) as RestackProgress;\n}\n\nasync function clearProgress(cwd: string): Promise<void> {\n\tconst progressPath = await getProgressPath(cwd);\n\tif (fs.existsSync(progressPath)) {\n\t\tfs.unlinkSync(progressPath);\n\t}\n}\n","import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { DubError } from \"../lib/errors\";\nimport { getCurrentBranch, getLastCommitMessage, pushBranch } from \"../lib/git\";\nimport {\n\tcheckGhAuth,\n\tcreatePr,\n\tensureGhInstalled,\n\tgetPr,\n\ttype PrInfo,\n\tupdatePrBody,\n} from \"../lib/github\";\nimport {\n\tbuildMetadataBlock,\n\tbuildStackTable,\n\tcomposePrBody,\n} from \"../lib/pr-body\";\nimport {\n\ttype Branch,\n\tfindStackForBranch,\n\treadState,\n\ttopologicalOrder,\n\twriteState,\n} from \"../lib/state\";\n\ninterface SubmitResult {\n\tpushed: string[];\n\tcreated: string[];\n\tupdated: string[];\n}\n\n/**\n * Pushes branches in the current stack and creates/updates GitHub PRs.\n *\n * @param cwd - Working directory\n * @param dryRun - If true, prints what would happen without executing\n * @throws {DubError} If not in a stack, on root branch, stack is non-linear, or gh errors\n */\nexport async function submit(\n\tcwd: string,\n\tdryRun: boolean,\n): Promise<SubmitResult> {\n\tawait ensureGhInstalled();\n\tawait checkGhAuth();\n\n\tconst state = await readState(cwd);\n\tconst currentBranch = await getCurrentBranch(cwd);\n\tconst stack = findStackForBranch(state, currentBranch);\n\n\tif (!stack) {\n\t\tthrow new DubError(\n\t\t\t`Branch '${currentBranch}' is not part of any stack. Run 'dub create' first.`,\n\t\t);\n\t}\n\n\tconst ordered = topologicalOrder(stack);\n\tconst currentEntry = ordered.find((b) => b.name === currentBranch);\n\tif (currentEntry?.type === \"root\") {\n\t\tthrow new DubError(\n\t\t\t\"Cannot submit from a root branch. Checkout a stack branch first.\",\n\t\t);\n\t}\n\n\tconst nonRootBranches = ordered.filter((b) => b.type !== \"root\");\n\n\tvalidateLinearStack(ordered);\n\n\tconst result: SubmitResult = { pushed: [], created: [], updated: [] };\n\tconst prMap = new Map<string, PrInfo>();\n\n\tfor (const branch of nonRootBranches) {\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would push ${branch.name}`);\n\t\t} else {\n\t\t\tawait pushBranch(branch.name, cwd);\n\t\t}\n\t\tresult.pushed.push(branch.name);\n\t}\n\n\tfor (const branch of nonRootBranches) {\n\t\tconst base = branch.parent as string;\n\n\t\tif (dryRun) {\n\t\t\tconsole.log(`[dry-run] would check/create PR: ${branch.name} → ${base}`);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst existing = await getPr(branch.name, cwd);\n\t\tif (existing) {\n\t\t\tprMap.set(branch.name, existing);\n\t\t\tresult.updated.push(branch.name);\n\t\t} else {\n\t\t\tconst title = await getLastCommitMessage(branch.name, cwd);\n\t\t\tconst tmpFile = writeTempBody(\"\");\n\t\t\ttry {\n\t\t\t\tconst created = await createPr(branch.name, base, title, tmpFile, cwd);\n\t\t\t\tprMap.set(branch.name, created);\n\t\t\t\tresult.created.push(branch.name);\n\t\t\t} finally {\n\t\t\t\tcleanupTempFile(tmpFile);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!dryRun) {\n\t\tawait updateAllPrBodies(nonRootBranches, prMap, stack.id, cwd);\n\n\t\tfor (const branch of nonRootBranches) {\n\t\t\tconst pr = prMap.get(branch.name);\n\t\t\tif (pr) {\n\t\t\t\tconst stateBranch = stack.branches.find((b) => b.name === branch.name);\n\t\t\t\tif (stateBranch) {\n\t\t\t\t\tstateBranch.pr_number = pr.number;\n\t\t\t\t\tstateBranch.pr_link = pr.url;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tawait writeState(state, cwd);\n\t}\n\n\treturn result;\n}\n\nfunction validateLinearStack(ordered: Branch[]): void {\n\tconst childCount = new Map<string, number>();\n\tfor (const branch of ordered) {\n\t\tif (branch.parent) {\n\t\t\tchildCount.set(branch.parent, (childCount.get(branch.parent) ?? 0) + 1);\n\t\t}\n\t}\n\tfor (const [parent, count] of childCount) {\n\t\tif (count > 1) {\n\t\t\tthrow new DubError(\n\t\t\t\t`Branch '${parent}' has ${count} children. ` +\n\t\t\t\t\t\"Branching stacks are not supported by submit. \" +\n\t\t\t\t\t\"Ensure each branch has at most one child.\",\n\t\t\t);\n\t\t}\n\t}\n}\n\nasync function updateAllPrBodies(\n\tbranches: Branch[],\n\tprMap: Map<string, PrInfo>,\n\tstackId: string,\n\tcwd: string,\n): Promise<void> {\n\tconst tableEntries = new Map<string, { number: number; title: string }>();\n\tfor (const branch of branches) {\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (pr) {\n\t\t\ttableEntries.set(branch.name, { number: pr.number, title: pr.title });\n\t\t}\n\t}\n\n\tfor (let i = 0; i < branches.length; i++) {\n\t\tconst branch = branches[i];\n\t\tconst pr = prMap.get(branch.name);\n\t\tif (!pr) continue;\n\n\t\tconst prevPr =\n\t\t\ti > 0 ? (prMap.get(branches[i - 1].name)?.number ?? null) : null;\n\t\tconst nextPr =\n\t\t\ti < branches.length - 1\n\t\t\t\t? (prMap.get(branches[i + 1].name)?.number ?? null)\n\t\t\t\t: null;\n\n\t\tconst stackTable = buildStackTable(branches, tableEntries, branch.name);\n\t\tconst metadataBlock = buildMetadataBlock(\n\t\t\tstackId,\n\t\t\tpr.number,\n\t\t\tprevPr,\n\t\t\tnextPr,\n\t\t\tbranch.name,\n\t\t);\n\n\t\tconst existingBody = pr.body;\n\t\tconst finalBody = composePrBody(existingBody, stackTable, metadataBlock);\n\n\t\tconst tmpFile = writeTempBody(finalBody);\n\t\ttry {\n\t\t\tawait updatePrBody(pr.number, tmpFile, cwd);\n\t\t} finally {\n\t\t\tcleanupTempFile(tmpFile);\n\t\t}\n\t}\n}\n\nfunction writeTempBody(content: string): string {\n\tconst tmpDir = os.tmpdir();\n\tconst tmpFile = path.join(tmpDir, `dubstack-body-${Date.now()}.md`);\n\tfs.writeFileSync(tmpFile, content);\n\treturn tmpFile;\n}\n\nfunction cleanupTempFile(filePath: string): void {\n\ttry {\n\t\tfs.unlinkSync(filePath);\n\t} catch {\n\t\t// Best-effort cleanup\n\t}\n}\n","import { execa } from \"execa\";\nimport { DubError } from \"./errors\";\n\n/** Details of a GitHub Pull Request. */\nexport interface PrInfo {\n\tnumber: number;\n\turl: string;\n\ttitle: string;\n\tbody: string;\n}\n\n/**\n * Ensures the `gh` CLI is installed and available in PATH.\n * @throws {DubError} If `gh` is not found.\n */\nexport async function ensureGhInstalled(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"--version\"]);\n\t} catch {\n\t\tthrow new DubError(\"gh CLI not found. Install it: https://cli.github.com\");\n\t}\n}\n\n/**\n * Ensures the user is authenticated with `gh`.\n * @throws {DubError} If not authenticated.\n */\nexport async function checkGhAuth(): Promise<void> {\n\ttry {\n\t\tawait execa(\"gh\", [\"auth\", \"status\"]);\n\t} catch {\n\t\tthrow new DubError(\"Not authenticated with GitHub. Run 'gh auth login'.\");\n\t}\n}\n\n/**\n * Fetches the open PR for a given head branch, if one exists.\n * @returns The PR info, or `null` if no open PR exists for that branch.\n */\nexport async function getPr(\n\tbranch: string,\n\tcwd: string,\n): Promise<PrInfo | null> {\n\tconst { stdout } = await execa(\n\t\t\"gh\",\n\t\t[\n\t\t\t\"pr\",\n\t\t\t\"list\",\n\t\t\t\"--head\",\n\t\t\tbranch,\n\t\t\t\"--state\",\n\t\t\t\"open\",\n\t\t\t\"--json\",\n\t\t\t\"number,url,title,body\",\n\t\t\t\"--jq\",\n\t\t\t\".[0]\",\n\t\t],\n\t\t{ cwd },\n\t);\n\n\tconst trimmed = stdout.trim();\n\tif (!trimmed || trimmed === \"null\") return null;\n\n\ttry {\n\t\treturn JSON.parse(trimmed) as PrInfo;\n\t} catch {\n\t\tthrow new DubError(`Failed to parse PR info for branch '${branch}'.`);\n\t}\n}\n\n/**\n * Creates a new PR and returns its info.\n *\n * Parses the PR number from the URL printed to stdout by `gh pr create`,\n * avoiding an extra API round-trip.\n *\n * @param branch - Head branch\n * @param base - Base branch the PR merges into\n * @param title - PR title\n * @param bodyFile - Absolute path to a file containing the PR body\n */\nexport async function createPr(\n\tbranch: string,\n\tbase: string,\n\ttitle: string,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<PrInfo> {\n\tlet stdout: string;\n\ttry {\n\t\tconst result = await execa(\n\t\t\t\"gh\",\n\t\t\t[\n\t\t\t\t\"pr\",\n\t\t\t\t\"create\",\n\t\t\t\t\"--head\",\n\t\t\t\tbranch,\n\t\t\t\t\"--base\",\n\t\t\t\tbase,\n\t\t\t\t\"--title\",\n\t\t\t\ttitle,\n\t\t\t\t\"--body-file\",\n\t\t\t\tbodyFile,\n\t\t\t],\n\t\t\t{ cwd },\n\t\t);\n\t\tstdout = result.stdout;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to create PR for '${branch}': ${message}`);\n\t}\n\n\tconst url = stdout.trim();\n\tconst numberMatch = url.match(/\\/pull\\/(\\d+)$/);\n\tif (!numberMatch) {\n\t\tthrow new DubError(`Unexpected output from 'gh pr create': ${url}`);\n\t}\n\n\treturn {\n\t\tnumber: Number.parseInt(numberMatch[1], 10),\n\t\turl,\n\t\ttitle,\n\t\tbody: \"\",\n\t};\n}\n\n/**\n * Updates a PR's body using a file.\n * @param prNumber - The PR number to update\n * @param bodyFile - Absolute path to a file containing the new body\n */\nexport async function updatePrBody(\n\tprNumber: number,\n\tbodyFile: string,\n\tcwd: string,\n): Promise<void> {\n\ttry {\n\t\tawait execa(\n\t\t\t\"gh\",\n\t\t\t[\"pr\", \"edit\", String(prNumber), \"--body-file\", bodyFile],\n\t\t\t{ cwd },\n\t\t);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (message.includes(\"403\") || message.includes(\"insufficient\")) {\n\t\t\tthrow new DubError(\n\t\t\t\t\"GitHub token lacks required permissions. Run 'gh auth login' with the 'repo' scope.\",\n\t\t\t);\n\t\t}\n\t\tthrow new DubError(`Failed to update PR #${prNumber}: ${message}`);\n\t}\n}\n","import type { Branch } from \"./state\";\n\ninterface StackEntry {\n\tnumber: number;\n\ttitle: string;\n}\n\nconst DUBSTACK_START = \"<!-- dubstack:start -->\";\nconst DUBSTACK_END = \"<!-- dubstack:end -->\";\nconst METADATA_START = \"<!-- dubstack-metadata\";\nconst METADATA_END = \"-->\";\n\n/**\n * Builds the visible stack navigation table wrapped in dubstack markers.\n *\n * @param orderedBranches - Non-root branches in topological order\n * @param prMap - Map of branch name → PR number + title\n * @param currentBranch - The branch to mark with 👈\n */\nexport function buildStackTable(\n\torderedBranches: Branch[],\n\tprMap: Map<string, StackEntry>,\n\tcurrentBranch: string,\n): string {\n\tconst lines = orderedBranches.map((branch) => {\n\t\tconst entry = prMap.get(branch.name);\n\t\tif (!entry) return `- ${branch.name}`;\n\t\tconst marker = branch.name === currentBranch ? \" 👈\" : \"\";\n\t\treturn `- #${entry.number} ${entry.title}${marker}`;\n\t});\n\n\treturn [\n\t\tDUBSTACK_START,\n\t\t\"---\",\n\t\t\"### 🥞 DubStack\",\n\t\t...lines,\n\t\tDUBSTACK_END,\n\t].join(\"\\n\");\n}\n\n/**\n * Builds the hidden metadata HTML comment block.\n */\nexport function buildMetadataBlock(\n\tstackId: string,\n\tprNumber: number,\n\tprevPr: number | null,\n\tnextPr: number | null,\n\tbranch: string,\n): string {\n\tconst metadata = {\n\t\tstack_id: stackId,\n\t\tpr_number: prNumber,\n\t\tprev_pr: prevPr,\n\t\tnext_pr: nextPr,\n\t\tbranch,\n\t};\n\treturn `${METADATA_START}\\n${JSON.stringify(metadata, null, 2)}\\n${METADATA_END}`;\n}\n\n/**\n * Strips all DubStack-generated sections from a PR body.\n * Preserves user-written content. Returns body unchanged if no markers exist.\n */\nexport function stripDubstackSections(body: string): string {\n\tlet result = body;\n\n\tconst startIdx = result.indexOf(DUBSTACK_START);\n\tconst endIdx = result.indexOf(DUBSTACK_END);\n\tif (startIdx !== -1 && endIdx !== -1) {\n\t\tresult =\n\t\t\tresult.slice(0, startIdx) + result.slice(endIdx + DUBSTACK_END.length);\n\t}\n\n\tconst metaStart = result.indexOf(METADATA_START);\n\tif (metaStart !== -1) {\n\t\tconst metaEnd = result.indexOf(\n\t\t\tMETADATA_END,\n\t\t\tmetaStart + METADATA_START.length,\n\t\t);\n\t\tif (metaEnd !== -1) {\n\t\t\tresult =\n\t\t\t\tresult.slice(0, metaStart) +\n\t\t\t\tresult.slice(metaEnd + METADATA_END.length);\n\t\t}\n\t}\n\n\treturn result.trimEnd();\n}\n\n/**\n * Composes the final PR body by combining user content with DubStack sections.\n *\n * @param existingBody - The existing PR body (may contain stale DubStack sections)\n * @param stackTable - Output of `buildStackTable`\n * @param metadataBlock - Output of `buildMetadataBlock`\n */\nexport function composePrBody(\n\texistingBody: string,\n\tstackTable: string,\n\tmetadataBlock: string,\n): string {\n\tconst userContent = stripDubstackSections(existingBody);\n\tconst parts = [userContent, stackTable, metadataBlock].filter(Boolean);\n\treturn parts.join(\"\\n\\n\");\n}\n","import { DubError } from \"../lib/errors\";\nimport {\n\tcheckoutBranch,\n\tdeleteBranch,\n\tforceBranchTo,\n\tgetCurrentBranch,\n\tisWorkingTreeClean,\n} from \"../lib/git\";\nimport { writeState } from \"../lib/state\";\nimport { clearUndoEntry, readUndoEntry } from \"../lib/undo-log\";\n\ninterface UndoResult {\n\tundone: \"create\" | \"restack\";\n\tdetails: string;\n}\n\n/**\n * Undoes the last `dub create` or `dub restack` operation.\n *\n * Reversal strategy:\n * - **create**: Deletes the created branch, restores state, checks out the previous branch.\n * - **restack**: Resets every rebased branch to its pre-rebase tip via `git branch -f`,\n * restores state, checks out the previous branch.\n *\n * Only one level of undo is supported. After undo, the undo entry is cleared.\n *\n * @param cwd - Working directory\n * @returns What was undone and a human-readable summary\n * @throws {DubError} If nothing to undo or working tree is dirty\n */\nexport async function undo(cwd: string): Promise<UndoResult> {\n\tconst entry = await readUndoEntry(cwd);\n\n\tif (!(await isWorkingTreeClean(cwd))) {\n\t\tthrow new DubError(\n\t\t\t\"Working tree has uncommitted changes. Commit or stash them before undoing.\",\n\t\t);\n\t}\n\n\tconst currentBranch = await getCurrentBranch(cwd);\n\n\tif (entry.operation === \"create\") {\n\t\t// If we're on a branch that's about to be deleted, switch away first\n\t\tconst needsCheckout = entry.createdBranches.includes(currentBranch);\n\t\tif (needsCheckout) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tfor (const branch of entry.createdBranches) {\n\t\t\tawait deleteBranch(branch, cwd);\n\t\t}\n\n\t\tif (!needsCheckout && currentBranch !== entry.previousBranch) {\n\t\t\tawait checkoutBranch(entry.previousBranch, cwd);\n\t\t}\n\n\t\tawait writeState(entry.previousState, cwd);\n\t\tawait clearUndoEntry(cwd);\n\n\t\treturn {\n\t\t\tundone: \"create\",\n\t\t\tdetails: `Deleted branch${entry.createdBranches.length > 1 ? \"es\" : \"\"} '${entry.createdBranches.join(\"', '\")}'`,\n\t\t};\n\t}\n\n\t// restack undo: reset all branches to their pre-rebase tips\n\t// First checkout a safe branch so we don't conflict with force-moves\n\tawait checkoutBranch(entry.previousBranch, cwd);\n\n\tfor (const [name, sha] of Object.entries(entry.branchTips)) {\n\t\tif (name === entry.previousBranch) continue; // skip the branch we're on\n\t\tawait forceBranchTo(name, sha, cwd);\n\t}\n\n\t// Now force the branch we're on (if it was tracked)\n\tif (entry.branchTips[entry.previousBranch]) {\n\t\tawait forceBranchTo(\n\t\t\tentry.previousBranch,\n\t\t\tentry.branchTips[entry.previousBranch],\n\t\t\tcwd,\n\t\t);\n\t}\n\n\tawait writeState(entry.previousState, cwd);\n\tawait clearUndoEntry(cwd);\n\n\treturn {\n\t\tundone: \"restack\",\n\t\tdetails: `Reset ${Object.keys(entry.branchTips).length} branches to pre-restack state`,\n\t};\n}\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","fs","path","execa","execa","require"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dubstack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI tool for managing stacked diffs (dependent git branches)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"@inquirer/search": "^4.1.3",
|
|
30
31
|
"chalk": "^5.6.2",
|
|
31
32
|
"commander": "^14.0.3",
|
|
32
33
|
"execa": "^9.6.1"
|