gflows 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/package.json +2 -1
- package/src/cli.ts +7 -4
- package/src/commands/help.ts +1 -0
- package/src/commands/switch.ts +38 -3
- package/src/types.ts +1 -1
package/README.md
CHANGED
|
@@ -265,7 +265,7 @@ Release and hotfix names must be a version (`vX.Y.Z` or `X.Y.Z`). Branch names u
|
|
|
265
265
|
| `init` | `-I` | Ensure main exists; create dev from main. |
|
|
266
266
|
| `start` | `-S` | Create a workflow branch (requires type + name). |
|
|
267
267
|
| `finish` | `-F` | Merge branch into target(s), optional tag (release/hotfix), delete, push. |
|
|
268
|
-
| `switch` | `-W` | Switch to a workflow branch (picker or name); with uncommitted changes: prompt or `--move` / `--restore` / `--clean` / `--cancel`. |
|
|
268
|
+
| `switch` | `-W` | Switch to a workflow branch (picker or name); with uncommitted changes: prompt or `--move` / `--restore` / `--clean` / `--destroy` / `--cancel`. |
|
|
269
269
|
| `delete` | `-L` | Delete local workflow branch(es). Never main/dev. |
|
|
270
270
|
| `list` | `-l` | List workflow branches; optional type filter and remote. |
|
|
271
271
|
| `bump` | — | Bump or rollback package version (patch/minor/major). |
|
|
@@ -404,6 +404,7 @@ Switch to a workflow branch. With TTY and no branch name, shows a picker; otherw
|
|
|
404
404
|
| **Move** | Move current changes to the target branch. |
|
|
405
405
|
| **Restore** | Save changes for this branch; restore target's saved state (if any). |
|
|
406
406
|
| **Clean** | Discard changes and switch clean at HEAD. |
|
|
407
|
+
| **Destroy** | Delete current branch and switch to the target branch (cannot destroy main or dev). |
|
|
407
408
|
| **Cancel** | Abort switching. |
|
|
408
409
|
|
|
409
410
|
You can skip the prompt by passing exactly one of the flags below. If the target branch has saved changes and you use **Clean**, a warning is shown (unless `-q`).
|
|
@@ -415,6 +416,7 @@ bun gflows switch
|
|
|
415
416
|
bun gflows switch feature/auth-refactor
|
|
416
417
|
bun gflows switch dev --restore
|
|
417
418
|
bun gflows switch main --clean
|
|
419
|
+
bun gflows switch dev --destroy
|
|
418
420
|
bun gflows -W feature/auth-refactor
|
|
419
421
|
```
|
|
420
422
|
|
|
@@ -428,6 +430,7 @@ bun gflows -W feature/auth-refactor
|
|
|
428
430
|
| `--move` | — | Move current changes to the target branch; no prompt. |
|
|
429
431
|
| `--restore` | — | Save for this branch; restore target's saved state (if any); no prompt. |
|
|
430
432
|
| `--clean` | — | Discard changes and switch clean at HEAD; no prompt. |
|
|
433
|
+
| `--destroy` | — | Delete current branch and switch to target; no prompt (not main/dev). |
|
|
431
434
|
| `--cancel` | — | Abort switching; no prompt. |
|
|
432
435
|
| `--verbose` | `-v` | Verbose output. |
|
|
433
436
|
| `--quiet` | `-q` | Minimal output (suppresses Clean warning about saved changes on target). |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gflows",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "A lightweight CLI for consistent Git branching workflows (main + dev, feature/bugfix/chore/release/hotfix).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"lint": "biome check .",
|
|
15
15
|
"lint:fix": "biome check . --write",
|
|
16
16
|
"format": "biome format . --write",
|
|
17
|
+
"check": "bun run test && bun run typecheck && bun run lint",
|
|
17
18
|
"gflows": "bun run src/cli.ts",
|
|
18
19
|
"publish:all": "bun run scripts/publish.ts",
|
|
19
20
|
"publish:npm": "bun run scripts/publish.ts -- --npm-only",
|
package/src/cli.ts
CHANGED
|
@@ -95,11 +95,12 @@ function buildParseArgsOptions() {
|
|
|
95
95
|
// list (-r is context-dependent: list → include-remote; start/finish → release)
|
|
96
96
|
includeRemote: { type: "boolean" as const },
|
|
97
97
|
"include-remote": { type: "boolean" as const },
|
|
98
|
-
// switch: explicit mode (--restore, --clean, --cancel, --move)
|
|
98
|
+
// switch: explicit mode (--restore, --clean, --cancel, --move, --destroy)
|
|
99
99
|
restore: { type: "boolean" as const },
|
|
100
100
|
clean: { type: "boolean" as const },
|
|
101
101
|
cancel: { type: "boolean" as const },
|
|
102
102
|
move: { type: "boolean" as const },
|
|
103
|
+
destroy: { type: "boolean" as const },
|
|
103
104
|
},
|
|
104
105
|
};
|
|
105
106
|
}
|
|
@@ -317,16 +318,17 @@ export function parse(argv: string[] = Bun.argv.slice(2)): ParsedArgs {
|
|
|
317
318
|
else if (command === "completion" && name === "zsh") completionShell = "zsh";
|
|
318
319
|
else if (command === "completion" && name === "fish") completionShell = "fish";
|
|
319
320
|
|
|
320
|
-
let switchMode: "restore" | "clean" | "cancel" | "move" | undefined;
|
|
321
|
+
let switchMode: "restore" | "clean" | "cancel" | "move" | "destroy" | undefined;
|
|
321
322
|
if (command === "switch") {
|
|
322
323
|
const restore = v.restore === true;
|
|
323
324
|
const clean = v.clean === true;
|
|
324
325
|
const cancel = v.cancel === true;
|
|
325
326
|
const move = v.move === true;
|
|
326
|
-
const
|
|
327
|
+
const destroy = v.destroy === true;
|
|
328
|
+
const count = [restore, clean, cancel, move, destroy].filter(Boolean).length;
|
|
327
329
|
if (count > 1) {
|
|
328
330
|
console.error(
|
|
329
|
-
"gflows switch: only one of --restore, --clean, --cancel, or --
|
|
331
|
+
"gflows switch: only one of --restore, --clean, --cancel, --move, or --destroy may be used at a time.",
|
|
330
332
|
);
|
|
331
333
|
process.exit(EXIT_USER);
|
|
332
334
|
}
|
|
@@ -334,6 +336,7 @@ export function parse(argv: string[] = Bun.argv.slice(2)): ParsedArgs {
|
|
|
334
336
|
else if (clean) switchMode = "clean";
|
|
335
337
|
else if (cancel) switchMode = "cancel";
|
|
336
338
|
else if (move) switchMode = "move";
|
|
339
|
+
else if (destroy) switchMode = "destroy";
|
|
337
340
|
}
|
|
338
341
|
|
|
339
342
|
return {
|
package/src/commands/help.ts
CHANGED
|
@@ -51,6 +51,7 @@ List: -r, --include-remote Include remote-tracking branches
|
|
|
51
51
|
Switch: --move Move current changes to the target branch
|
|
52
52
|
--restore Save for this branch; restore target's saved state (if any)
|
|
53
53
|
--clean Discard changes and switch clean at HEAD
|
|
54
|
+
--destroy Delete current branch and switch to target (not main/dev)
|
|
54
55
|
--cancel Abort switching
|
|
55
56
|
|
|
56
57
|
Exit codes: 0 success, 1 usage/validation, 2 Git or system error.
|
package/src/commands/switch.ts
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
import { resolveConfig } from "../config.js";
|
|
7
7
|
import { EXIT_GIT, EXIT_OK, EXIT_USER } from "../constants.js";
|
|
8
|
-
import { NotRepoError } from "../errors.js";
|
|
8
|
+
import { CannotDeleteMainOrDevError, NotRepoError } from "../errors.js";
|
|
9
9
|
import {
|
|
10
10
|
branchList,
|
|
11
11
|
checkout,
|
|
12
12
|
cleanUntracked,
|
|
13
|
+
deleteBranch,
|
|
13
14
|
findStashRefByBranch,
|
|
14
15
|
findStashRefByMessage,
|
|
15
16
|
getCurrentBranch,
|
|
@@ -26,7 +27,7 @@ import { hint, success } from "../out.js";
|
|
|
26
27
|
import type { BranchType, ParsedArgs } from "../types.js";
|
|
27
28
|
|
|
28
29
|
/** User choice when switching with uncommitted changes. */
|
|
29
|
-
type SwitchWhenUncommitted = "cancel" | "restore" | "clean" | "move";
|
|
30
|
+
type SwitchWhenUncommitted = "cancel" | "restore" | "clean" | "move" | "destroy";
|
|
30
31
|
|
|
31
32
|
const BRANCH_TYPES: BranchType[] = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
|
|
32
33
|
|
|
@@ -124,6 +125,10 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
124
125
|
value: "restore" as const,
|
|
125
126
|
},
|
|
126
127
|
{ name: "Clean — Discard changes and switch clean at HEAD", value: "clean" as const },
|
|
128
|
+
{
|
|
129
|
+
name: "Destroy — Delete current branch and switch to the target branch",
|
|
130
|
+
value: "destroy" as const,
|
|
131
|
+
},
|
|
127
132
|
{ name: "Cancel — Abort switching", value: "cancel" as const },
|
|
128
133
|
],
|
|
129
134
|
});
|
|
@@ -139,6 +144,31 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
139
144
|
process.exit(EXIT_USER);
|
|
140
145
|
}
|
|
141
146
|
|
|
147
|
+
let branchToDestroy: string | undefined;
|
|
148
|
+
if (whenUncommitted === "destroy") {
|
|
149
|
+
const currentBranch = await getCurrentBranch(root, gitOpts);
|
|
150
|
+
if (!currentBranch) {
|
|
151
|
+
console.error("gflows switch: cannot destroy when HEAD is detached.");
|
|
152
|
+
process.exit(EXIT_USER);
|
|
153
|
+
}
|
|
154
|
+
if (currentBranch === config.main || currentBranch === config.dev) {
|
|
155
|
+
throw new CannotDeleteMainOrDevError(
|
|
156
|
+
`Cannot destroy the long-lived branch '${currentBranch}'.`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (currentBranch === targetBranch) {
|
|
160
|
+
console.error(
|
|
161
|
+
"gflows switch: cannot destroy current branch when switching to the same branch.",
|
|
162
|
+
);
|
|
163
|
+
process.exit(EXIT_USER);
|
|
164
|
+
}
|
|
165
|
+
branchToDestroy = currentBranch;
|
|
166
|
+
if (!treeClean) {
|
|
167
|
+
await restoreTracked(root, gitOpts);
|
|
168
|
+
await cleanUntracked(root, gitOpts);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
142
172
|
if (!treeClean && whenUncommitted === "move") {
|
|
143
173
|
await stashPushMove(root, gitOpts);
|
|
144
174
|
}
|
|
@@ -210,7 +240,12 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
210
240
|
await tryRestoreTargetStash();
|
|
211
241
|
}
|
|
212
242
|
|
|
213
|
-
if (
|
|
243
|
+
if (branchToDestroy) {
|
|
244
|
+
await deleteBranch(root, branchToDestroy, gitOpts);
|
|
245
|
+
if (!quiet && !dryRun) {
|
|
246
|
+
success(`Deleted branch '${branchToDestroy}' and switched to '${targetBranch}'.`);
|
|
247
|
+
}
|
|
248
|
+
} else if (!quiet && !dryRun) {
|
|
214
249
|
success(`Switched to branch '${targetBranch}'.`);
|
|
215
250
|
hint("Use gflows list to see all workflow branches.");
|
|
216
251
|
}
|
package/src/types.ts
CHANGED
|
@@ -120,5 +120,5 @@ export interface ParsedArgs {
|
|
|
120
120
|
// list
|
|
121
121
|
includeRemote: boolean;
|
|
122
122
|
// switch: explicit mode when uncommitted (overrides prompt)
|
|
123
|
-
switchMode?: "restore" | "clean" | "cancel" | "move";
|
|
123
|
+
switchMode?: "restore" | "clean" | "cancel" | "move" | "destroy";
|
|
124
124
|
}
|