@harryisfish/gitt 1.5.0 → 1.6.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/commands/clean.js +30 -38
- package/dist/commands/upgrade.js +265 -0
- package/dist/errors.js +27 -14
- package/dist/index.js +66 -99
- package/dist/utils/config.js +53 -2
- package/package.json +9 -2
- package/dist/commands/rebase.js +0 -137
- package/dist/commands/stash.js +0 -217
- package/dist/display.js +0 -159
- package/dist/prompts.js +0 -56
- package/dist/theme.js +0 -59
package/dist/commands/clean.js
CHANGED
|
@@ -6,7 +6,6 @@ const listr2_1 = require("listr2");
|
|
|
6
6
|
const prompts_1 = require("@inquirer/prompts");
|
|
7
7
|
const minimatch_1 = require("minimatch");
|
|
8
8
|
const errors_1 = require("../errors");
|
|
9
|
-
const errors_2 = require("../errors");
|
|
10
9
|
const git_1 = require("../utils/git");
|
|
11
10
|
const config_1 = require("../utils/config");
|
|
12
11
|
const git = (0, simple_git_1.simpleGit)();
|
|
@@ -18,6 +17,7 @@ async function cleanDeletedBranches(options = {}) {
|
|
|
18
17
|
try {
|
|
19
18
|
const state = {
|
|
20
19
|
mainBranch: '',
|
|
20
|
+
currentBranch: '',
|
|
21
21
|
deletedBranches: []
|
|
22
22
|
};
|
|
23
23
|
// Phase 1: Discovery
|
|
@@ -27,36 +27,11 @@ async function cleanDeletedBranches(options = {}) {
|
|
|
27
27
|
task: async (ctx) => {
|
|
28
28
|
const mainBranch = await (0, git_1.getMainBranch)();
|
|
29
29
|
ctx.mainBranch = mainBranch;
|
|
30
|
+
const branchInfo = await git.branchLocal();
|
|
31
|
+
ctx.currentBranch = branchInfo.current;
|
|
30
32
|
await git.fetch(['origin', mainBranch]);
|
|
31
33
|
}
|
|
32
34
|
},
|
|
33
|
-
{
|
|
34
|
-
title: 'Switch to main branch',
|
|
35
|
-
task: async (ctx) => {
|
|
36
|
-
// Check if we are on a branch that will be deleted?
|
|
37
|
-
// For now, just try to switch to main to be safe.
|
|
38
|
-
// But if main is checked out in another worktree, this might fail?
|
|
39
|
-
// Let's just try.
|
|
40
|
-
try {
|
|
41
|
-
await git.checkout(ctx.mainBranch);
|
|
42
|
-
}
|
|
43
|
-
catch (e) {
|
|
44
|
-
// If we can't checkout main (e.g. dirty state), warn but continue?
|
|
45
|
-
// Ideally we should be on main to delete other branches safely.
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
title: 'Sync main with remote',
|
|
51
|
-
task: async () => {
|
|
52
|
-
try {
|
|
53
|
-
await git.pull();
|
|
54
|
-
}
|
|
55
|
-
catch (e) {
|
|
56
|
-
// Ignore pull errors (e.g. if not on branch)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
35
|
{
|
|
61
36
|
title: 'Analyze branches',
|
|
62
37
|
task: async (ctx) => {
|
|
@@ -110,7 +85,7 @@ async function cleanDeletedBranches(options = {}) {
|
|
|
110
85
|
await discoveryTasks.run(state);
|
|
111
86
|
// Phase 2: Interaction / Filtering
|
|
112
87
|
if (state.deletedBranches.length === 0) {
|
|
113
|
-
(0,
|
|
88
|
+
(0, errors_1.printSuccess)('No branches need to be cleaned up');
|
|
114
89
|
return;
|
|
115
90
|
}
|
|
116
91
|
if (options.interactive) {
|
|
@@ -128,20 +103,20 @@ async function cleanDeletedBranches(options = {}) {
|
|
|
128
103
|
}
|
|
129
104
|
catch (e) {
|
|
130
105
|
// User cancelled
|
|
131
|
-
throw new
|
|
106
|
+
throw new errors_1.UserCancelError('Operation cancelled');
|
|
132
107
|
}
|
|
133
108
|
}
|
|
134
109
|
else {
|
|
135
110
|
// Auto mode: Filter out unmerged branches
|
|
136
111
|
const unmerged = state.deletedBranches.filter(b => !b.isMerged);
|
|
137
|
-
if (unmerged.length > 0) {
|
|
138
|
-
console.log('\nSkipping unmerged branches (use -i to force delete):');
|
|
139
|
-
unmerged.forEach(b => console.log(` - ${b.name} (${b.reason})`));
|
|
140
|
-
}
|
|
141
112
|
state.deletedBranches = state.deletedBranches.filter(b => b.isMerged);
|
|
113
|
+
if (state.deletedBranches.length === 0 && unmerged.length > 0) {
|
|
114
|
+
(0, errors_1.printSuccess)(`No merged branches to clean up (${unmerged.length} unmerged branch${unmerged.length > 1 ? 'es' : ''} skipped, use -i to review)`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
142
117
|
}
|
|
143
118
|
if (state.deletedBranches.length === 0) {
|
|
144
|
-
(0,
|
|
119
|
+
(0, errors_1.printSuccess)('No branches selected for deletion');
|
|
145
120
|
return;
|
|
146
121
|
}
|
|
147
122
|
if (options.dryRun) {
|
|
@@ -149,11 +124,23 @@ async function cleanDeletedBranches(options = {}) {
|
|
|
149
124
|
state.deletedBranches.forEach(b => console.log(` - ${b.name} (${b.reason})`));
|
|
150
125
|
return;
|
|
151
126
|
}
|
|
127
|
+
// Switch to main branch if current branch will be deleted
|
|
128
|
+
const willDeleteCurrent = state.deletedBranches.some(b => b.name === state.currentBranch);
|
|
129
|
+
if (willDeleteCurrent) {
|
|
130
|
+
try {
|
|
131
|
+
await git.checkout(state.mainBranch);
|
|
132
|
+
await git.pull();
|
|
133
|
+
console.log(`Switched to ${state.mainBranch} and synced with remote`);
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
throw new errors_1.GitError(`Cannot switch to ${state.mainBranch}. Current branch "${state.currentBranch}" will be deleted but checkout failed: ${e instanceof Error ? e.message : 'Unknown error'}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
152
139
|
// Phase 3: Execution
|
|
153
140
|
const deleteTasks = new listr2_1.Listr([
|
|
154
141
|
{
|
|
155
142
|
title: 'Delete branches',
|
|
156
|
-
task: (
|
|
143
|
+
task: (_ctx) => {
|
|
157
144
|
return new listr2_1.Listr(state.deletedBranches.map(branch => ({
|
|
158
145
|
title: `Delete ${branch.name}`,
|
|
159
146
|
task: async () => {
|
|
@@ -165,9 +152,14 @@ async function cleanDeletedBranches(options = {}) {
|
|
|
165
152
|
}
|
|
166
153
|
]);
|
|
167
154
|
await deleteTasks.run(state);
|
|
168
|
-
(0,
|
|
155
|
+
(0, errors_1.printSuccess)('Branch cleanup completed');
|
|
169
156
|
}
|
|
170
157
|
catch (error) {
|
|
171
|
-
|
|
158
|
+
if (error instanceof errors_1.GitError || error instanceof errors_1.UserCancelError) {
|
|
159
|
+
// Re-throw our custom errors as-is
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
// Wrap other errors and preserve the original error
|
|
163
|
+
throw new errors_1.GitError(error instanceof Error ? error.message : 'Unknown error occurred while cleaning branches', error instanceof Error ? error : undefined);
|
|
172
164
|
}
|
|
173
165
|
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.upgradeCommand = upgradeCommand;
|
|
40
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const errors_1 = require("../errors");
|
|
44
|
+
const packageJson = require('../../package.json');
|
|
45
|
+
/**
|
|
46
|
+
* Detect which package manager is being used
|
|
47
|
+
*/
|
|
48
|
+
function detectPackageManager() {
|
|
49
|
+
try {
|
|
50
|
+
// Check if installed globally with pnpm
|
|
51
|
+
try {
|
|
52
|
+
const result = (0, child_process_1.spawnSync)('pnpm', ['list', '-g', '--depth=0'], {
|
|
53
|
+
encoding: 'utf8',
|
|
54
|
+
stdio: 'pipe'
|
|
55
|
+
});
|
|
56
|
+
if (result.status === 0 && result.stdout.includes(packageJson.name)) {
|
|
57
|
+
return 'pnpm';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
// Not installed with pnpm or pnpm not available
|
|
62
|
+
}
|
|
63
|
+
// Check if installed globally with yarn
|
|
64
|
+
try {
|
|
65
|
+
const result = (0, child_process_1.spawnSync)('yarn', ['global', 'list'], {
|
|
66
|
+
encoding: 'utf8',
|
|
67
|
+
stdio: 'pipe'
|
|
68
|
+
});
|
|
69
|
+
if (result.status === 0 && result.stdout.includes(packageJson.name)) {
|
|
70
|
+
return 'yarn';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
// Not installed with yarn or yarn not available
|
|
75
|
+
}
|
|
76
|
+
// Check if installed globally with npm
|
|
77
|
+
try {
|
|
78
|
+
const result = (0, child_process_1.spawnSync)('npm', ['list', '-g', '--depth=0'], {
|
|
79
|
+
encoding: 'utf8',
|
|
80
|
+
stdio: 'pipe'
|
|
81
|
+
});
|
|
82
|
+
if (result.status === 0 && result.stdout.includes(packageJson.name)) {
|
|
83
|
+
return 'npm';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
// Not installed with npm
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Fetch latest release information from GitHub
|
|
97
|
+
*/
|
|
98
|
+
async function fetchLatestRelease() {
|
|
99
|
+
try {
|
|
100
|
+
const response = await fetch('https://api.github.com/repos/harryisfish/gitt/releases/latest', {
|
|
101
|
+
headers: {
|
|
102
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
103
|
+
'User-Agent': 'gitt-cli'
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const data = await response.json();
|
|
110
|
+
return {
|
|
111
|
+
version: data.tag_name.replace(/^v/, ''), // Remove 'v' prefix if present
|
|
112
|
+
changelog: data.body || 'No changelog available',
|
|
113
|
+
publishedAt: data.published_at
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Format changelog for display
|
|
122
|
+
*/
|
|
123
|
+
function formatChangelog(changelog) {
|
|
124
|
+
// Split by lines and format
|
|
125
|
+
const lines = changelog.split('\n');
|
|
126
|
+
const formatted = [];
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
if (line.trim() === '')
|
|
129
|
+
continue;
|
|
130
|
+
// Format headers
|
|
131
|
+
if (line.startsWith('## ')) {
|
|
132
|
+
formatted.push(chalk_1.default.bold.cyan(line));
|
|
133
|
+
}
|
|
134
|
+
else if (line.startsWith('### ')) {
|
|
135
|
+
formatted.push(chalk_1.default.bold.yellow(line));
|
|
136
|
+
}
|
|
137
|
+
else if (line.startsWith('- ') || line.startsWith('* ')) {
|
|
138
|
+
formatted.push(chalk_1.default.gray(' ' + line));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
formatted.push(line);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return formatted.join('\n');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check for updates using update-notifier
|
|
148
|
+
*/
|
|
149
|
+
async function checkForUpdate() {
|
|
150
|
+
try {
|
|
151
|
+
const updateNotifier = await Promise.resolve().then(() => __importStar(require('update-notifier'))).then(m => m.default);
|
|
152
|
+
const notifier = updateNotifier({
|
|
153
|
+
pkg: packageJson,
|
|
154
|
+
updateCheckInterval: 0 // Force check
|
|
155
|
+
});
|
|
156
|
+
if (notifier.update) {
|
|
157
|
+
return {
|
|
158
|
+
current: notifier.update.current,
|
|
159
|
+
latest: notifier.update.latest,
|
|
160
|
+
type: notifier.update.type
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Execute upgrade command
|
|
171
|
+
*/
|
|
172
|
+
function executeUpgrade(packageManager, packageName) {
|
|
173
|
+
console.log(chalk_1.default.cyan('\nUpgrading...'));
|
|
174
|
+
try {
|
|
175
|
+
let command;
|
|
176
|
+
let args;
|
|
177
|
+
switch (packageManager) {
|
|
178
|
+
case 'pnpm':
|
|
179
|
+
command = 'pnpm';
|
|
180
|
+
args = ['add', '-g', `${packageName}@latest`];
|
|
181
|
+
break;
|
|
182
|
+
case 'yarn':
|
|
183
|
+
command = 'yarn';
|
|
184
|
+
args = ['global', 'add', `${packageName}@latest`];
|
|
185
|
+
break;
|
|
186
|
+
case 'npm':
|
|
187
|
+
default:
|
|
188
|
+
command = 'npm';
|
|
189
|
+
args = ['install', '-g', `${packageName}@latest`];
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
console.log(chalk_1.default.gray(`Running: ${command} ${args.join(' ')}\n`));
|
|
193
|
+
const result = (0, child_process_1.spawnSync)(command, args, {
|
|
194
|
+
stdio: 'inherit',
|
|
195
|
+
encoding: 'utf8'
|
|
196
|
+
});
|
|
197
|
+
if (result.status !== 0) {
|
|
198
|
+
throw new Error(`Command exited with status ${result.status}`);
|
|
199
|
+
}
|
|
200
|
+
console.log('');
|
|
201
|
+
(0, errors_1.printSuccess)('Upgrade completed successfully!');
|
|
202
|
+
console.log(chalk_1.default.gray('Run "gitt --version" to verify the new version.\n'));
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
throw new errors_1.GitError('Upgrade failed. Please try upgrading manually:\n' +
|
|
206
|
+
` ${packageManager} install -g ${packageName}@latest`, error instanceof Error ? error : undefined);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Main upgrade command handler
|
|
211
|
+
*/
|
|
212
|
+
async function upgradeCommand() {
|
|
213
|
+
try {
|
|
214
|
+
console.log(chalk_1.default.cyan('🔍 Checking for updates...\n'));
|
|
215
|
+
// Check for updates
|
|
216
|
+
const updateInfo = await checkForUpdate();
|
|
217
|
+
if (!updateInfo) {
|
|
218
|
+
(0, errors_1.printSuccess)('You are already on the latest version! 🎉');
|
|
219
|
+
console.log(chalk_1.default.gray(`Current version: ${packageJson.version}\n`));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const { current, latest, type } = updateInfo;
|
|
223
|
+
// Display version comparison
|
|
224
|
+
console.log(chalk_1.default.bold('Version Information:'));
|
|
225
|
+
console.log(` Current: ${chalk_1.default.yellow(current)}`);
|
|
226
|
+
console.log(` Latest: ${chalk_1.default.green(latest)}`);
|
|
227
|
+
console.log(` Type: ${chalk_1.default.gray(type)}\n`);
|
|
228
|
+
// Fetch and display changelog
|
|
229
|
+
console.log(chalk_1.default.bold('📝 Fetching release notes...\n'));
|
|
230
|
+
const releaseInfo = await fetchLatestRelease();
|
|
231
|
+
if (releaseInfo && releaseInfo.changelog) {
|
|
232
|
+
console.log(chalk_1.default.bold('What\'s New:\n'));
|
|
233
|
+
console.log(formatChangelog(releaseInfo.changelog));
|
|
234
|
+
console.log('');
|
|
235
|
+
}
|
|
236
|
+
// Detect package manager
|
|
237
|
+
const packageManager = detectPackageManager();
|
|
238
|
+
if (!packageManager) {
|
|
239
|
+
console.log(chalk_1.default.yellow('⚠ Could not detect package manager.'));
|
|
240
|
+
console.log(chalk_1.default.gray('Please upgrade manually using one of these commands:\n'));
|
|
241
|
+
console.log(chalk_1.default.gray(` npm install -g ${packageJson.name}@latest`));
|
|
242
|
+
console.log(chalk_1.default.gray(` pnpm add -g ${packageJson.name}@latest`));
|
|
243
|
+
console.log(chalk_1.default.gray(` yarn global add ${packageJson.name}@latest\n`));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
console.log(chalk_1.default.gray(`Package manager detected: ${packageManager}\n`));
|
|
247
|
+
// Ask for confirmation
|
|
248
|
+
const shouldUpgrade = await (0, prompts_1.confirm)({
|
|
249
|
+
message: `Would you like to upgrade to v${latest}?`,
|
|
250
|
+
default: true
|
|
251
|
+
});
|
|
252
|
+
if (!shouldUpgrade) {
|
|
253
|
+
console.log(chalk_1.default.gray('\nUpgrade cancelled.\n'));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// Execute upgrade
|
|
257
|
+
executeUpgrade(packageManager, packageJson.name);
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
if (error instanceof errors_1.GitError) {
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
throw new errors_1.GitError('Failed to check for updates. Please check your internet connection.', error instanceof Error ? error : undefined);
|
|
264
|
+
}
|
|
265
|
+
}
|
package/dist/errors.js
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.UserCancelError = exports.GitError = void 0;
|
|
4
7
|
exports.handleError = handleError;
|
|
5
8
|
exports.printSuccess = printSuccess;
|
|
6
9
|
exports.printError = printError;
|
|
7
|
-
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
// Custom error types
|
|
8
12
|
class GitError extends Error {
|
|
9
|
-
constructor(message) {
|
|
13
|
+
constructor(message, cause) {
|
|
10
14
|
super(message);
|
|
11
15
|
this.name = 'GitError';
|
|
16
|
+
this.cause = cause;
|
|
17
|
+
// Preserve stack trace from the cause if available
|
|
18
|
+
if (cause === null || cause === void 0 ? void 0 : cause.stack) {
|
|
19
|
+
this.stack = `${this.stack}\nCaused by: ${cause.stack}`;
|
|
20
|
+
}
|
|
12
21
|
}
|
|
13
22
|
}
|
|
14
23
|
exports.GitError = GitError;
|
|
@@ -19,32 +28,36 @@ class UserCancelError extends Error {
|
|
|
19
28
|
}
|
|
20
29
|
}
|
|
21
30
|
exports.UserCancelError = UserCancelError;
|
|
22
|
-
//
|
|
23
|
-
const ERROR_COLOR = '\x1b[31m';
|
|
24
|
-
const SUCCESS_COLOR = '\x1b[32m';
|
|
25
|
-
const RESET_COLOR = '\x1b[0m';
|
|
26
|
-
// 统一错误处理函数
|
|
31
|
+
// Unified error handling function
|
|
27
32
|
function handleError(error) {
|
|
28
33
|
if (error instanceof UserCancelError) {
|
|
29
34
|
console.log(error.message);
|
|
30
35
|
process.exit(0);
|
|
31
36
|
}
|
|
32
37
|
if (error instanceof GitError) {
|
|
33
|
-
console.error(
|
|
38
|
+
console.error(chalk_1.default.red('Error:'), error.message);
|
|
39
|
+
// In development mode, show full stack trace
|
|
40
|
+
if (process.env.DEBUG || process.env.VERBOSE) {
|
|
41
|
+
console.error(chalk_1.default.gray(error.stack));
|
|
42
|
+
}
|
|
34
43
|
process.exit(1);
|
|
35
44
|
}
|
|
36
45
|
if (error instanceof Error) {
|
|
37
|
-
console.error(
|
|
46
|
+
console.error(chalk_1.default.red('Program error:'), error.message);
|
|
47
|
+
// In development mode, show full stack trace
|
|
48
|
+
if (process.env.DEBUG || process.env.VERBOSE) {
|
|
49
|
+
console.error(chalk_1.default.gray(error.stack));
|
|
50
|
+
}
|
|
38
51
|
process.exit(1);
|
|
39
52
|
}
|
|
40
|
-
console.error(
|
|
53
|
+
console.error(chalk_1.default.red('Unknown error occurred'));
|
|
41
54
|
process.exit(1);
|
|
42
55
|
}
|
|
43
|
-
//
|
|
56
|
+
// Success message handler
|
|
44
57
|
function printSuccess(message) {
|
|
45
|
-
console.log(
|
|
58
|
+
console.log(chalk_1.default.green('✓'), message);
|
|
46
59
|
}
|
|
47
|
-
//
|
|
60
|
+
// Error message handler
|
|
48
61
|
function printError(message) {
|
|
49
|
-
console.error(
|
|
62
|
+
console.error(chalk_1.default.red(message));
|
|
50
63
|
}
|
package/dist/index.js
CHANGED
|
@@ -34,39 +34,31 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const commander_1 = require("commander");
|
|
37
38
|
const simple_git_1 = require("simple-git");
|
|
38
39
|
const errors_1 = require("./errors");
|
|
39
40
|
const clean_1 = require("./commands/clean");
|
|
40
41
|
const git = (0, simple_git_1.simpleGit)();
|
|
41
|
-
|
|
42
|
+
const packageJson = require('../package.json');
|
|
43
|
+
// Handle Ctrl+C and other termination signals
|
|
42
44
|
process.on('SIGINT', () => {
|
|
43
45
|
throw new errors_1.UserCancelError('\nOperation cancelled');
|
|
44
46
|
});
|
|
45
47
|
process.on('SIGTERM', () => {
|
|
46
48
|
throw new errors_1.UserCancelError('\nProgram terminated');
|
|
47
49
|
});
|
|
48
|
-
//
|
|
49
|
-
async function initGitRepo() {
|
|
50
|
-
try {
|
|
51
|
-
await git.init();
|
|
52
|
-
(0, errors_1.printSuccess)('Git repository initialized successfully');
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
throw new errors_1.GitError('Failed to initialize Git repository');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// 检查当前目录是否是 Git 仓库
|
|
50
|
+
// Check if current directory is a Git repository
|
|
59
51
|
async function checkGitRepo() {
|
|
60
52
|
const isRepo = await git.checkIsRepo();
|
|
61
53
|
if (!isRepo) {
|
|
62
54
|
throw new errors_1.GitError('Current directory is not a Git repository');
|
|
63
55
|
}
|
|
64
|
-
//
|
|
56
|
+
// Check if remote repository is configured
|
|
65
57
|
const remotes = await git.getRemotes();
|
|
66
58
|
if (remotes.length === 0) {
|
|
67
59
|
throw new errors_1.GitError('Current Git repository has no remote configured');
|
|
68
60
|
}
|
|
69
|
-
//
|
|
61
|
+
// Check if remote repository is accessible
|
|
70
62
|
try {
|
|
71
63
|
await git.fetch(['--dry-run']);
|
|
72
64
|
}
|
|
@@ -74,104 +66,79 @@ async function checkGitRepo() {
|
|
|
74
66
|
throw new errors_1.GitError('Cannot access remote repository, please check network connection or repository permissions');
|
|
75
67
|
}
|
|
76
68
|
}
|
|
77
|
-
function printHelp() {
|
|
78
|
-
console.log(`
|
|
79
|
-
Usage: gitt [command] [options]
|
|
80
|
-
|
|
81
|
-
Commands:
|
|
82
|
-
(default) Clean up local branches that have been deleted on remote
|
|
83
|
-
set-main <branch> Set the main branch for the current project
|
|
84
|
-
ignore <pattern> Add a branch pattern to the ignore list (e.g., "release/*")
|
|
85
|
-
|
|
86
|
-
Options:
|
|
87
|
-
-i, --interactive Interactive mode: Select branches to delete
|
|
88
|
-
-d, --dry-run Dry run: Show what would be deleted without deleting
|
|
89
|
-
--stale [days] Find stale branches (default: 90 days, use with a number for custom)
|
|
90
|
-
-v, --version Show version number
|
|
91
|
-
-h, --help Show this help message
|
|
92
|
-
|
|
93
|
-
Examples:
|
|
94
|
-
gitt # Auto-clean deleted branches
|
|
95
|
-
gitt -i # Select branches to delete interactively
|
|
96
|
-
gitt -d # Preview deletion
|
|
97
|
-
gitt --stale # Find branches inactive for 90+ days
|
|
98
|
-
gitt --stale 30 # Find branches inactive for 30+ days
|
|
99
|
-
gitt ignore "temp/*" # Ignore branches matching "temp/*"
|
|
100
|
-
gitt set-main master # Set main branch to 'master'
|
|
101
|
-
`);
|
|
102
|
-
}
|
|
103
69
|
async function main() {
|
|
104
70
|
try {
|
|
105
|
-
|
|
106
|
-
const command = args[0];
|
|
107
|
-
// Check for version command
|
|
108
|
-
if (args.includes('-v') || args.includes('--version')) {
|
|
109
|
-
const packageJson = require('../package.json');
|
|
110
|
-
console.log(`v${packageJson.version}`);
|
|
111
|
-
process.exit(0);
|
|
112
|
-
}
|
|
113
|
-
// Check for help command
|
|
114
|
-
if (args.includes('-h') || args.includes('--help')) {
|
|
115
|
-
printHelp();
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}
|
|
118
|
-
// Check for updates
|
|
71
|
+
// Check for updates before parsing commands
|
|
119
72
|
try {
|
|
120
|
-
// Use
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
updateNotifier({ pkg }).notify();
|
|
73
|
+
// Use dynamic import to load update-notifier
|
|
74
|
+
const updateNotifier = await Promise.resolve().then(() => __importStar(require('update-notifier'))).then(m => m.default);
|
|
75
|
+
updateNotifier({ pkg: packageJson }).notify();
|
|
124
76
|
}
|
|
125
77
|
catch (e) {
|
|
126
78
|
// Ignore update check errors
|
|
127
79
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configIgnoreBranch(pattern));
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
// Parse options
|
|
146
|
-
const isInteractive = args.includes('-i') || args.includes('--interactive');
|
|
147
|
-
const isDryRun = args.includes('-d') || args.includes('--dry-run');
|
|
148
|
-
// Parse stale options
|
|
149
|
-
const staleIndex = args.indexOf('--stale');
|
|
150
|
-
const isStale = staleIndex !== -1;
|
|
151
|
-
let staleDays = 90; // Default 3 months
|
|
152
|
-
if (isStale) {
|
|
153
|
-
// Check if next arg is a number (days)
|
|
154
|
-
// Note: This is a simple check, ideally use a proper arg parser like commander or yargs
|
|
155
|
-
const nextArg = args[staleIndex + 1];
|
|
156
|
-
if (nextArg && !nextArg.startsWith('-') && !isNaN(Number(nextArg))) {
|
|
157
|
-
staleDays = Number(nextArg);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// 默认执行清理操作
|
|
80
|
+
const program = new commander_1.Command();
|
|
81
|
+
program
|
|
82
|
+
.name('gitt')
|
|
83
|
+
.description('A CLI tool for Git branch management')
|
|
84
|
+
.version(packageJson.version, '-v, --version', 'Show version number');
|
|
85
|
+
// Default command: clean deleted branches
|
|
86
|
+
program
|
|
87
|
+
.option('-i, --interactive', 'Interactive mode: Select branches to delete')
|
|
88
|
+
.option('-d, --dry-run', 'Dry run: Show what would be deleted without deleting')
|
|
89
|
+
.option('--stale [days]', 'Find stale branches (default: 90 days)', '90')
|
|
90
|
+
.action(async (options) => {
|
|
91
|
+
await checkGitRepo();
|
|
92
|
+
const staleDays = options.stale === true ? 90 : parseInt(options.stale, 10);
|
|
93
|
+
const isStale = options.stale !== undefined;
|
|
161
94
|
await (0, clean_1.cleanDeletedBranches)({
|
|
162
|
-
interactive:
|
|
163
|
-
dryRun:
|
|
95
|
+
interactive: options.interactive || false,
|
|
96
|
+
dryRun: options.dryRun || false,
|
|
164
97
|
stale: isStale,
|
|
165
98
|
staleDays: staleDays
|
|
166
99
|
});
|
|
167
|
-
}
|
|
168
|
-
//
|
|
169
|
-
|
|
100
|
+
});
|
|
101
|
+
// set-main command
|
|
102
|
+
program
|
|
103
|
+
.command('set-main <branch>')
|
|
104
|
+
.description('Set the main branch for the current project')
|
|
105
|
+
.action(async (branch) => {
|
|
106
|
+
await checkGitRepo();
|
|
107
|
+
await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configMainBranch(branch));
|
|
108
|
+
});
|
|
109
|
+
// ignore command
|
|
110
|
+
program
|
|
111
|
+
.command('ignore <pattern>')
|
|
112
|
+
.description('Add a branch pattern to the ignore list (e.g., "release/*")')
|
|
113
|
+
.action(async (pattern) => {
|
|
114
|
+
await checkGitRepo();
|
|
115
|
+
await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configIgnoreBranch(pattern));
|
|
116
|
+
});
|
|
117
|
+
// upgrade command
|
|
118
|
+
program
|
|
119
|
+
.command('upgrade')
|
|
120
|
+
.description('Check for updates and upgrade to the latest version')
|
|
121
|
+
.action(async () => {
|
|
122
|
+
// No need to check Git repo for upgrade command
|
|
123
|
+
await Promise.resolve().then(() => __importStar(require('./commands/upgrade'))).then(m => m.upgradeCommand());
|
|
124
|
+
});
|
|
125
|
+
// Add examples to help
|
|
126
|
+
program.addHelpText('after', `
|
|
127
|
+
Examples:
|
|
128
|
+
$ gitt # Auto-clean deleted branches
|
|
129
|
+
$ gitt -i # Select branches to delete interactively
|
|
130
|
+
$ gitt -d # Preview deletion
|
|
131
|
+
$ gitt --stale # Find branches inactive for 90+ days
|
|
132
|
+
$ gitt --stale 30 # Find branches inactive for 30+ days
|
|
133
|
+
$ gitt ignore "temp/*" # Ignore branches matching "temp/*"
|
|
134
|
+
$ gitt set-main master # Set main branch to 'master'
|
|
135
|
+
$ gitt upgrade # Check for updates and upgrade
|
|
136
|
+
`);
|
|
137
|
+
await program.parseAsync(process.argv);
|
|
170
138
|
}
|
|
171
139
|
catch (error) {
|
|
172
140
|
(0, errors_1.handleError)(error);
|
|
173
|
-
process.exit(1);
|
|
174
141
|
}
|
|
175
142
|
}
|
|
176
|
-
//
|
|
177
|
-
main()
|
|
143
|
+
// Start the program
|
|
144
|
+
main();
|