preguito 0.2.0 → 0.2.2
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/cli-sea.cjs +209 -357
- package/dist/cli.mjs +200 -347
- package/package.json +1 -1
- package/README.md +0 -464
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
2
3
|
import { Command } from "commander";
|
|
3
4
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
5
|
import { existsSync } from "node:fs";
|
|
@@ -277,7 +278,7 @@ async function writeConfig(config) {
|
|
|
277
278
|
|
|
278
279
|
//#endregion
|
|
279
280
|
//#region src/commands/commit-parser.ts
|
|
280
|
-
function parsePositionalArgs(args, config) {
|
|
281
|
+
function parsePositionalArgs(args, config, bodyFromFlag) {
|
|
281
282
|
if (args.length === 0) throw new PrequitoError("No arguments provided. Usage: guito c [card_id] [shortcodes] <message...>");
|
|
282
283
|
const context = {};
|
|
283
284
|
let cursor = 0;
|
|
@@ -297,7 +298,8 @@ function parsePositionalArgs(args, config) {
|
|
|
297
298
|
const message = messageParts.join(" ");
|
|
298
299
|
return {
|
|
299
300
|
context: mergeContext(config.defaults, context),
|
|
300
|
-
message
|
|
301
|
+
message,
|
|
302
|
+
body: bodyFromFlag
|
|
301
303
|
};
|
|
302
304
|
}
|
|
303
305
|
function resolveShortcodes(shortcodesStr, config) {
|
|
@@ -326,6 +328,29 @@ function resolveShortcodes(shortcodesStr, config) {
|
|
|
326
328
|
return result;
|
|
327
329
|
}
|
|
328
330
|
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/utils/validation.ts
|
|
333
|
+
const GIT_HASH_PATTERN = /^[a-f0-9]{4,40}$/i;
|
|
334
|
+
const GIT_REF_FORBIDDEN_CHARS = /[\x00-\x1f\x7f ~^:?*[\\]/;
|
|
335
|
+
const GIT_REF_FORBIDDEN_SEQUENCES = /\.\.|\.lock(\/|$)|@\{|\/\//;
|
|
336
|
+
function validateHash(hash) {
|
|
337
|
+
if (!GIT_HASH_PATTERN.test(hash)) throw new PrequitoError(`Invalid git hash: "${hash}". Expected 4-40 hexadecimal characters.`);
|
|
338
|
+
}
|
|
339
|
+
function validateRefName(name, type) {
|
|
340
|
+
if (!name || name.trim() === "") throw new PrequitoError(`${type} name cannot be empty.`);
|
|
341
|
+
if (name.startsWith("-")) throw new PrequitoError(`Invalid ${type} name: "${name}". Cannot start with '-'.`);
|
|
342
|
+
if (name.endsWith(".")) throw new PrequitoError(`Invalid ${type} name: "${name}". Cannot end with '.'.`);
|
|
343
|
+
if (name.endsWith("/")) throw new PrequitoError(`Invalid ${type} name: "${name}". Cannot end with '/'.`);
|
|
344
|
+
if (GIT_REF_FORBIDDEN_CHARS.test(name)) throw new PrequitoError(`Invalid ${type} name: "${name}". Contains forbidden characters.`);
|
|
345
|
+
if (GIT_REF_FORBIDDEN_SEQUENCES.test(name)) throw new PrequitoError(`Invalid ${type} name: "${name}". Contains forbidden pattern.`);
|
|
346
|
+
}
|
|
347
|
+
function validateBranchName(branch) {
|
|
348
|
+
validateRefName(branch, "Branch");
|
|
349
|
+
}
|
|
350
|
+
function validateTagName(tag) {
|
|
351
|
+
validateRefName(tag, "Tag");
|
|
352
|
+
}
|
|
353
|
+
|
|
329
354
|
//#endregion
|
|
330
355
|
//#region src/git/operations.ts
|
|
331
356
|
const execFileAsync = promisify(execFile);
|
|
@@ -374,11 +399,16 @@ async function stageAll() {
|
|
|
374
399
|
await git(["add", "-A"]);
|
|
375
400
|
}
|
|
376
401
|
async function commit(message) {
|
|
377
|
-
|
|
402
|
+
const parts = message.split("\n\n");
|
|
403
|
+
const title = parts[0];
|
|
404
|
+
const body = parts.slice(1).join("\n\n");
|
|
405
|
+
const args = [
|
|
378
406
|
"commit",
|
|
379
407
|
"-m",
|
|
380
|
-
|
|
381
|
-
]
|
|
408
|
+
title
|
|
409
|
+
];
|
|
410
|
+
if (body) args.push("-m", body);
|
|
411
|
+
return (await git(args)).stdout;
|
|
382
412
|
}
|
|
383
413
|
async function commitAmend() {
|
|
384
414
|
return (await git([
|
|
@@ -400,6 +430,7 @@ async function forcePushLease() {
|
|
|
400
430
|
return result.stdout + result.stderr;
|
|
401
431
|
}
|
|
402
432
|
async function checkout(branch) {
|
|
433
|
+
validateBranchName(branch);
|
|
403
434
|
await git(["checkout", branch]);
|
|
404
435
|
}
|
|
405
436
|
async function pull() {
|
|
@@ -407,10 +438,12 @@ async function pull() {
|
|
|
407
438
|
return result.stdout + result.stderr;
|
|
408
439
|
}
|
|
409
440
|
async function rebase(branch) {
|
|
441
|
+
validateBranchName(branch);
|
|
410
442
|
const result = await git(["rebase", branch]);
|
|
411
443
|
return result.stdout + result.stderr;
|
|
412
444
|
}
|
|
413
445
|
async function rebaseInteractiveEdit(hash) {
|
|
446
|
+
validateHash(hash);
|
|
414
447
|
const sedCmd = `sed -i 's/^pick ${hash.slice(0, 7)}/edit ${hash.slice(0, 7)}/'`;
|
|
415
448
|
const result = await git([
|
|
416
449
|
"rebase",
|
|
@@ -437,7 +470,8 @@ async function rebaseInteractive(count) {
|
|
|
437
470
|
`HEAD~${count}`
|
|
438
471
|
]);
|
|
439
472
|
}
|
|
440
|
-
async function pushUpstream
|
|
473
|
+
async function pushUpstream(branch) {
|
|
474
|
+
if (branch !== void 0) validateBranchName(branch);
|
|
441
475
|
const result = await git([
|
|
442
476
|
"push",
|
|
443
477
|
"--set-upstream",
|
|
@@ -447,6 +481,7 @@ async function pushUpstream$1(branch) {
|
|
|
447
481
|
return result.stdout + result.stderr;
|
|
448
482
|
}
|
|
449
483
|
async function commitFixup(hash) {
|
|
484
|
+
validateHash(hash);
|
|
450
485
|
return (await git([
|
|
451
486
|
"commit",
|
|
452
487
|
"--fixup",
|
|
@@ -461,14 +496,17 @@ async function resetSoft(count = 1) {
|
|
|
461
496
|
])).stdout;
|
|
462
497
|
}
|
|
463
498
|
async function createBranch(branch) {
|
|
499
|
+
validateBranchName(branch);
|
|
464
500
|
await git([
|
|
465
501
|
"checkout",
|
|
466
502
|
"-b",
|
|
467
503
|
branch
|
|
468
504
|
]);
|
|
469
505
|
}
|
|
470
|
-
async function stash() {
|
|
471
|
-
|
|
506
|
+
async function stash(message) {
|
|
507
|
+
const args = ["stash"];
|
|
508
|
+
if (message) args.push("-m", message);
|
|
509
|
+
return (await git(args)).stdout;
|
|
472
510
|
}
|
|
473
511
|
async function stashPop() {
|
|
474
512
|
return (await git(["stash", "pop"])).stdout;
|
|
@@ -492,6 +530,7 @@ async function logGrep(keyword, count) {
|
|
|
492
530
|
return (await git(args)).stdout;
|
|
493
531
|
}
|
|
494
532
|
async function logTag(tag) {
|
|
533
|
+
validateTagName(tag);
|
|
495
534
|
return (await git([
|
|
496
535
|
"log",
|
|
497
536
|
"--oneline",
|
|
@@ -499,12 +538,44 @@ async function logTag(tag) {
|
|
|
499
538
|
])).stdout;
|
|
500
539
|
}
|
|
501
540
|
async function logTagAll(tag) {
|
|
541
|
+
validateTagName(tag);
|
|
502
542
|
return (await git([
|
|
503
543
|
"log",
|
|
504
544
|
"--oneline",
|
|
505
545
|
tag
|
|
506
546
|
])).stdout;
|
|
507
547
|
}
|
|
548
|
+
async function diff(options = []) {
|
|
549
|
+
return (await git(["diff", ...options])).stdout;
|
|
550
|
+
}
|
|
551
|
+
async function stashList() {
|
|
552
|
+
return (await git(["stash", "list"])).stdout;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/utils/command.ts
|
|
557
|
+
async function requireGitRepo() {
|
|
558
|
+
if (!await isGitRepo()) throw new PrequitoError("Not inside a git repository.");
|
|
559
|
+
}
|
|
560
|
+
function withErrorHandling(fn) {
|
|
561
|
+
return async (...args) => {
|
|
562
|
+
try {
|
|
563
|
+
await fn(...args);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
if (error instanceof PrequitoError) {
|
|
566
|
+
console.error(`✖ ${error.message}`);
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function parseCount(value, defaultValue = 1) {
|
|
574
|
+
if (value === void 0) return defaultValue;
|
|
575
|
+
const num = parseInt(value, 10);
|
|
576
|
+
if (isNaN(num) || num <= 0) throw new PrequitoError("Count must be a positive integer.");
|
|
577
|
+
return num;
|
|
578
|
+
}
|
|
508
579
|
|
|
509
580
|
//#endregion
|
|
510
581
|
//#region src/utils/spinner.ts
|
|
@@ -551,36 +622,23 @@ function spinner(message, options) {
|
|
|
551
622
|
//#endregion
|
|
552
623
|
//#region src/commands/commit.ts
|
|
553
624
|
function registerCommitCommand(program) {
|
|
554
|
-
program.command("c").alias("commit").description("Templated commit (e.g. guito c 42 f \"msg\" -p)").argument("[args...]", "Card ID, shortcodes, and message").option("-p, --push", "Push after committing").option("-f, --force", "Push with --force-with-lease after committing").option("-d, --dry-run", "Show the generated message without executing").option("-S, --no-stage", "Skip auto-staging (git add -A)").action(async (args, opts) => {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
} catch (error) {
|
|
558
|
-
if (error instanceof PrequitoError) {
|
|
559
|
-
console.error(`\u2716 ${error.message}`);
|
|
560
|
-
process.exit(1);
|
|
561
|
-
}
|
|
562
|
-
throw error;
|
|
563
|
-
}
|
|
564
|
-
});
|
|
625
|
+
program.command("c").alias("commit").description("Templated commit (e.g. guito c 42 f \"msg\" -p)").argument("[args...]", "Card ID, shortcodes, and message").option("-p, --push", "Push after committing").option("-f, --force", "Push with --force-with-lease after committing").option("-d, --dry-run", "Show the generated message without executing").option("-S, --no-stage", "Skip auto-staging (git add -A)").action(withErrorHandling(async (args, opts) => {
|
|
626
|
+
await executeCommit(args, opts);
|
|
627
|
+
}));
|
|
565
628
|
}
|
|
566
629
|
async function executeCommit(args, opts) {
|
|
567
|
-
|
|
568
|
-
console.error("✖ Not inside a git repository.");
|
|
569
|
-
process.exit(1);
|
|
570
|
-
}
|
|
630
|
+
await requireGitRepo();
|
|
571
631
|
const config = await loadConfigOrDefault();
|
|
572
|
-
const { context, message } = parsePositionalArgs(args, config);
|
|
573
|
-
const
|
|
632
|
+
const { context, message, body } = parsePositionalArgs(args, config, typeof opts.body === "string" ? opts.body : void 0);
|
|
633
|
+
const commitTitle = renderTemplate(config.template, context, message);
|
|
634
|
+
const commitMessage = body ? `${commitTitle}\n\n${body}` : commitTitle;
|
|
574
635
|
if (opts.dryRun) {
|
|
575
636
|
console.log(commitMessage);
|
|
576
637
|
return;
|
|
577
638
|
}
|
|
578
639
|
if (opts.stage !== false) await stageAll();
|
|
579
|
-
if (!await hasStagedChanges())
|
|
580
|
-
|
|
581
|
-
process.exit(1);
|
|
582
|
-
}
|
|
583
|
-
const stopCommit = spinner(`Committing: ${commitMessage}`);
|
|
640
|
+
if (!await hasStagedChanges()) throw new PrequitoError("No staged changes to commit.");
|
|
641
|
+
const stopCommit = spinner(`Committing: ${commitTitle}`);
|
|
584
642
|
await commit(commitMessage);
|
|
585
643
|
stopCommit("✔ Committed.");
|
|
586
644
|
if (opts.force) {
|
|
@@ -597,34 +655,11 @@ async function executeCommit(args, opts) {
|
|
|
597
655
|
//#endregion
|
|
598
656
|
//#region src/commands/amend-push.ts
|
|
599
657
|
function registerAmendPushCommands(program) {
|
|
600
|
-
program.command("ap").description("Amend last commit + force push (git push --force)").action(
|
|
601
|
-
|
|
602
|
-
await amendAndPush(false);
|
|
603
|
-
} catch (error) {
|
|
604
|
-
if (error instanceof PrequitoError) {
|
|
605
|
-
console.error(`✖ ${error.message}`);
|
|
606
|
-
process.exit(1);
|
|
607
|
-
}
|
|
608
|
-
throw error;
|
|
609
|
-
}
|
|
610
|
-
});
|
|
611
|
-
program.command("apl").description("Amend last commit + safe force push (--force-with-lease)").action(async () => {
|
|
612
|
-
try {
|
|
613
|
-
await amendAndPush(true);
|
|
614
|
-
} catch (error) {
|
|
615
|
-
if (error instanceof PrequitoError) {
|
|
616
|
-
console.error(`✖ ${error.message}`);
|
|
617
|
-
process.exit(1);
|
|
618
|
-
}
|
|
619
|
-
throw error;
|
|
620
|
-
}
|
|
621
|
-
});
|
|
658
|
+
program.command("ap").description("Amend last commit + force push (git push --force)").action(withErrorHandling(() => amendAndPush(false)));
|
|
659
|
+
program.command("apl").description("Amend last commit + safe force push (--force-with-lease)").action(withErrorHandling(() => amendAndPush(true)));
|
|
622
660
|
}
|
|
623
661
|
async function amendAndPush(useLease) {
|
|
624
|
-
|
|
625
|
-
console.error("✖ Not inside a git repository.");
|
|
626
|
-
process.exit(1);
|
|
627
|
-
}
|
|
662
|
+
await requireGitRepo();
|
|
628
663
|
const stopStage = spinner("Staging all changes...");
|
|
629
664
|
await stageAll();
|
|
630
665
|
stopStage("✔ Staged.");
|
|
@@ -645,45 +680,12 @@ async function amendAndPush(useLease) {
|
|
|
645
680
|
//#endregion
|
|
646
681
|
//#region src/commands/rebase.ts
|
|
647
682
|
function registerRebaseCommands(program) {
|
|
648
|
-
program.command("r <branch>").alias("rebase").description("Rebase on <branch> (checkout, pull, rebase) (e.g. guito r main)").action(
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
} catch (error) {
|
|
652
|
-
if (error instanceof PrequitoError) {
|
|
653
|
-
console.error(`✖ ${error.message}`);
|
|
654
|
-
process.exit(1);
|
|
655
|
-
}
|
|
656
|
-
throw error;
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
program.command("ri <count>").description("Interactive rebase last <count> commits (e.g. guito ri 3)").action(async (count) => {
|
|
660
|
-
try {
|
|
661
|
-
await interactiveRebase(count);
|
|
662
|
-
} catch (error) {
|
|
663
|
-
if (error instanceof PrequitoError) {
|
|
664
|
-
console.error(`✖ ${error.message}`);
|
|
665
|
-
process.exit(1);
|
|
666
|
-
}
|
|
667
|
-
throw error;
|
|
668
|
-
}
|
|
669
|
-
});
|
|
670
|
-
program.command("re <hash>").description("Edit rebase at <hash> (e.g. guito re abc123)").action(async (hash) => {
|
|
671
|
-
try {
|
|
672
|
-
await editRebase(hash);
|
|
673
|
-
} catch (error) {
|
|
674
|
-
if (error instanceof PrequitoError) {
|
|
675
|
-
console.error(`✖ ${error.message}`);
|
|
676
|
-
process.exit(1);
|
|
677
|
-
}
|
|
678
|
-
throw error;
|
|
679
|
-
}
|
|
680
|
-
});
|
|
683
|
+
program.command("r <branch>").alias("rebase").description("Rebase on <branch> (checkout, pull, rebase) (e.g. guito r main)").action(withErrorHandling(quickRebase));
|
|
684
|
+
program.command("ri <count>").description("Interactive rebase last <count> commits (e.g. guito ri 3)").action(withErrorHandling(interactiveRebase));
|
|
685
|
+
program.command("re <hash>").description("Edit rebase at <hash> (e.g. guito re abc123)").action(withErrorHandling(editRebase));
|
|
681
686
|
}
|
|
682
687
|
async function quickRebase(branch) {
|
|
683
|
-
|
|
684
|
-
console.error("✖ Not inside a git repository.");
|
|
685
|
-
process.exit(1);
|
|
686
|
-
}
|
|
688
|
+
await requireGitRepo();
|
|
687
689
|
const currentBranch = await getCurrentBranch();
|
|
688
690
|
console.log(`→ Current branch: ${currentBranch}`);
|
|
689
691
|
let stop = spinner(`Checking out ${branch}...`);
|
|
@@ -700,23 +702,13 @@ async function quickRebase(branch) {
|
|
|
700
702
|
stop("✔ Rebase complete.");
|
|
701
703
|
}
|
|
702
704
|
async function interactiveRebase(count) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
process.exit(1);
|
|
706
|
-
}
|
|
707
|
-
const num = parseInt(count, 10);
|
|
708
|
-
if (isNaN(num) || num <= 0) {
|
|
709
|
-
console.error("✖ Count must be a positive integer.");
|
|
710
|
-
process.exit(1);
|
|
711
|
-
}
|
|
705
|
+
await requireGitRepo();
|
|
706
|
+
const num = parseCount(count);
|
|
712
707
|
console.log(`→ Starting interactive rebase for the last ${num} commit(s)...`);
|
|
713
708
|
await rebaseInteractive(num);
|
|
714
709
|
}
|
|
715
710
|
async function editRebase(hash) {
|
|
716
|
-
|
|
717
|
-
console.error("✖ Not inside a git repository.");
|
|
718
|
-
process.exit(1);
|
|
719
|
-
}
|
|
711
|
+
await requireGitRepo();
|
|
720
712
|
console.log(`→ Starting edit rebase on commit ${hash}...`);
|
|
721
713
|
await rebaseInteractiveEdit(hash);
|
|
722
714
|
console.log("✔ Rebase paused at the target commit. Make your changes, then run:");
|
|
@@ -1037,76 +1029,32 @@ function registerConfigCommand(program) {
|
|
|
1037
1029
|
//#endregion
|
|
1038
1030
|
//#region src/commands/push.ts
|
|
1039
1031
|
function registerPushCommands(program) {
|
|
1040
|
-
program.command("p").alias("push").description("Push current branch (git push)").action(async () => {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
await pushUpstream();
|
|
1054
|
-
} catch (error) {
|
|
1055
|
-
if (error instanceof PrequitoError) {
|
|
1056
|
-
console.error(`✖ ${error.message}`);
|
|
1057
|
-
process.exit(1);
|
|
1058
|
-
}
|
|
1059
|
-
throw error;
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
async function executePush() {
|
|
1064
|
-
if (!await isGitRepo()) {
|
|
1065
|
-
console.error("✖ Not inside a git repository.");
|
|
1066
|
-
process.exit(1);
|
|
1067
|
-
}
|
|
1068
|
-
const stop = spinner("Pushing...");
|
|
1069
|
-
await push();
|
|
1070
|
-
stop("✔ Pushed.");
|
|
1071
|
-
}
|
|
1072
|
-
async function pushUpstream() {
|
|
1073
|
-
if (!await isGitRepo()) {
|
|
1074
|
-
console.error("✖ Not inside a git repository.");
|
|
1075
|
-
process.exit(1);
|
|
1076
|
-
}
|
|
1077
|
-
const branch = await getCurrentBranch();
|
|
1078
|
-
const stop = spinner(`Pushing with --set-upstream origin ${branch}...`);
|
|
1079
|
-
await pushUpstream$1(branch);
|
|
1080
|
-
stop("✔ Pushed.");
|
|
1032
|
+
program.command("p").alias("push").description("Push current branch (git push)").action(withErrorHandling(async () => {
|
|
1033
|
+
await requireGitRepo();
|
|
1034
|
+
const stop = spinner("Pushing...");
|
|
1035
|
+
await push();
|
|
1036
|
+
stop("✔ Pushed.");
|
|
1037
|
+
}));
|
|
1038
|
+
program.command("pu").description("Push + set upstream (git push --set-upstream origin <branch>)").action(withErrorHandling(async () => {
|
|
1039
|
+
await requireGitRepo();
|
|
1040
|
+
const branch = await getCurrentBranch();
|
|
1041
|
+
const stop = spinner(`Pushing with --set-upstream origin ${branch}...`);
|
|
1042
|
+
await pushUpstream(branch);
|
|
1043
|
+
stop("✔ Pushed.");
|
|
1044
|
+
}));
|
|
1081
1045
|
}
|
|
1082
1046
|
|
|
1083
1047
|
//#endregion
|
|
1084
1048
|
//#region src/commands/fixup.ts
|
|
1085
1049
|
function registerFixupCommand(program) {
|
|
1086
|
-
program.command("cf <hash>").description("Fixup commit for <hash> (e.g. guito cf abc123 -f)").option("-p, --push", "Push after creating the fixup commit").option("-f, --force", "Push with --force-with-lease after creating").action(
|
|
1087
|
-
try {
|
|
1088
|
-
await executeFixup(hash, opts);
|
|
1089
|
-
} catch (error) {
|
|
1090
|
-
if (error instanceof PrequitoError) {
|
|
1091
|
-
console.error(`✖ ${error.message}`);
|
|
1092
|
-
process.exit(1);
|
|
1093
|
-
}
|
|
1094
|
-
throw error;
|
|
1095
|
-
}
|
|
1096
|
-
});
|
|
1050
|
+
program.command("cf <hash>").description("Fixup commit for <hash> (e.g. guito cf abc123 -f)").option("-p, --push", "Push after creating the fixup commit").option("-f, --force", "Push with --force-with-lease after creating").action(withErrorHandling(executeFixup));
|
|
1097
1051
|
}
|
|
1098
1052
|
async function executeFixup(hash, opts) {
|
|
1099
|
-
|
|
1100
|
-
console.error("✖ Not inside a git repository.");
|
|
1101
|
-
process.exit(1);
|
|
1102
|
-
}
|
|
1053
|
+
await requireGitRepo();
|
|
1103
1054
|
const stopStage = spinner("Staging all changes...");
|
|
1104
1055
|
await stageAll();
|
|
1105
1056
|
stopStage("✔ Staged.");
|
|
1106
|
-
if (!await hasStagedChanges())
|
|
1107
|
-
console.error("✖ No staged changes to commit.");
|
|
1108
|
-
process.exit(1);
|
|
1109
|
-
}
|
|
1057
|
+
if (!await hasStagedChanges()) throw new PrequitoError("No staged changes to commit.");
|
|
1110
1058
|
const stopCommit = spinner(`Creating fixup commit for ${hash}...`);
|
|
1111
1059
|
await commitFixup(hash);
|
|
1112
1060
|
stopCommit("✔ Fixup commit created.");
|
|
@@ -1124,221 +1072,125 @@ async function executeFixup(hash, opts) {
|
|
|
1124
1072
|
//#endregion
|
|
1125
1073
|
//#region src/commands/undo.ts
|
|
1126
1074
|
function registerUndoCommand(program) {
|
|
1127
|
-
program.command("u [count]").alias("undo").description("Undo last N commits, keep changes staged (e.g. guito u 3)").action(async (count) => {
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
}
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
async function executeUndo(count) {
|
|
1140
|
-
if (!await isGitRepo()) {
|
|
1141
|
-
console.error("✖ Not inside a git repository.");
|
|
1142
|
-
process.exit(1);
|
|
1143
|
-
}
|
|
1144
|
-
const num = count ? parseInt(count, 10) : 1;
|
|
1145
|
-
if (isNaN(num) || num <= 0) {
|
|
1146
|
-
console.error("✖ Count must be a positive integer.");
|
|
1147
|
-
process.exit(1);
|
|
1148
|
-
}
|
|
1149
|
-
const stop = spinner(`Undoing last ${num} commit(s)...`);
|
|
1150
|
-
await resetSoft(num);
|
|
1151
|
-
stop(`✔ Undid last ${num} commit(s). Changes are staged.`);
|
|
1152
|
-
const st = await status();
|
|
1153
|
-
if (st) console.log(st);
|
|
1075
|
+
program.command("u [count]").alias("undo").description("Undo last N commits, keep changes staged (e.g. guito u 3)").action(withErrorHandling(async (count) => {
|
|
1076
|
+
await requireGitRepo();
|
|
1077
|
+
const num = parseCount(count);
|
|
1078
|
+
console.log(`→ Undoing last ${num} commit(s)...`);
|
|
1079
|
+
await resetSoft(num);
|
|
1080
|
+
console.log(`✔ Undid last ${num} commit(s). Changes are staged.`);
|
|
1081
|
+
const st = await status();
|
|
1082
|
+
if (st) console.log(st);
|
|
1083
|
+
}));
|
|
1154
1084
|
}
|
|
1155
1085
|
|
|
1156
1086
|
//#endregion
|
|
1157
1087
|
//#region src/commands/status.ts
|
|
1158
1088
|
function registerStatusCommand(program) {
|
|
1159
|
-
program.command("s").alias("status").description("Short status (git status --short)").action(async () => {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
process.exit(1);
|
|
1166
|
-
}
|
|
1167
|
-
throw error;
|
|
1168
|
-
}
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
async function executeStatus() {
|
|
1172
|
-
if (!await isGitRepo()) {
|
|
1173
|
-
console.error("✖ Not inside a git repository.");
|
|
1174
|
-
process.exit(1);
|
|
1175
|
-
}
|
|
1176
|
-
const output = await status();
|
|
1177
|
-
if (output) console.log(output.trimEnd());
|
|
1178
|
-
else console.log("✨ Nothing to commit, working tree clean.");
|
|
1089
|
+
program.command("s").alias("status").description("Short status (git status --short)").action(withErrorHandling(async () => {
|
|
1090
|
+
await requireGitRepo();
|
|
1091
|
+
const output = await status();
|
|
1092
|
+
if (output) console.log(output.trimEnd());
|
|
1093
|
+
else console.log("✨ Nothing to commit, working tree clean.");
|
|
1094
|
+
}));
|
|
1179
1095
|
}
|
|
1180
1096
|
|
|
1181
1097
|
//#endregion
|
|
1182
1098
|
//#region src/commands/switch.ts
|
|
1183
1099
|
function registerSwitchCommand(program) {
|
|
1184
|
-
program.command("sw <branch>").alias("switch").description("Switch branch, -n to create (e.g. guito sw -n feature/x)").option("-n, --new", "Create a new branch").action(async (branch, opts) => {
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
throw error;
|
|
1100
|
+
program.command("sw <branch>").alias("switch").description("Switch branch, -n to create (e.g. guito sw -n feature/x)").option("-n, --new", "Create a new branch").action(withErrorHandling(async (branch, opts) => {
|
|
1101
|
+
await requireGitRepo();
|
|
1102
|
+
if (opts.new) {
|
|
1103
|
+
console.log(`→ Creating and switching to ${branch}...`);
|
|
1104
|
+
await createBranch(branch);
|
|
1105
|
+
} else {
|
|
1106
|
+
console.log(`→ Switching to ${branch}...`);
|
|
1107
|
+
await checkout(branch);
|
|
1193
1108
|
}
|
|
1194
|
-
|
|
1195
|
-
}
|
|
1196
|
-
async function executeSwitch(branch, opts) {
|
|
1197
|
-
if (!await isGitRepo()) {
|
|
1198
|
-
console.error("✖ Not inside a git repository.");
|
|
1199
|
-
process.exit(1);
|
|
1200
|
-
}
|
|
1201
|
-
const stop = spinner(opts.new ? `Creating and switching to ${branch}...` : `Switching to ${branch}...`);
|
|
1202
|
-
if (opts.new) await createBranch(branch);
|
|
1203
|
-
else await checkout(branch);
|
|
1204
|
-
stop(`✔ On branch ${branch}.`);
|
|
1109
|
+
console.log(`✔ On branch ${branch}.`);
|
|
1110
|
+
}));
|
|
1205
1111
|
}
|
|
1206
1112
|
|
|
1207
1113
|
//#endregion
|
|
1208
1114
|
//#region src/commands/stash.ts
|
|
1209
1115
|
function registerStashCommands(program) {
|
|
1210
|
-
program.command("st").description("Stash all changes (git stash)").action(async () => {
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
throw error;
|
|
1230
|
-
}
|
|
1231
|
-
});
|
|
1232
|
-
}
|
|
1233
|
-
async function executeStash() {
|
|
1234
|
-
if (!await isGitRepo()) {
|
|
1235
|
-
console.error("✖ Not inside a git repository.");
|
|
1236
|
-
process.exit(1);
|
|
1237
|
-
}
|
|
1238
|
-
const stop = spinner("Stashing changes...");
|
|
1239
|
-
await stash();
|
|
1240
|
-
stop("✔ Stashed.");
|
|
1241
|
-
}
|
|
1242
|
-
async function executeStashPop() {
|
|
1243
|
-
if (!await isGitRepo()) {
|
|
1244
|
-
console.error("✖ Not inside a git repository.");
|
|
1245
|
-
process.exit(1);
|
|
1246
|
-
}
|
|
1247
|
-
const stop = spinner("Restoring stashed changes...");
|
|
1248
|
-
await stashPop();
|
|
1249
|
-
stop("✔ Restored.");
|
|
1116
|
+
program.command("st").description("Stash all changes (git stash)").option("-m, --message <message>", "Stash with a descriptive message").action(withErrorHandling(async (opts) => {
|
|
1117
|
+
await requireGitRepo();
|
|
1118
|
+
if (opts.message) console.log(`→ Stashing changes: ${opts.message}...`);
|
|
1119
|
+
else console.log("→ Stashing changes...");
|
|
1120
|
+
await stash(opts.message);
|
|
1121
|
+
console.log("✔ Stashed.");
|
|
1122
|
+
}));
|
|
1123
|
+
program.command("stp").description("Pop the latest stash (git stash pop)").action(withErrorHandling(async () => {
|
|
1124
|
+
await requireGitRepo();
|
|
1125
|
+
console.log("→ Restoring stashed changes...");
|
|
1126
|
+
await stashPop();
|
|
1127
|
+
console.log("✔ Restored.");
|
|
1128
|
+
}));
|
|
1129
|
+
program.command("stl").description("List all stashes (git stash list)").action(withErrorHandling(async () => {
|
|
1130
|
+
await requireGitRepo();
|
|
1131
|
+
const output = await stashList();
|
|
1132
|
+
if (output) console.log(output.trimEnd());
|
|
1133
|
+
else console.log("✨ No stashes found.");
|
|
1134
|
+
}));
|
|
1250
1135
|
}
|
|
1251
1136
|
|
|
1252
1137
|
//#endregion
|
|
1253
1138
|
//#region src/commands/log.ts
|
|
1254
1139
|
function registerLogCommand(program) {
|
|
1255
|
-
program.command("l [count]").alias("log").description("Compact log, default last 10 (e.g. guito l 20)").action(async (count) => {
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1263
|
-
throw error;
|
|
1264
|
-
}
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
async function executeLog(count) {
|
|
1268
|
-
if (!await isGitRepo()) {
|
|
1269
|
-
console.error("✖ Not inside a git repository.");
|
|
1270
|
-
process.exit(1);
|
|
1271
|
-
}
|
|
1272
|
-
const num = count ? parseInt(count, 10) : 10;
|
|
1273
|
-
if (isNaN(num) || num <= 0) {
|
|
1274
|
-
console.error("✖ Count must be a positive integer.");
|
|
1275
|
-
process.exit(1);
|
|
1276
|
-
}
|
|
1277
|
-
const output = await logOneline(num);
|
|
1278
|
-
if (output) console.log(output.trimEnd());
|
|
1279
|
-
else console.log("✨ No commits found.");
|
|
1140
|
+
program.command("l [count]").alias("log").description("Compact log, default last 10 (e.g. guito l 20)").action(withErrorHandling(async (count) => {
|
|
1141
|
+
await requireGitRepo();
|
|
1142
|
+
const num = parseCount(count, 10);
|
|
1143
|
+
const output = await logOneline(num);
|
|
1144
|
+
if (output) console.log(output.trimEnd());
|
|
1145
|
+
else console.log("✨ No commits found.");
|
|
1146
|
+
}));
|
|
1280
1147
|
}
|
|
1281
1148
|
|
|
1282
1149
|
//#endregion
|
|
1283
1150
|
//#region src/commands/find.ts
|
|
1284
1151
|
function registerFindCommands(program) {
|
|
1285
|
-
program.command("f <keyword>").alias("find").description("Search commits by message (e.g. guito f \"login\")").option("-n, --number <count>", "Limit number of results").action(async (keyword, opts) => {
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
throw error;
|
|
1152
|
+
program.command("f <keyword>").alias("find").description("Search commits by message (e.g. guito f \"login\")").option("-n, --number <count>", "Limit number of results").action(withErrorHandling(async (keyword, opts) => {
|
|
1153
|
+
await requireGitRepo();
|
|
1154
|
+
const count = opts.number ? parseInt(opts.number, 10) : void 0;
|
|
1155
|
+
const output = await logGrep(keyword, count);
|
|
1156
|
+
if (output) console.log(output.trimEnd());
|
|
1157
|
+
else console.log(`✨ No commits found matching "${keyword}".`);
|
|
1158
|
+
}));
|
|
1159
|
+
program.command("t <tag>").alias("tag").description("Commits since <tag> (e.g. guito t v1.0.0)").option("-a, --all", "Show all commits reachable from the tag").action(withErrorHandling(async (tag, opts) => {
|
|
1160
|
+
await requireGitRepo();
|
|
1161
|
+
if (opts.all) {
|
|
1162
|
+
console.log(`🏷️ Commits reachable from ${tag}:`);
|
|
1163
|
+
const output = await logTagAll(tag);
|
|
1164
|
+
if (output) console.log(output.trimEnd());
|
|
1165
|
+
else console.log("✨ No commits found.");
|
|
1166
|
+
} else {
|
|
1167
|
+
console.log(`🏷️ Commits since ${tag}:`);
|
|
1168
|
+
const output = await logTag(tag);
|
|
1169
|
+
if (output) console.log(output.trimEnd());
|
|
1170
|
+
else console.log("✨ No commits since this tag.");
|
|
1305
1171
|
}
|
|
1306
|
-
});
|
|
1172
|
+
}));
|
|
1307
1173
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
}
|
|
1320
|
-
async function executeTag(tag, opts) {
|
|
1321
|
-
if (!await isGitRepo()) {
|
|
1322
|
-
console.error("✖ Not inside a git repository.");
|
|
1323
|
-
process.exit(1);
|
|
1324
|
-
}
|
|
1325
|
-
if (opts.all) {
|
|
1326
|
-
console.log(`🏷️ Commits reachable from ${tag}:`);
|
|
1327
|
-
const output = await logTagAll(tag);
|
|
1328
|
-
if (output) console.log(output.trimEnd());
|
|
1329
|
-
else console.log("✨ No commits found.");
|
|
1330
|
-
} else {
|
|
1331
|
-
console.log(`🏷️ Commits since ${tag}:`);
|
|
1332
|
-
const output = await logTag(tag);
|
|
1174
|
+
|
|
1175
|
+
//#endregion
|
|
1176
|
+
//#region src/commands/diff.ts
|
|
1177
|
+
function registerDiffCommand(program) {
|
|
1178
|
+
program.command("d").alias("diff").description("Show changes (git diff)").option("-s, --staged", "Show staged changes only").option("--stat", "Show diffstat summary").option("-n, --name-only", "Show only names of changed files").action(withErrorHandling(async (opts) => {
|
|
1179
|
+
await requireGitRepo();
|
|
1180
|
+
const options = [];
|
|
1181
|
+
if (opts.staged) options.push("--staged");
|
|
1182
|
+
if (opts.stat) options.push("--stat");
|
|
1183
|
+
if (opts.nameOnly) options.push("--name-only");
|
|
1184
|
+
const output = await diff(options);
|
|
1333
1185
|
if (output) console.log(output.trimEnd());
|
|
1334
|
-
else console.log("✨ No
|
|
1335
|
-
}
|
|
1186
|
+
else console.log("✨ No changes.");
|
|
1187
|
+
}));
|
|
1336
1188
|
}
|
|
1337
1189
|
|
|
1338
1190
|
//#endregion
|
|
1339
1191
|
//#region src/cli.ts
|
|
1340
1192
|
const program = new Command();
|
|
1341
|
-
program.name("guito").description("preguito - a lazy git CLI with commit templates and shortcuts").version("0.
|
|
1193
|
+
program.name("guito").description("preguito - a lazy git CLI with commit templates and shortcuts").version("0.2.2").addHelpText("before", "\n🦥 preguito v0.1.0\n A lazy git CLI with commit templates and shortcuts.\n");
|
|
1342
1194
|
registerCommitCommand(program);
|
|
1343
1195
|
registerAmendPushCommands(program);
|
|
1344
1196
|
registerRebaseCommands(program);
|
|
@@ -1352,6 +1204,7 @@ registerSwitchCommand(program);
|
|
|
1352
1204
|
registerStashCommands(program);
|
|
1353
1205
|
registerLogCommand(program);
|
|
1354
1206
|
registerFindCommands(program);
|
|
1207
|
+
registerDiffCommand(program);
|
|
1355
1208
|
program.parse();
|
|
1356
1209
|
|
|
1357
1210
|
//#endregion
|