@coder11125/omg 0.3.0 → 0.3.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.
- package/README.md +170 -81
- package/dist/index.js +262 -210
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,14 +39,62 @@ function validateConfigKey(key) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
const ERROR_MAPPINGS = [
|
|
43
|
+
{ pattern: /not a git repository/i, command: 'omg init' },
|
|
44
|
+
{ pattern: /no upstream branch/i, command: 'omg push -u origin <branch>' },
|
|
45
|
+
{ pattern: /merge conflict/i, command: 'omg merge --abort', context: 'or resolve conflicts manually' },
|
|
46
|
+
{ pattern: /rebase conflict/i, command: 'omg rebase --abort', context: 'or use --continue after resolving' },
|
|
47
|
+
{ pattern: /not fully merged/i, command: 'omg branch -D <branch>', context: 'force delete unmerged branch' },
|
|
48
|
+
{ pattern: /no remote/i, command: 'omg remote <url>' },
|
|
49
|
+
{ pattern: /uncommitted changes/i, command: 'omg -c "message"', context: 'stage and commit first' },
|
|
50
|
+
{ pattern: /local changes/i, command: 'omg stash', context: 'stash changes first' },
|
|
51
|
+
{ pattern: /no stash entries/i, command: 'omg stash', context: 'create a stash first' },
|
|
52
|
+
{ pattern: /already exists/i, command: 'omg doctor', context: 'check for issues' },
|
|
53
|
+
{ pattern: /detached HEAD/i, command: 'omg oops restore-branch' },
|
|
54
|
+
{ pattern: /conflict/i, command: 'omg doctor', context: 'resolve conflicts' },
|
|
55
|
+
{ pattern: /permission denied/i, command: 'omg doctor' },
|
|
56
|
+
{ pattern: /could not resolve host/i, command: 'omg doctor', context: 'check network/remote' },
|
|
57
|
+
];
|
|
58
|
+
let verboseMode = false;
|
|
59
|
+
function setVerbose(verbose) {
|
|
60
|
+
verboseMode = verbose;
|
|
61
|
+
}
|
|
62
|
+
function handleNerdError(err, context) {
|
|
63
|
+
const rawMessage = formatError(err);
|
|
64
|
+
// Find matching error pattern
|
|
65
|
+
for (const mapping of ERROR_MAPPINGS) {
|
|
66
|
+
if (mapping.pattern.test(rawMessage)) {
|
|
67
|
+
const cmd = context ? mapping.command.replace('<branch>', context) : mapping.command;
|
|
68
|
+
let output = `${chalk.magenta('(OMG)')} 🤓 Nerd Error hidden: Run ${chalk.cyan(cmd)} to solve it`;
|
|
69
|
+
if (mapping.context) {
|
|
70
|
+
output += chalk.dim(` (${mapping.context})`);
|
|
71
|
+
}
|
|
72
|
+
console.error(output);
|
|
73
|
+
if (verboseMode) {
|
|
74
|
+
console.error(chalk.dim('\nDetails:'));
|
|
75
|
+
console.error(chalk.dim(rawMessage));
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Fallback for unmapped errors
|
|
81
|
+
console.error(`${chalk.magenta('(OMG)')} 🤓 Nerd Error hidden: Run ${chalk.cyan('omg doctor')} to diagnose`);
|
|
82
|
+
if (verboseMode) {
|
|
83
|
+
console.error(chalk.dim('\nDetails:'));
|
|
84
|
+
console.error(chalk.dim(rawMessage));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
42
87
|
const program = new Command();
|
|
43
88
|
program
|
|
44
89
|
.name('omg')
|
|
45
90
|
.description('Oh My Git - a friendly CLI wrapper for common git tasks')
|
|
46
91
|
.version(PACKAGE_VERSION, '-V, --version', 'output the current version')
|
|
47
|
-
.option('
|
|
92
|
+
.option('--verbose', 'show detailed error output (show nerd errors)')
|
|
93
|
+
.option('--visit <branch>', 'checkout the specified branch')
|
|
48
94
|
.option('-c, --commit <message>', 'stage all changes and commit with a message')
|
|
49
95
|
.action(async (opts) => {
|
|
96
|
+
// Set verbose mode globally
|
|
97
|
+
setVerbose(opts.verbose ?? false);
|
|
50
98
|
let didSomething = false;
|
|
51
99
|
if (opts.visit) {
|
|
52
100
|
didSomething = true;
|
|
@@ -60,6 +108,13 @@ program
|
|
|
60
108
|
program.help();
|
|
61
109
|
}
|
|
62
110
|
});
|
|
111
|
+
// Hook to set verbose mode before any command runs
|
|
112
|
+
program.hook('preAction', (thisCommand) => {
|
|
113
|
+
const opts = thisCommand.opts();
|
|
114
|
+
if (opts.verbose !== undefined) {
|
|
115
|
+
setVerbose(opts.verbose);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
63
118
|
// ---------------------------------------------------------------------------
|
|
64
119
|
// branch subcommand
|
|
65
120
|
// ---------------------------------------------------------------------------
|
|
@@ -369,7 +424,7 @@ async function checkoutBranch(branch) {
|
|
|
369
424
|
}
|
|
370
425
|
catch (err) {
|
|
371
426
|
spinner.fail(chalk.red(`Failed to checkout '${branch}'`));
|
|
372
|
-
|
|
427
|
+
handleNerdError(err);
|
|
373
428
|
process.exitCode = 1;
|
|
374
429
|
}
|
|
375
430
|
}
|
|
@@ -384,7 +439,7 @@ async function stageAndCommit(message) {
|
|
|
384
439
|
}
|
|
385
440
|
catch (err) {
|
|
386
441
|
spinner.fail(chalk.red('Commit failed'));
|
|
387
|
-
|
|
442
|
+
handleNerdError(err);
|
|
388
443
|
process.exitCode = 1;
|
|
389
444
|
}
|
|
390
445
|
}
|
|
@@ -415,7 +470,7 @@ async function listBranches() {
|
|
|
415
470
|
}
|
|
416
471
|
catch (err) {
|
|
417
472
|
spinner.fail(chalk.red('Could not list branches'));
|
|
418
|
-
|
|
473
|
+
handleNerdError(err);
|
|
419
474
|
process.exitCode = 1;
|
|
420
475
|
}
|
|
421
476
|
}
|
|
@@ -446,7 +501,7 @@ async function createBranch(name, switchAfter) {
|
|
|
446
501
|
}
|
|
447
502
|
catch (err) {
|
|
448
503
|
spinner.fail(chalk.red(`Failed to create branch '${name}'`));
|
|
449
|
-
|
|
504
|
+
handleNerdError(err);
|
|
450
505
|
process.exitCode = 1;
|
|
451
506
|
}
|
|
452
507
|
}
|
|
@@ -474,7 +529,7 @@ async function deleteBranch(name) {
|
|
|
474
529
|
}
|
|
475
530
|
else {
|
|
476
531
|
spinner.fail(chalk.red(`Failed to delete '${name}'`));
|
|
477
|
-
|
|
532
|
+
handleNerdError(err, name);
|
|
478
533
|
}
|
|
479
534
|
process.exitCode = 1;
|
|
480
535
|
}
|
|
@@ -501,7 +556,7 @@ async function listRemotes() {
|
|
|
501
556
|
}
|
|
502
557
|
catch (err) {
|
|
503
558
|
spinner.fail(chalk.red('Could not list remotes'));
|
|
504
|
-
|
|
559
|
+
handleNerdError(err);
|
|
505
560
|
process.exitCode = 1;
|
|
506
561
|
}
|
|
507
562
|
}
|
|
@@ -522,7 +577,7 @@ async function addRemote(url, name) {
|
|
|
522
577
|
}
|
|
523
578
|
catch (err) {
|
|
524
579
|
spinner.fail(chalk.red(`Failed to add remote '${name}'`));
|
|
525
|
-
|
|
580
|
+
handleNerdError(err);
|
|
526
581
|
process.exitCode = 1;
|
|
527
582
|
}
|
|
528
583
|
}
|
|
@@ -576,7 +631,7 @@ async function showStatus() {
|
|
|
576
631
|
}
|
|
577
632
|
catch (err) {
|
|
578
633
|
spinner.fail(chalk.red('Could not fetch status'));
|
|
579
|
-
|
|
634
|
+
handleNerdError(err);
|
|
580
635
|
process.exitCode = 1;
|
|
581
636
|
}
|
|
582
637
|
}
|
|
@@ -609,9 +664,10 @@ async function pushCommits(remote, force = false, setUpstream) {
|
|
|
609
664
|
const msg = formatError(err);
|
|
610
665
|
if (msg.includes('no upstream branch')) {
|
|
611
666
|
console.error(chalk.red('No upstream configured. Use -u flag to set upstream.'));
|
|
667
|
+
handleNerdError(err);
|
|
612
668
|
}
|
|
613
669
|
else {
|
|
614
|
-
|
|
670
|
+
handleNerdError(err);
|
|
615
671
|
}
|
|
616
672
|
process.exitCode = 1;
|
|
617
673
|
}
|
|
@@ -639,16 +695,7 @@ async function pullChanges(remote, rebase = false) {
|
|
|
639
695
|
}
|
|
640
696
|
catch (err) {
|
|
641
697
|
spinner.fail(chalk.red('Pull failed'));
|
|
642
|
-
|
|
643
|
-
if (msg.includes('merge conflicts')) {
|
|
644
|
-
console.error(chalk.red('Merge conflicts detected. Resolve conflicts and commit.'));
|
|
645
|
-
}
|
|
646
|
-
else if (msg.includes('local changes')) {
|
|
647
|
-
console.error(chalk.red('You have uncommitted changes. Stash or commit them first.'));
|
|
648
|
-
}
|
|
649
|
-
else {
|
|
650
|
-
console.error(chalk.red(msg));
|
|
651
|
-
}
|
|
698
|
+
handleNerdError(err);
|
|
652
699
|
process.exitCode = 1;
|
|
653
700
|
}
|
|
654
701
|
}
|
|
@@ -674,14 +721,11 @@ async function mergeBranch(branch, squash = false) {
|
|
|
674
721
|
catch (err) {
|
|
675
722
|
spinner.fail(chalk.red('Merge failed'));
|
|
676
723
|
const msg = formatError(err);
|
|
677
|
-
if (msg.includes('
|
|
678
|
-
console.error(chalk.red('Merge conflicts detected. Resolve conflicts and commit, or use --abort to cancel.'));
|
|
679
|
-
}
|
|
680
|
-
else if (msg.includes('already up to date')) {
|
|
724
|
+
if (msg.includes('already up to date')) {
|
|
681
725
|
console.log(chalk.yellow('Already up to date.'));
|
|
682
726
|
}
|
|
683
727
|
else {
|
|
684
|
-
|
|
728
|
+
handleNerdError(err);
|
|
685
729
|
}
|
|
686
730
|
process.exitCode = 1;
|
|
687
731
|
}
|
|
@@ -694,13 +738,7 @@ async function abortMerge() {
|
|
|
694
738
|
}
|
|
695
739
|
catch (err) {
|
|
696
740
|
spinner.fail(chalk.red('Failed to abort merge'));
|
|
697
|
-
|
|
698
|
-
if (msg.includes('no merge')) {
|
|
699
|
-
console.error(chalk.red('No merge in progress'));
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
console.error(chalk.red(msg));
|
|
703
|
-
}
|
|
741
|
+
handleNerdError(err);
|
|
704
742
|
process.exitCode = 1;
|
|
705
743
|
}
|
|
706
744
|
}
|
|
@@ -716,13 +754,7 @@ async function rebaseBranch(branch) {
|
|
|
716
754
|
}
|
|
717
755
|
catch (err) {
|
|
718
756
|
spinner.fail(chalk.red('Rebase failed'));
|
|
719
|
-
|
|
720
|
-
if (msg.includes('conflicts')) {
|
|
721
|
-
console.error(chalk.red('Rebase conflicts detected. Resolve conflicts, then use --continue or --abort.'));
|
|
722
|
-
}
|
|
723
|
-
else {
|
|
724
|
-
console.error(chalk.red(msg));
|
|
725
|
-
}
|
|
757
|
+
handleNerdError(err);
|
|
726
758
|
process.exitCode = 1;
|
|
727
759
|
}
|
|
728
760
|
}
|
|
@@ -734,16 +766,7 @@ async function continueRebase() {
|
|
|
734
766
|
}
|
|
735
767
|
catch (err) {
|
|
736
768
|
spinner.fail(chalk.red('Failed to continue rebase'));
|
|
737
|
-
|
|
738
|
-
if (msg.includes('no rebase')) {
|
|
739
|
-
console.error(chalk.red('No rebase in progress'));
|
|
740
|
-
}
|
|
741
|
-
else if (msg.includes('conflicts')) {
|
|
742
|
-
console.error(chalk.red('Unresolved conflicts remain. Resolve them first.'));
|
|
743
|
-
}
|
|
744
|
-
else {
|
|
745
|
-
console.error(chalk.red(msg));
|
|
746
|
-
}
|
|
769
|
+
handleNerdError(err);
|
|
747
770
|
process.exitCode = 1;
|
|
748
771
|
}
|
|
749
772
|
}
|
|
@@ -755,13 +778,7 @@ async function abortRebase() {
|
|
|
755
778
|
}
|
|
756
779
|
catch (err) {
|
|
757
780
|
spinner.fail(chalk.red('Failed to abort rebase'));
|
|
758
|
-
|
|
759
|
-
if (msg.includes('no rebase')) {
|
|
760
|
-
console.error(chalk.red('No rebase in progress'));
|
|
761
|
-
}
|
|
762
|
-
else {
|
|
763
|
-
console.error(chalk.red(msg));
|
|
764
|
-
}
|
|
781
|
+
handleNerdError(err);
|
|
765
782
|
process.exitCode = 1;
|
|
766
783
|
}
|
|
767
784
|
}
|
|
@@ -795,7 +812,7 @@ async function showLog(count, oneline) {
|
|
|
795
812
|
}
|
|
796
813
|
catch (err) {
|
|
797
814
|
spinner.fail(chalk.red('Failed to fetch log'));
|
|
798
|
-
|
|
815
|
+
handleNerdError(err);
|
|
799
816
|
process.exitCode = 1;
|
|
800
817
|
}
|
|
801
818
|
}
|
|
@@ -848,7 +865,7 @@ async function showDiff(file, staged) {
|
|
|
848
865
|
}
|
|
849
866
|
catch (err) {
|
|
850
867
|
spinner.fail(chalk.red('Failed to fetch diff'));
|
|
851
|
-
|
|
868
|
+
handleNerdError(err);
|
|
852
869
|
process.exitCode = 1;
|
|
853
870
|
}
|
|
854
871
|
}
|
|
@@ -874,16 +891,7 @@ async function cloneRepo(url, directory) {
|
|
|
874
891
|
}
|
|
875
892
|
catch (err) {
|
|
876
893
|
spinner.fail(chalk.red('Clone failed'));
|
|
877
|
-
|
|
878
|
-
if (msg.includes('already exists')) {
|
|
879
|
-
console.error(chalk.red(`Directory '${targetDir}' already exists.`));
|
|
880
|
-
}
|
|
881
|
-
else if (msg.includes('not found') || msg.includes('does not exist')) {
|
|
882
|
-
console.error(chalk.red('Repository not found. Check the URL.'));
|
|
883
|
-
}
|
|
884
|
-
else {
|
|
885
|
-
console.error(chalk.red(msg));
|
|
886
|
-
}
|
|
894
|
+
handleNerdError(err);
|
|
887
895
|
process.exitCode = 1;
|
|
888
896
|
}
|
|
889
897
|
}
|
|
@@ -927,7 +935,7 @@ async function stashSave() {
|
|
|
927
935
|
}
|
|
928
936
|
catch (err) {
|
|
929
937
|
spinner.fail(chalk.red('Failed to stash changes'));
|
|
930
|
-
|
|
938
|
+
handleNerdError(err);
|
|
931
939
|
process.exitCode = 1;
|
|
932
940
|
}
|
|
933
941
|
}
|
|
@@ -939,16 +947,7 @@ async function stashPop() {
|
|
|
939
947
|
}
|
|
940
948
|
catch (err) {
|
|
941
949
|
spinner.fail(chalk.red('Failed to pop stash'));
|
|
942
|
-
|
|
943
|
-
if (msg.includes('No stash entries')) {
|
|
944
|
-
console.error(chalk.red('No stash entries found'));
|
|
945
|
-
}
|
|
946
|
-
else if (msg.includes('conflicts')) {
|
|
947
|
-
console.error(chalk.red('Conflicts when applying stash. Resolve conflicts manually.'));
|
|
948
|
-
}
|
|
949
|
-
else {
|
|
950
|
-
console.error(chalk.red(msg));
|
|
951
|
-
}
|
|
950
|
+
handleNerdError(err);
|
|
952
951
|
process.exitCode = 1;
|
|
953
952
|
}
|
|
954
953
|
}
|
|
@@ -970,7 +969,7 @@ async function stashList() {
|
|
|
970
969
|
}
|
|
971
970
|
catch (err) {
|
|
972
971
|
spinner.fail(chalk.red('Failed to list stashes'));
|
|
973
|
-
|
|
972
|
+
handleNerdError(err);
|
|
974
973
|
process.exitCode = 1;
|
|
975
974
|
}
|
|
976
975
|
}
|
|
@@ -989,13 +988,7 @@ async function stashDrop(index) {
|
|
|
989
988
|
}
|
|
990
989
|
catch (err) {
|
|
991
990
|
spinner.fail(chalk.red(`Failed to drop ${stashRef}`));
|
|
992
|
-
|
|
993
|
-
if (msg.includes('Invalid reflog')) {
|
|
994
|
-
console.error(chalk.red(`Invalid stash index: ${index ?? 0}`));
|
|
995
|
-
}
|
|
996
|
-
else {
|
|
997
|
-
console.error(chalk.red(msg));
|
|
998
|
-
}
|
|
991
|
+
handleNerdError(err, index ?? '0');
|
|
999
992
|
process.exitCode = 1;
|
|
1000
993
|
}
|
|
1001
994
|
}
|
|
@@ -1014,16 +1007,7 @@ async function stashApply(index) {
|
|
|
1014
1007
|
}
|
|
1015
1008
|
catch (err) {
|
|
1016
1009
|
spinner.fail(chalk.red(`Failed to apply ${stashRef}`));
|
|
1017
|
-
|
|
1018
|
-
if (msg.includes('Invalid reflog')) {
|
|
1019
|
-
console.error(chalk.red(`Invalid stash index: ${index ?? 0}`));
|
|
1020
|
-
}
|
|
1021
|
-
else if (msg.includes('conflicts')) {
|
|
1022
|
-
console.error(chalk.red('Conflicts when applying stash. Resolve conflicts manually.'));
|
|
1023
|
-
}
|
|
1024
|
-
else {
|
|
1025
|
-
console.error(chalk.red(msg));
|
|
1026
|
-
}
|
|
1010
|
+
handleNerdError(err, index ?? '0');
|
|
1027
1011
|
process.exitCode = 1;
|
|
1028
1012
|
}
|
|
1029
1013
|
}
|
|
@@ -1065,17 +1049,7 @@ async function performUpdate(version) {
|
|
|
1065
1049
|
}
|
|
1066
1050
|
catch (err) {
|
|
1067
1051
|
spinner.fail(chalk.red('Update failed'));
|
|
1068
|
-
|
|
1069
|
-
if (msg.includes('EACCES') || msg.includes('permission')) {
|
|
1070
|
-
console.error(chalk.red('Permission denied. Try running with sudo:'));
|
|
1071
|
-
console.error(chalk.yellow(' sudo omg update'));
|
|
1072
|
-
}
|
|
1073
|
-
else if (msg.includes('npm')) {
|
|
1074
|
-
console.error(chalk.red('npm command failed. Is npm installed?'));
|
|
1075
|
-
}
|
|
1076
|
-
else {
|
|
1077
|
-
console.error(chalk.red(msg));
|
|
1078
|
-
}
|
|
1052
|
+
handleNerdError(err);
|
|
1079
1053
|
process.exitCode = 1;
|
|
1080
1054
|
}
|
|
1081
1055
|
}
|
|
@@ -1121,7 +1095,7 @@ async function initRepo(directory, message) {
|
|
|
1121
1095
|
}
|
|
1122
1096
|
catch (err) {
|
|
1123
1097
|
spinner.fail(chalk.red('Failed to initialize repository'));
|
|
1124
|
-
|
|
1098
|
+
handleNerdError(err);
|
|
1125
1099
|
process.exitCode = 1;
|
|
1126
1100
|
}
|
|
1127
1101
|
}
|
|
@@ -1143,13 +1117,7 @@ async function createTag(name, message) {
|
|
|
1143
1117
|
}
|
|
1144
1118
|
catch (err) {
|
|
1145
1119
|
spinner.fail(chalk.red(`Failed to create tag '${name}'`));
|
|
1146
|
-
|
|
1147
|
-
if (msg.includes('already exists')) {
|
|
1148
|
-
console.error(chalk.red(`Tag '${name}' already exists`));
|
|
1149
|
-
}
|
|
1150
|
-
else {
|
|
1151
|
-
console.error(chalk.red(msg));
|
|
1152
|
-
}
|
|
1120
|
+
handleNerdError(err, name);
|
|
1153
1121
|
process.exitCode = 1;
|
|
1154
1122
|
}
|
|
1155
1123
|
}
|
|
@@ -1170,7 +1138,7 @@ async function listTags() {
|
|
|
1170
1138
|
}
|
|
1171
1139
|
catch (err) {
|
|
1172
1140
|
spinner.fail(chalk.red('Failed to list tags'));
|
|
1173
|
-
|
|
1141
|
+
handleNerdError(err);
|
|
1174
1142
|
process.exitCode = 1;
|
|
1175
1143
|
}
|
|
1176
1144
|
}
|
|
@@ -1193,16 +1161,7 @@ async function fetchChanges(remote) {
|
|
|
1193
1161
|
}
|
|
1194
1162
|
catch (err) {
|
|
1195
1163
|
spinner.fail(chalk.red('Fetch failed'));
|
|
1196
|
-
|
|
1197
|
-
if (msg.includes('not a git repository')) {
|
|
1198
|
-
console.error(chalk.red('Not a git repository'));
|
|
1199
|
-
}
|
|
1200
|
-
else if (msg.includes('no remote')) {
|
|
1201
|
-
console.error(chalk.red('No remote configured. Add one with: omg remote <url>'));
|
|
1202
|
-
}
|
|
1203
|
-
else {
|
|
1204
|
-
console.error(chalk.red(msg));
|
|
1205
|
-
}
|
|
1164
|
+
handleNerdError(err);
|
|
1206
1165
|
process.exitCode = 1;
|
|
1207
1166
|
}
|
|
1208
1167
|
}
|
|
@@ -1232,7 +1191,7 @@ async function resetChanges(mode) {
|
|
|
1232
1191
|
}
|
|
1233
1192
|
catch (err) {
|
|
1234
1193
|
spinner.fail(chalk.red('Reset failed'));
|
|
1235
|
-
|
|
1194
|
+
handleNerdError(err);
|
|
1236
1195
|
process.exitCode = 1;
|
|
1237
1196
|
}
|
|
1238
1197
|
}
|
|
@@ -1248,19 +1207,7 @@ async function revertCommit(commit) {
|
|
|
1248
1207
|
}
|
|
1249
1208
|
catch (err) {
|
|
1250
1209
|
spinner.fail(chalk.red(`Failed to revert '${commit}'`));
|
|
1251
|
-
|
|
1252
|
-
if (msg.includes('conflicts')) {
|
|
1253
|
-
console.error(chalk.red('Merge conflicts detected. Resolve conflicts, then use: omg revert --continue'));
|
|
1254
|
-
}
|
|
1255
|
-
else if (msg.includes('is a merge')) {
|
|
1256
|
-
console.error(chalk.red('Cannot revert merge commit automatically. Use git revert -m 1 <commit>'));
|
|
1257
|
-
}
|
|
1258
|
-
else if (msg.includes('already reverted')) {
|
|
1259
|
-
console.log(chalk.yellow('This commit may have already been reverted.'));
|
|
1260
|
-
}
|
|
1261
|
-
else {
|
|
1262
|
-
console.error(chalk.red(msg));
|
|
1263
|
-
}
|
|
1210
|
+
handleNerdError(err, commit);
|
|
1264
1211
|
process.exitCode = 1;
|
|
1265
1212
|
}
|
|
1266
1213
|
}
|
|
@@ -1272,16 +1219,7 @@ async function continueRevert() {
|
|
|
1272
1219
|
}
|
|
1273
1220
|
catch (err) {
|
|
1274
1221
|
spinner.fail(chalk.red('Failed to continue revert'));
|
|
1275
|
-
|
|
1276
|
-
if (msg.includes('no revert')) {
|
|
1277
|
-
console.error(chalk.red('No revert in progress'));
|
|
1278
|
-
}
|
|
1279
|
-
else if (msg.includes('conflicts')) {
|
|
1280
|
-
console.error(chalk.red('Unresolved conflicts remain. Resolve them first.'));
|
|
1281
|
-
}
|
|
1282
|
-
else {
|
|
1283
|
-
console.error(chalk.red(msg));
|
|
1284
|
-
}
|
|
1222
|
+
handleNerdError(err);
|
|
1285
1223
|
process.exitCode = 1;
|
|
1286
1224
|
}
|
|
1287
1225
|
}
|
|
@@ -1297,19 +1235,7 @@ async function cherryPickCommit(commit) {
|
|
|
1297
1235
|
}
|
|
1298
1236
|
catch (err) {
|
|
1299
1237
|
spinner.fail(chalk.red(`Failed to cherry-pick '${commit}'`));
|
|
1300
|
-
|
|
1301
|
-
if (msg.includes('conflicts')) {
|
|
1302
|
-
console.error(chalk.red('Merge conflicts detected. Resolve conflicts, then use: omg cherry-pick --continue'));
|
|
1303
|
-
}
|
|
1304
|
-
else if (msg.includes('already exists')) {
|
|
1305
|
-
console.error(chalk.yellow('This commit is already in the current branch.'));
|
|
1306
|
-
}
|
|
1307
|
-
else if (msg.includes('empty')) {
|
|
1308
|
-
console.error(chalk.yellow('This cherry-pick would result in an empty commit.'));
|
|
1309
|
-
}
|
|
1310
|
-
else {
|
|
1311
|
-
console.error(chalk.red(msg));
|
|
1312
|
-
}
|
|
1238
|
+
handleNerdError(err, commit);
|
|
1313
1239
|
process.exitCode = 1;
|
|
1314
1240
|
}
|
|
1315
1241
|
}
|
|
@@ -1321,16 +1247,7 @@ async function continueCherryPick() {
|
|
|
1321
1247
|
}
|
|
1322
1248
|
catch (err) {
|
|
1323
1249
|
spinner.fail(chalk.red('Failed to continue cherry-pick'));
|
|
1324
|
-
|
|
1325
|
-
if (msg.includes('no cherry-pick')) {
|
|
1326
|
-
console.error(chalk.red('No cherry-pick in progress'));
|
|
1327
|
-
}
|
|
1328
|
-
else if (msg.includes('conflicts')) {
|
|
1329
|
-
console.error(chalk.red('Unresolved conflicts remain. Resolve them first.'));
|
|
1330
|
-
}
|
|
1331
|
-
else {
|
|
1332
|
-
console.error(chalk.red(msg));
|
|
1333
|
-
}
|
|
1250
|
+
handleNerdError(err);
|
|
1334
1251
|
process.exitCode = 1;
|
|
1335
1252
|
}
|
|
1336
1253
|
}
|
|
@@ -1408,7 +1325,7 @@ async function shipChanges(message, useRebase, dryRun) {
|
|
|
1408
1325
|
}
|
|
1409
1326
|
catch (err) {
|
|
1410
1327
|
spinner.fail(chalk.red('Failed to analyze repository'));
|
|
1411
|
-
|
|
1328
|
+
handleNerdError(err);
|
|
1412
1329
|
process.exitCode = 1;
|
|
1413
1330
|
return;
|
|
1414
1331
|
}
|
|
@@ -1428,7 +1345,7 @@ async function shipChanges(message, useRebase, dryRun) {
|
|
|
1428
1345
|
}
|
|
1429
1346
|
catch (err) {
|
|
1430
1347
|
stepSpinner.fail(chalk.red('Commit failed'));
|
|
1431
|
-
|
|
1348
|
+
handleNerdError(err);
|
|
1432
1349
|
process.exitCode = 1;
|
|
1433
1350
|
return;
|
|
1434
1351
|
}
|
|
@@ -1448,7 +1365,7 @@ async function shipChanges(message, useRebase, dryRun) {
|
|
|
1448
1365
|
}
|
|
1449
1366
|
catch (err) {
|
|
1450
1367
|
stepSpinner.fail(chalk.red('Failed to stage'));
|
|
1451
|
-
|
|
1368
|
+
handleNerdError(err);
|
|
1452
1369
|
process.exitCode = 1;
|
|
1453
1370
|
return;
|
|
1454
1371
|
}
|
|
@@ -1470,13 +1387,7 @@ async function shipChanges(message, useRebase, dryRun) {
|
|
|
1470
1387
|
}
|
|
1471
1388
|
catch (err) {
|
|
1472
1389
|
fetchSpinner.fail(chalk.red('Fetch failed'));
|
|
1473
|
-
|
|
1474
|
-
if (msg.includes('no remote')) {
|
|
1475
|
-
console.error(chalk.red('No remote configured. Add one with: omg remote <url>'));
|
|
1476
|
-
}
|
|
1477
|
-
else {
|
|
1478
|
-
console.error(chalk.red(msg));
|
|
1479
|
-
}
|
|
1390
|
+
handleNerdError(err);
|
|
1480
1391
|
process.exitCode = 1;
|
|
1481
1392
|
return;
|
|
1482
1393
|
}
|
|
@@ -1504,7 +1415,7 @@ async function shipChanges(message, useRebase, dryRun) {
|
|
|
1504
1415
|
}
|
|
1505
1416
|
catch (err) {
|
|
1506
1417
|
rebaseSpinner.fail(chalk.red('Rebase failed'));
|
|
1507
|
-
|
|
1418
|
+
handleNerdError(err);
|
|
1508
1419
|
process.exitCode = 1;
|
|
1509
1420
|
return;
|
|
1510
1421
|
}
|
|
@@ -1522,7 +1433,7 @@ async function shipChanges(message, useRebase, dryRun) {
|
|
|
1522
1433
|
}
|
|
1523
1434
|
catch (err) {
|
|
1524
1435
|
mergeSpinner.fail(chalk.red('Merge failed'));
|
|
1525
|
-
|
|
1436
|
+
handleNerdError(err);
|
|
1526
1437
|
process.exitCode = 1;
|
|
1527
1438
|
return;
|
|
1528
1439
|
}
|
|
@@ -1545,13 +1456,7 @@ async function shipChanges(message, useRebase, dryRun) {
|
|
|
1545
1456
|
}
|
|
1546
1457
|
catch (err) {
|
|
1547
1458
|
pushSpinner.fail(chalk.red('Push failed'));
|
|
1548
|
-
|
|
1549
|
-
if (msg.includes('rejected')) {
|
|
1550
|
-
console.error(chalk.red('Push was rejected. Run `omg ship` again to sync and retry.'));
|
|
1551
|
-
}
|
|
1552
|
-
else {
|
|
1553
|
-
console.error(chalk.red(msg));
|
|
1554
|
-
}
|
|
1459
|
+
handleNerdError(err);
|
|
1555
1460
|
process.exitCode = 1;
|
|
1556
1461
|
return;
|
|
1557
1462
|
}
|
|
@@ -1610,13 +1515,7 @@ async function oopsUncommit(keepChanges) {
|
|
|
1610
1515
|
}
|
|
1611
1516
|
catch (err) {
|
|
1612
1517
|
spinner.fail(chalk.red('Failed to undo commit'));
|
|
1613
|
-
|
|
1614
|
-
if (msg.includes('unknown revision')) {
|
|
1615
|
-
console.error(chalk.red('No commits to undo'));
|
|
1616
|
-
}
|
|
1617
|
-
else {
|
|
1618
|
-
console.error(chalk.red(msg));
|
|
1619
|
-
}
|
|
1518
|
+
handleNerdError(err);
|
|
1620
1519
|
process.exitCode = 1;
|
|
1621
1520
|
}
|
|
1622
1521
|
}
|
|
@@ -1629,7 +1528,7 @@ async function oopsUnstage() {
|
|
|
1629
1528
|
}
|
|
1630
1529
|
catch (err) {
|
|
1631
1530
|
spinner.fail(chalk.red('Failed to unstage'));
|
|
1632
|
-
|
|
1531
|
+
handleNerdError(err);
|
|
1633
1532
|
process.exitCode = 1;
|
|
1634
1533
|
}
|
|
1635
1534
|
}
|
|
@@ -1641,7 +1540,7 @@ async function oopsUnadd(file) {
|
|
|
1641
1540
|
}
|
|
1642
1541
|
catch (err) {
|
|
1643
1542
|
spinner.fail(chalk.red(`Failed to unstage '${file}'`));
|
|
1644
|
-
|
|
1543
|
+
handleNerdError(err);
|
|
1645
1544
|
process.exitCode = 1;
|
|
1646
1545
|
}
|
|
1647
1546
|
}
|
|
@@ -1679,7 +1578,7 @@ async function oopsRestoreBranch() {
|
|
|
1679
1578
|
}
|
|
1680
1579
|
catch (err) {
|
|
1681
1580
|
spinner.fail(chalk.red('Failed to check reflog'));
|
|
1682
|
-
|
|
1581
|
+
handleNerdError(err);
|
|
1683
1582
|
process.exitCode = 1;
|
|
1684
1583
|
}
|
|
1685
1584
|
}
|
|
@@ -1742,7 +1641,7 @@ async function syncWorkspace(baseBranch) {
|
|
|
1742
1641
|
}
|
|
1743
1642
|
catch (err) {
|
|
1744
1643
|
checkSpinner.fail(chalk.red('Failed to check repository'));
|
|
1745
|
-
|
|
1644
|
+
handleNerdError(err);
|
|
1746
1645
|
process.exitCode = 1;
|
|
1747
1646
|
return;
|
|
1748
1647
|
}
|
|
@@ -1821,7 +1720,7 @@ async function syncWorkspace(baseBranch) {
|
|
|
1821
1720
|
}
|
|
1822
1721
|
catch (err) {
|
|
1823
1722
|
returnSpinner.fail(chalk.red('Failed to return to original branch'));
|
|
1824
|
-
|
|
1723
|
+
handleNerdError(err);
|
|
1825
1724
|
console.error(chalk.yellow(`You are now on ${baseBranch}. Manual recovery needed.`));
|
|
1826
1725
|
process.exitCode = 1;
|
|
1827
1726
|
return;
|
|
@@ -1835,7 +1734,7 @@ async function syncWorkspace(baseBranch) {
|
|
|
1835
1734
|
}
|
|
1836
1735
|
catch (err) {
|
|
1837
1736
|
popSpinner.fail(chalk.red('Failed to restore stash'));
|
|
1838
|
-
|
|
1737
|
+
handleNerdError(err);
|
|
1839
1738
|
console.error(chalk.yellow('Your changes are in the stash. Run: omg stash pop'));
|
|
1840
1739
|
process.exitCode = 1;
|
|
1841
1740
|
return;
|
|
@@ -2020,7 +1919,7 @@ async function runDoctor(autoFix) {
|
|
|
2020
1919
|
}
|
|
2021
1920
|
catch (err) {
|
|
2022
1921
|
spinner.fail(chalk.red('Failed to run health checks'));
|
|
2023
|
-
|
|
1922
|
+
handleNerdError(err);
|
|
2024
1923
|
process.exitCode = 1;
|
|
2025
1924
|
return;
|
|
2026
1925
|
}
|
|
@@ -2105,7 +2004,7 @@ async function getConfig(key) {
|
|
|
2105
2004
|
}
|
|
2106
2005
|
catch (err) {
|
|
2107
2006
|
console.error(chalk.red(`Failed to get config '${key}'`));
|
|
2108
|
-
|
|
2007
|
+
handleNerdError(err);
|
|
2109
2008
|
process.exitCode = 1;
|
|
2110
2009
|
}
|
|
2111
2010
|
}
|
|
@@ -2118,10 +2017,163 @@ async function setConfig(key, value) {
|
|
|
2118
2017
|
}
|
|
2119
2018
|
catch (err) {
|
|
2120
2019
|
spinner.fail(chalk.red(`Failed to set config '${key}'`));
|
|
2121
|
-
|
|
2020
|
+
handleNerdError(err);
|
|
2021
|
+
process.exitCode = 1;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
// ---------------------------------------------------------------------------
|
|
2025
|
+
// blame subcommand
|
|
2026
|
+
// ---------------------------------------------------------------------------
|
|
2027
|
+
program
|
|
2028
|
+
.command('blame <file>')
|
|
2029
|
+
.description('show line-by-line authorship of a file\n' +
|
|
2030
|
+
' <file> file to blame\n' +
|
|
2031
|
+
' -L <line> show specific line\n' +
|
|
2032
|
+
' -s, --stats show author statistics')
|
|
2033
|
+
.option('-L, --line <number>', 'show blame for specific line only')
|
|
2034
|
+
.option('-s, --stats', 'show author statistics instead of line-by-line')
|
|
2035
|
+
.action(async (file, options) => {
|
|
2036
|
+
await showBlame(file, options.line ? parseInt(options.line, 10) : undefined, options.stats ?? false);
|
|
2037
|
+
});
|
|
2038
|
+
// ---------------------------------------------------------------------------
|
|
2039
|
+
// blame helpers
|
|
2040
|
+
// ---------------------------------------------------------------------------
|
|
2041
|
+
async function showBlame(file, lineNum, showStats = false) {
|
|
2042
|
+
validateNotFlag(file, 'file path');
|
|
2043
|
+
const spinner = ora(`Analyzing ${chalk.cyan(file)}`).start();
|
|
2044
|
+
try {
|
|
2045
|
+
const blameData = await git.raw(['blame', file]);
|
|
2046
|
+
spinner.stop();
|
|
2047
|
+
if (showStats) {
|
|
2048
|
+
await showBlameStats(blameData, file);
|
|
2049
|
+
}
|
|
2050
|
+
else if (lineNum !== undefined) {
|
|
2051
|
+
showBlameLine(blameData, file, lineNum);
|
|
2052
|
+
}
|
|
2053
|
+
else {
|
|
2054
|
+
showBlameFull(blameData, file);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
catch (err) {
|
|
2058
|
+
spinner.fail(chalk.red(`Failed to blame '${file}'`));
|
|
2059
|
+
handleNerdError(err);
|
|
2122
2060
|
process.exitCode = 1;
|
|
2123
2061
|
}
|
|
2124
2062
|
}
|
|
2063
|
+
function showBlameFull(blameData, file) {
|
|
2064
|
+
const lines = blameData.split('\n').filter(line => line.trim());
|
|
2065
|
+
if (lines.length === 0) {
|
|
2066
|
+
console.log(chalk.yellow(`No blame data available for ${chalk.cyan(file)}`));
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
console.log(chalk.bold(`\n--- Blame: ${chalk.cyan(file)} ---\n`));
|
|
2070
|
+
// Parse blame output
|
|
2071
|
+
// Git blame format: hash (author date time timezone lineNum) content
|
|
2072
|
+
const entries = [];
|
|
2073
|
+
for (const line of lines) {
|
|
2074
|
+
// Match pattern: hash (author date time timezone lineNum) content
|
|
2075
|
+
const match = line.match(/^([0-9a-f]+)\s+\(([^)]+)\)\s*(.*)$/);
|
|
2076
|
+
if (match) {
|
|
2077
|
+
const hash = match[1];
|
|
2078
|
+
const metaPart = match[2];
|
|
2079
|
+
const content = match[3];
|
|
2080
|
+
// Parse meta part: author date time timezone lineNum
|
|
2081
|
+
const metaParts = metaPart.split(/\s+/);
|
|
2082
|
+
if (metaParts.length >= 4) {
|
|
2083
|
+
const author = metaParts[0];
|
|
2084
|
+
const date = metaParts.slice(1, metaParts.length - 1).join(' ');
|
|
2085
|
+
const lineNum = parseInt(metaParts[metaParts.length - 1], 10);
|
|
2086
|
+
entries.push({
|
|
2087
|
+
hash,
|
|
2088
|
+
author,
|
|
2089
|
+
date,
|
|
2090
|
+
lineNum,
|
|
2091
|
+
content,
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
// Display with formatting
|
|
2097
|
+
for (const entry of entries) {
|
|
2098
|
+
const shortHash = entry.hash.slice(0, 7);
|
|
2099
|
+
const lineNum = chalk.dim(`${entry.lineNum.toString().padStart(4)}:`);
|
|
2100
|
+
const hashStr = chalk.green(shortHash);
|
|
2101
|
+
const authorStr = chalk.cyan(entry.author.padEnd(15));
|
|
2102
|
+
const dateStr = chalk.dim(entry.date);
|
|
2103
|
+
console.log(`${lineNum} ${hashStr} ${authorStr} ${dateStr} ${entry.content}`);
|
|
2104
|
+
}
|
|
2105
|
+
console.log('');
|
|
2106
|
+
}
|
|
2107
|
+
function showBlameLine(blameData, file, lineNum) {
|
|
2108
|
+
const lines = blameData.split('\n').filter(line => line.trim());
|
|
2109
|
+
if (lines.length === 0 || lineNum < 1 || lineNum > lines.length) {
|
|
2110
|
+
console.log(chalk.yellow(`Line ${lineNum} not found in ${chalk.cyan(file)}`));
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
const targetLine = lines[lineNum - 1];
|
|
2114
|
+
// Match pattern: hash (author date time timezone lineNum) content
|
|
2115
|
+
const match = targetLine.match(/^([0-9a-f]+)\s+\(([^)]+)\)\s*(.*)$/);
|
|
2116
|
+
if (match) {
|
|
2117
|
+
const hash = match[1];
|
|
2118
|
+
const metaPart = match[2];
|
|
2119
|
+
const content = match[3];
|
|
2120
|
+
// Parse meta part: author date time timezone lineNum
|
|
2121
|
+
const metaParts = metaPart.split(/\s+/);
|
|
2122
|
+
if (metaParts.length >= 4) {
|
|
2123
|
+
const author = metaParts[0];
|
|
2124
|
+
const date = metaParts.slice(1, metaParts.length - 1).join(' ');
|
|
2125
|
+
console.log(chalk.bold(`\n--- Blame: ${chalk.cyan(file)}:${chalk.yellow(lineNum.toString())} ---\n`));
|
|
2126
|
+
console.log(`${chalk.green('Commit:')} ${chalk.cyan(hash.slice(0, 7))} ${chalk.dim(`(${hash})`)}`);
|
|
2127
|
+
console.log(`${chalk.green('Author:')} ${chalk.cyan(author)}`);
|
|
2128
|
+
console.log(`${chalk.green('Date:')} ${chalk.dim(date)}`);
|
|
2129
|
+
console.log(`${chalk.green('Line:')} ${content}`);
|
|
2130
|
+
console.log('');
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
async function showBlameStats(blameData, file) {
|
|
2135
|
+
const lines = blameData.split('\n').filter(line => line.trim());
|
|
2136
|
+
if (lines.length === 0) {
|
|
2137
|
+
console.log(chalk.yellow(`No blame data available for ${chalk.cyan(file)}`));
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
// Parse and count by author
|
|
2141
|
+
const authorStats = new Map();
|
|
2142
|
+
for (const line of lines) {
|
|
2143
|
+
// Match pattern: hash (author date time timezone lineNum) content
|
|
2144
|
+
const match = line.match(/^([0-9a-f]+)\s+\(([^)]+)\)\s*(.*)$/);
|
|
2145
|
+
if (match) {
|
|
2146
|
+
const metaPart = match[2];
|
|
2147
|
+
const metaParts = metaPart.split(/\s+/);
|
|
2148
|
+
if (metaParts.length >= 4) {
|
|
2149
|
+
const author = metaParts[0];
|
|
2150
|
+
authorStats.set(author, (authorStats.get(author) || 0) + 1);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
const totalLines = Array.from(authorStats.values()).reduce((a, b) => a + b, 0);
|
|
2155
|
+
const sortedStats = Array.from(authorStats.entries())
|
|
2156
|
+
.sort((a, b) => b[1] - a[1])
|
|
2157
|
+
.map(([author, count]) => ({
|
|
2158
|
+
author,
|
|
2159
|
+
count,
|
|
2160
|
+
percentage: ((count / totalLines) * 100).toFixed(1),
|
|
2161
|
+
}));
|
|
2162
|
+
console.log(chalk.bold(`\n--- Author Statistics: ${chalk.cyan(file)} ---\n`));
|
|
2163
|
+
console.log(`${chalk.dim('Total lines:')} ${totalLines}\n`);
|
|
2164
|
+
const maxCount = sortedStats[0]?.count || 1;
|
|
2165
|
+
const maxAuthorLength = Math.max(...sortedStats.map(s => s.author.length));
|
|
2166
|
+
for (const stat of sortedStats) {
|
|
2167
|
+
const barLength = Math.floor((stat.count / maxCount) * 30);
|
|
2168
|
+
const bar = '█'.repeat(barLength);
|
|
2169
|
+
const authorPadded = stat.author.padEnd(maxAuthorLength);
|
|
2170
|
+
console.log(`${chalk.cyan(authorPadded)} ` +
|
|
2171
|
+
`${chalk.yellow(stat.count.toString().padStart(4))} lines ` +
|
|
2172
|
+
`${chalk.dim(`(${stat.percentage}%)`)} ` +
|
|
2173
|
+
`${chalk.green(bar)}`);
|
|
2174
|
+
}
|
|
2175
|
+
console.log('');
|
|
2176
|
+
}
|
|
2125
2177
|
// ---------------------------------------------------------------------------
|
|
2126
2178
|
// utilities
|
|
2127
2179
|
// ---------------------------------------------------------------------------
|
|
@@ -2131,7 +2183,7 @@ function formatError(err) {
|
|
|
2131
2183
|
return String(err);
|
|
2132
2184
|
}
|
|
2133
2185
|
program.parseAsync(process.argv).catch((err) => {
|
|
2134
|
-
|
|
2186
|
+
handleNerdError(err);
|
|
2135
2187
|
process.exit(1);
|
|
2136
2188
|
});
|
|
2137
2189
|
//# sourceMappingURL=index.js.map
|