dubstack 0.1.3

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +276 -0
  3. package/dist/commands/create.d.ts +18 -0
  4. package/dist/commands/create.d.ts.map +1 -0
  5. package/dist/commands/create.js +35 -0
  6. package/dist/commands/create.js.map +1 -0
  7. package/dist/commands/init.d.ts +18 -0
  8. package/dist/commands/init.d.ts.map +1 -0
  9. package/dist/commands/init.js +41 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/log.d.ts +12 -0
  12. package/dist/commands/log.d.ts.map +1 -0
  13. package/dist/commands/log.js +77 -0
  14. package/dist/commands/log.js.map +1 -0
  15. package/dist/commands/restack.d.ts +33 -0
  16. package/dist/commands/restack.d.ts.map +1 -0
  17. package/dist/commands/restack.js +190 -0
  18. package/dist/commands/restack.js.map +1 -0
  19. package/dist/commands/undo.d.ts +21 -0
  20. package/dist/commands/undo.d.ts.map +1 -0
  21. package/dist/commands/undo.js +63 -0
  22. package/dist/commands/undo.js.map +1 -0
  23. package/dist/index.d.ts +20 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +125 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lib/errors.d.ts +15 -0
  28. package/dist/lib/errors.d.ts.map +1 -0
  29. package/dist/lib/errors.js +18 -0
  30. package/dist/lib/errors.js.map +1 -0
  31. package/dist/lib/git.d.ts +69 -0
  32. package/dist/lib/git.d.ts.map +1 -0
  33. package/dist/lib/git.js +184 -0
  34. package/dist/lib/git.js.map +1 -0
  35. package/dist/lib/state.d.ts +70 -0
  36. package/dist/lib/state.d.ts.map +1 -0
  37. package/dist/lib/state.js +110 -0
  38. package/dist/lib/state.js.map +1 -0
  39. package/dist/lib/undo-log.d.ts +33 -0
  40. package/dist/lib/undo-log.d.ts.map +1 -0
  41. package/dist/lib/undo-log.js +37 -0
  42. package/dist/lib/undo-log.js.map +1 -0
  43. package/package.json +49 -0
@@ -0,0 +1,190 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { DubError } from "../lib/errors.js";
4
+ import { branchExists, checkoutBranch, getBranchTip, getCurrentBranch, getMergeBase, rebaseContinue as gitRebaseContinue, isWorkingTreeClean, rebaseOnto, } from "../lib/git.js";
5
+ import { getDubDir, readState } from "../lib/state.js";
6
+ import { saveUndoEntry } from "../lib/undo-log.js";
7
+ /**
8
+ * Rebases all branches in the current stack onto their updated parents.
9
+ *
10
+ * Uses a snapshot-before-rebase strategy: captures every branch's tip
11
+ * BEFORE starting any rebases, then uses `git rebase --onto <parent_new_tip>
12
+ * <parent_old_tip> <child>`. This prevents the duplication bug where a child
13
+ * replays its parent's already-rebased commits.
14
+ *
15
+ * On conflict, writes progress to `restack-progress.json` so
16
+ * `dub restack --continue` can resume.
17
+ *
18
+ * @param cwd - Working directory
19
+ * @returns Result with status, list of rebased branches, and optional conflict branch
20
+ * @throws {DubError} If not initialized, dirty tree, not in a stack, or branch missing
21
+ */
22
+ export async function restack(cwd) {
23
+ const state = await readState(cwd);
24
+ if (!(await isWorkingTreeClean(cwd))) {
25
+ throw new DubError("Working tree has uncommitted changes. Commit or stash them before restacking.");
26
+ }
27
+ const originalBranch = await getCurrentBranch(cwd);
28
+ const targetStacks = getTargetStacks(state.stacks, originalBranch);
29
+ if (targetStacks.length === 0) {
30
+ throw new DubError(`Branch '${originalBranch}' is not part of any stack. Run 'dub create' first.`);
31
+ }
32
+ const allBranches = targetStacks.flatMap((s) => s.branches);
33
+ for (const branch of allBranches) {
34
+ if (!(await branchExists(branch.name, cwd))) {
35
+ throw new DubError(`Branch '${branch.name}' is tracked in state but no longer exists in git.\n` +
36
+ " Remove it from the stack or recreate it before restacking.");
37
+ }
38
+ }
39
+ // Snapshot all branch tips BEFORE building steps or rebasing
40
+ const branchTips = {};
41
+ for (const branch of allBranches) {
42
+ branchTips[branch.name] = await getBranchTip(branch.name, cwd);
43
+ }
44
+ const steps = await buildRestackSteps(targetStacks, cwd);
45
+ if (steps.length === 0) {
46
+ return { status: "up-to-date", rebased: [] };
47
+ }
48
+ await saveUndoEntry({
49
+ operation: "restack",
50
+ timestamp: new Date().toISOString(),
51
+ previousBranch: originalBranch,
52
+ previousState: structuredClone(state),
53
+ branchTips,
54
+ createdBranches: [],
55
+ }, cwd);
56
+ const progress = { originalBranch, steps };
57
+ await writeProgress(progress, cwd);
58
+ return executeRestackSteps(progress, cwd);
59
+ }
60
+ /**
61
+ * Continues a restack after conflict resolution.
62
+ *
63
+ * Reads the saved progress file, finishes the in-progress rebase,
64
+ * then resumes with remaining branches.
65
+ *
66
+ * @param cwd - Working directory
67
+ * @throws {DubError} If no restack is in progress
68
+ */
69
+ export async function restackContinue(cwd) {
70
+ const progress = await readProgress(cwd);
71
+ if (!progress) {
72
+ throw new DubError("No restack in progress. Run 'dub restack' to start.");
73
+ }
74
+ await gitRebaseContinue(cwd);
75
+ const conflictedStep = progress.steps.find((s) => s.status === "conflicted");
76
+ if (conflictedStep) {
77
+ conflictedStep.status = "done";
78
+ }
79
+ return executeRestackSteps(progress, cwd);
80
+ }
81
+ async function executeRestackSteps(progress, cwd) {
82
+ const rebased = [];
83
+ for (const step of progress.steps) {
84
+ if (step.status !== "pending") {
85
+ if (step.status === "done")
86
+ rebased.push(step.branch);
87
+ continue;
88
+ }
89
+ const parentNewTip = await getBranchTip(step.parent, cwd);
90
+ if (parentNewTip === step.parentOldTip) {
91
+ step.status = "skipped";
92
+ await writeProgress(progress, cwd);
93
+ continue;
94
+ }
95
+ try {
96
+ await rebaseOnto(parentNewTip, step.parentOldTip, step.branch, cwd);
97
+ step.status = "done";
98
+ rebased.push(step.branch);
99
+ await writeProgress(progress, cwd);
100
+ }
101
+ catch (error) {
102
+ if (error instanceof DubError && error.message.includes("Conflict")) {
103
+ step.status = "conflicted";
104
+ await writeProgress(progress, cwd);
105
+ return { status: "conflict", rebased, conflictBranch: step.branch };
106
+ }
107
+ throw error;
108
+ }
109
+ }
110
+ await clearProgress(cwd);
111
+ await checkoutBranch(progress.originalBranch, cwd);
112
+ const allSkipped = progress.steps.every((s) => s.status === "skipped" || s.status === "done");
113
+ return {
114
+ status: rebased.length === 0 && allSkipped ? "up-to-date" : "success",
115
+ rebased,
116
+ };
117
+ }
118
+ function getTargetStacks(stacks, currentBranch) {
119
+ // If current branch is a root of any stacks, restack all of them
120
+ const rootStacks = stacks.filter((s) => s.branches.some((b) => b.name === currentBranch && b.type === "root"));
121
+ if (rootStacks.length > 0)
122
+ return rootStacks;
123
+ // Otherwise, find the stack containing the current branch
124
+ const stack = stacks.find((s) => s.branches.some((b) => b.name === currentBranch));
125
+ return stack ? [stack] : [];
126
+ }
127
+ async function buildRestackSteps(stacks, cwd) {
128
+ const steps = [];
129
+ for (const stack of stacks) {
130
+ const ordered = topologicalOrder(stack);
131
+ for (const branch of ordered) {
132
+ if (branch.type === "root" || !branch.parent)
133
+ continue;
134
+ const mergeBase = await getMergeBase(branch.parent, branch.name, cwd);
135
+ steps.push({
136
+ branch: branch.name,
137
+ parent: branch.parent,
138
+ parentOldTip: mergeBase,
139
+ status: "pending",
140
+ });
141
+ }
142
+ }
143
+ return steps;
144
+ }
145
+ function topologicalOrder(stack) {
146
+ const result = [];
147
+ const root = stack.branches.find((b) => b.type === "root");
148
+ if (!root)
149
+ return result;
150
+ const childMap = new Map();
151
+ for (const branch of stack.branches) {
152
+ if (branch.parent) {
153
+ const children = childMap.get(branch.parent) ?? [];
154
+ children.push(branch);
155
+ childMap.set(branch.parent, children);
156
+ }
157
+ }
158
+ const queue = [root];
159
+ while (queue.length > 0) {
160
+ const current = queue.shift();
161
+ if (!current)
162
+ break;
163
+ result.push(current);
164
+ const children = childMap.get(current.name) ?? [];
165
+ queue.push(...children);
166
+ }
167
+ return result;
168
+ }
169
+ async function getProgressPath(cwd) {
170
+ const dubDir = await getDubDir(cwd);
171
+ return path.join(dubDir, "restack-progress.json");
172
+ }
173
+ async function writeProgress(progress, cwd) {
174
+ const progressPath = await getProgressPath(cwd);
175
+ fs.writeFileSync(progressPath, `${JSON.stringify(progress, null, 2)}\n`);
176
+ }
177
+ async function readProgress(cwd) {
178
+ const progressPath = await getProgressPath(cwd);
179
+ if (!fs.existsSync(progressPath))
180
+ return null;
181
+ const raw = fs.readFileSync(progressPath, "utf-8");
182
+ return JSON.parse(raw);
183
+ }
184
+ async function clearProgress(cwd) {
185
+ const progressPath = await getProgressPath(cwd);
186
+ if (fs.existsSync(progressPath)) {
187
+ fs.unlinkSync(progressPath);
188
+ }
189
+ }
190
+ //# sourceMappingURL=restack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restack.js","sourceRoot":"","sources":["../../src/commands/restack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ interface UndoResult {
2
+ undone: "create" | "restack";
3
+ details: string;
4
+ }
5
+ /**
6
+ * Undoes the last `dub create` or `dub restack` operation.
7
+ *
8
+ * Reversal strategy:
9
+ * - **create**: Deletes the created branch, restores state, checks out the previous branch.
10
+ * - **restack**: Resets every rebased branch to its pre-rebase tip via `git branch -f`,
11
+ * restores state, checks out the previous branch.
12
+ *
13
+ * Only one level of undo is supported. After undo, the undo entry is cleared.
14
+ *
15
+ * @param cwd - Working directory
16
+ * @returns What was undone and a human-readable summary
17
+ * @throws {DubError} If nothing to undo or working tree is dirty
18
+ */
19
+ export declare function undo(cwd: string): Promise<UndoResult>;
20
+ export {};
21
+ //# sourceMappingURL=undo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/commands/undo.ts"],"names":[],"mappings":"AAWA,UAAU,UAAU;IACnB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA4D3D"}
@@ -0,0 +1,63 @@
1
+ import { DubError } from "../lib/errors.js";
2
+ import { checkoutBranch, deleteBranch, forceBranchTo, getCurrentBranch, isWorkingTreeClean, } from "../lib/git.js";
3
+ import { writeState } from "../lib/state.js";
4
+ import { clearUndoEntry, readUndoEntry } from "../lib/undo-log.js";
5
+ /**
6
+ * Undoes the last `dub create` or `dub restack` operation.
7
+ *
8
+ * Reversal strategy:
9
+ * - **create**: Deletes the created branch, restores state, checks out the previous branch.
10
+ * - **restack**: Resets every rebased branch to its pre-rebase tip via `git branch -f`,
11
+ * restores state, checks out the previous branch.
12
+ *
13
+ * Only one level of undo is supported. After undo, the undo entry is cleared.
14
+ *
15
+ * @param cwd - Working directory
16
+ * @returns What was undone and a human-readable summary
17
+ * @throws {DubError} If nothing to undo or working tree is dirty
18
+ */
19
+ export async function undo(cwd) {
20
+ const entry = await readUndoEntry(cwd);
21
+ if (!(await isWorkingTreeClean(cwd))) {
22
+ throw new DubError("Working tree has uncommitted changes. Commit or stash them before undoing.");
23
+ }
24
+ const currentBranch = await getCurrentBranch(cwd);
25
+ if (entry.operation === "create") {
26
+ // If we're on a branch that's about to be deleted, switch away first
27
+ const needsCheckout = entry.createdBranches.includes(currentBranch);
28
+ if (needsCheckout) {
29
+ await checkoutBranch(entry.previousBranch, cwd);
30
+ }
31
+ for (const branch of entry.createdBranches) {
32
+ await deleteBranch(branch, cwd);
33
+ }
34
+ if (!needsCheckout && currentBranch !== entry.previousBranch) {
35
+ await checkoutBranch(entry.previousBranch, cwd);
36
+ }
37
+ await writeState(entry.previousState, cwd);
38
+ await clearUndoEntry(cwd);
39
+ return {
40
+ undone: "create",
41
+ details: `Deleted branch${entry.createdBranches.length > 1 ? "es" : ""} '${entry.createdBranches.join("', '")}'`,
42
+ };
43
+ }
44
+ // restack undo: reset all branches to their pre-rebase tips
45
+ // First checkout a safe branch so we don't conflict with force-moves
46
+ await checkoutBranch(entry.previousBranch, cwd);
47
+ for (const [name, sha] of Object.entries(entry.branchTips)) {
48
+ if (name === entry.previousBranch)
49
+ continue; // skip the branch we're on
50
+ await forceBranchTo(name, sha, cwd);
51
+ }
52
+ // Now force the branch we're on (if it was tracked)
53
+ if (entry.branchTips[entry.previousBranch]) {
54
+ await forceBranchTo(entry.previousBranch, entry.branchTips[entry.previousBranch], cwd);
55
+ }
56
+ await writeState(entry.previousState, cwd);
57
+ await clearUndoEntry(cwd);
58
+ return {
59
+ undone: "restack",
60
+ details: `Reset ${Object.keys(entry.branchTips).length} branches to pre-restack state`,
61
+ };
62
+ }
63
+ //# sourceMappingURL=undo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.js","sourceRoot":"","sources":["../../src/commands/undo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EACN,cAAc,EACd,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOnE;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IAEvC,IAAI,CAAC,CAAC,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,QAAQ,CACjB,4EAA4E,CAC5E,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAElD,IAAI,KAAK,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,qEAAqE;QACrE,MAAM,aAAa,GAAG,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACpE,IAAI,aAAa,EAAE,CAAC;YACnB,MAAM,cAAc,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;YAC5C,MAAM,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,CAAC,aAAa,IAAI,aAAa,KAAK,KAAK,CAAC,cAAc,EAAE,CAAC;YAC9D,MAAM,cAAc,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,UAAU,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC3C,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;QAE1B,OAAO;YACN,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,iBAAiB,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG;SAChH,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,qEAAqE;IACrE,MAAM,cAAc,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IAEhD,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5D,IAAI,IAAI,KAAK,KAAK,CAAC,cAAc;YAAE,SAAS,CAAC,2BAA2B;QACxE,MAAM,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,oDAAoD;IACpD,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;QAC5C,MAAM,aAAa,CAClB,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,EACtC,GAAG,CACH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAC3C,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IAE1B,OAAO;QACN,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,SAAS,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,gCAAgC;KACtF,CAAC;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * DubStack CLI — manage stacked diffs with ease.
4
+ *
5
+ * A local-first tool for managing chains of dependent git branches
6
+ * (stacked diffs) without manually dealing with complex rebase chains.
7
+ *
8
+ * @example
9
+ * ```bash
10
+ * dub init # Initialize in current repo
11
+ * dub create feat/my-branch # Create stacked branch
12
+ * dub log # View stack tree
13
+ * dub restack # Rebase stack onto updated parent
14
+ * dub undo # Undo last dub operation
15
+ * ```
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;GAgBG"}
package/dist/index.js ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * DubStack CLI — manage stacked diffs with ease.
4
+ *
5
+ * A local-first tool for managing chains of dependent git branches
6
+ * (stacked diffs) without manually dealing with complex rebase chains.
7
+ *
8
+ * @example
9
+ * ```bash
10
+ * dub init # Initialize in current repo
11
+ * dub create feat/my-branch # Create stacked branch
12
+ * dub log # View stack tree
13
+ * dub restack # Rebase stack onto updated parent
14
+ * dub undo # Undo last dub operation
15
+ * ```
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ import { createRequire } from "node:module";
20
+ import chalk from "chalk";
21
+ import { Command } from "commander";
22
+ import { create } from "./commands/create.js";
23
+ import { init } from "./commands/init.js";
24
+ import { log } from "./commands/log.js";
25
+ import { restack, restackContinue } from "./commands/restack.js";
26
+ import { undo } from "./commands/undo.js";
27
+ import { DubError } from "./lib/errors.js";
28
+ const require = createRequire(import.meta.url);
29
+ const { version } = require("../package.json");
30
+ const program = new Command();
31
+ program
32
+ .name("dub")
33
+ .description("Manage stacked diffs (dependent git branches) with ease")
34
+ .version(version);
35
+ program
36
+ .command("init")
37
+ .description("Initialize DubStack in the current git repository")
38
+ .addHelpText("after", `
39
+ Examples:
40
+ $ dub init Initialize DubStack, creating .git/dubstack/ and updating .gitignore`)
41
+ .action(async () => {
42
+ const result = await init(process.cwd());
43
+ if (result.status === "created") {
44
+ console.log(chalk.green("✔ DubStack initialized"));
45
+ }
46
+ else {
47
+ console.log(chalk.yellow("⚠ DubStack already initialized"));
48
+ }
49
+ });
50
+ program
51
+ .command("create")
52
+ .argument("<branch-name>", "Name of the new branch to create")
53
+ .description("Create a new branch stacked on top of the current branch")
54
+ .addHelpText("after", `
55
+ Examples:
56
+ $ dub create feat/api-endpoint Create a branch on top of current branch
57
+ $ dub create feat/ui-component Stack another branch on top`)
58
+ .action(async (branchName) => {
59
+ const result = await create(branchName, process.cwd());
60
+ console.log(chalk.green(`✔ Created branch '${result.branch}' on top of '${result.parent}'`));
61
+ });
62
+ program
63
+ .command("log")
64
+ .description("Display an ASCII tree of the current stack")
65
+ .addHelpText("after", `
66
+ Examples:
67
+ $ dub log Show the branch tree with current branch highlighted`)
68
+ .action(async () => {
69
+ const output = await log(process.cwd());
70
+ // Apply chalk styling to the output
71
+ const styled = output
72
+ .replace(/\*(.+?) \(Current\)\*/g, chalk.bold.cyan("$1 (Current)"))
73
+ .replace(/⚠ \(missing\)/g, chalk.yellow("⚠ (missing)"));
74
+ console.log(styled);
75
+ });
76
+ program
77
+ .command("restack")
78
+ .description("Rebase all branches in the stack onto their updated parents")
79
+ .option("--continue", "Continue restacking after resolving conflicts")
80
+ .addHelpText("after", `
81
+ Examples:
82
+ $ dub restack Rebase the current stack
83
+ $ dub restack --continue Continue after resolving conflicts`)
84
+ .action(async (options) => {
85
+ const result = options.continue
86
+ ? await restackContinue(process.cwd())
87
+ : await restack(process.cwd());
88
+ if (result.status === "up-to-date") {
89
+ console.log(chalk.green("✔ Stack is already up to date"));
90
+ }
91
+ else if (result.status === "conflict") {
92
+ console.log(chalk.yellow(`⚠ Conflict while restacking '${result.conflictBranch}'`));
93
+ console.log(chalk.dim(" Resolve conflicts, stage changes, then run: dub restack --continue"));
94
+ }
95
+ else {
96
+ console.log(chalk.green(`✔ Restacked ${result.rebased.length} branch(es)`));
97
+ for (const branch of result.rebased) {
98
+ console.log(chalk.dim(` ↳ ${branch}`));
99
+ }
100
+ }
101
+ });
102
+ program
103
+ .command("undo")
104
+ .description("Undo the last dub create or dub restack operation")
105
+ .addHelpText("after", `
106
+ Examples:
107
+ $ dub undo Roll back the last dub operation`)
108
+ .action(async () => {
109
+ const result = await undo(process.cwd());
110
+ console.log(chalk.green(`✔ Undid '${result.undone}': ${result.details}`));
111
+ });
112
+ async function main() {
113
+ try {
114
+ await program.parseAsync(process.argv);
115
+ }
116
+ catch (error) {
117
+ if (error instanceof DubError) {
118
+ console.error(chalk.red(`✖ ${error.message}`));
119
+ process.exit(1);
120
+ }
121
+ throw error;
122
+ }
123
+ }
124
+ main();
125
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Base error class for all user-facing DubStack errors.
3
+ *
4
+ * The CLI entry point catches instances of this class and prints
5
+ * a clean, colored error message. Unknown errors get a full stack trace.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * throw new DubError("Branch 'feat/x' already exists")
10
+ * ```
11
+ */
12
+ export declare class DubError extends Error {
13
+ constructor(message: string);
14
+ }
15
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,qBAAa,QAAS,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI3B"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Base error class for all user-facing DubStack errors.
3
+ *
4
+ * The CLI entry point catches instances of this class and prints
5
+ * a clean, colored error message. Unknown errors get a full stack trace.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * throw new DubError("Branch 'feat/x' already exists")
10
+ * ```
11
+ */
12
+ export class DubError extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = "DubError";
16
+ }
17
+ }
18
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IAClC,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACxB,CAAC;CACD"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Checks whether the given directory is inside a git repository.
3
+ * @returns `true` if inside a git worktree, `false` otherwise. Never throws.
4
+ */
5
+ export declare function isGitRepo(cwd: string): Promise<boolean>;
6
+ /**
7
+ * Returns the absolute path to the repository root.
8
+ * @throws {DubError} If not inside a git repository.
9
+ */
10
+ export declare function getRepoRoot(cwd: string): Promise<string>;
11
+ /**
12
+ * Returns the name of the currently checked-out branch.
13
+ * @throws {DubError} If HEAD is detached or the repo has no commits.
14
+ */
15
+ export declare function getCurrentBranch(cwd: string): Promise<string>;
16
+ /**
17
+ * Checks whether a branch with the given name exists locally.
18
+ * @returns `true` if the branch exists, `false` otherwise. Never throws.
19
+ */
20
+ export declare function branchExists(name: string, cwd: string): Promise<boolean>;
21
+ /**
22
+ * Creates a new branch and switches to it.
23
+ * @throws {DubError} If a branch with that name already exists.
24
+ */
25
+ export declare function createBranch(name: string, cwd: string): Promise<void>;
26
+ /**
27
+ * Switches to an existing branch.
28
+ * @throws {DubError} If the branch does not exist.
29
+ */
30
+ export declare function checkoutBranch(name: string, cwd: string): Promise<void>;
31
+ /**
32
+ * Deletes a local branch forcefully. Used by undo to remove created branches.
33
+ * @throws {DubError} If the branch does not exist.
34
+ */
35
+ export declare function deleteBranch(name: string, cwd: string): Promise<void>;
36
+ /**
37
+ * Force-moves a branch pointer to a specific commit SHA.
38
+ * Used by undo to reset branches to their pre-operation tips.
39
+ */
40
+ export declare function forceBranchTo(name: string, sha: string, cwd: string): Promise<void>;
41
+ /**
42
+ * Checks whether the working tree is clean (no uncommitted changes).
43
+ * @returns `true` if clean (no output from `git status --porcelain`).
44
+ */
45
+ export declare function isWorkingTreeClean(cwd: string): Promise<boolean>;
46
+ /**
47
+ * Performs `git rebase --onto` to move a branch from one base to another.
48
+ *
49
+ * @param newBase - The commit/branch to rebase onto
50
+ * @param oldBase - The old base commit to replay from
51
+ * @param branch - The branch being rebased
52
+ * @throws {DubError} If a merge conflict occurs during rebase
53
+ */
54
+ export declare function rebaseOnto(newBase: string, oldBase: string, branch: string, cwd: string): Promise<void>;
55
+ /**
56
+ * Continues an in-progress rebase after conflicts have been resolved.
57
+ * @throws {DubError} If the rebase continue fails.
58
+ */
59
+ export declare function rebaseContinue(cwd: string): Promise<void>;
60
+ /**
61
+ * Returns the merge-base (common ancestor) commit SHA of two branches.
62
+ */
63
+ export declare function getMergeBase(a: string, b: string, cwd: string): Promise<string>;
64
+ /**
65
+ * Returns the commit SHA at the tip of a branch.
66
+ * @throws {DubError} If the branch does not exist.
67
+ */
68
+ export declare function getBranchTip(name: string, cwd: string): Promise<string>;
69
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/lib/git.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAW7D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW9D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBnE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CACjC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CASlB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3E;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7E;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3E;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW/D;AAED;;GAEG;AACH,wBAAsB,YAAY,CACjC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,MAAM,CAAC,CASjB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO7E"}