@harryisfish/gitt 1.0.0
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/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/display.js +77 -0
- package/dist/errors.js +50 -0
- package/dist/index.js +183 -0
- package/dist/prompts.js +56 -0
- package/dist/theme.js +59 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Harry Wong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Gitt
|
|
2
|
+
|
|
3
|
+
这是一个命令行工具,用于帮助你管理 Git 仓库与远端仓库,如保持同步、推送、拉取等。
|
|
4
|
+
|
|
5
|
+
## 技术
|
|
6
|
+
1. 使用 Node.js 开发
|
|
7
|
+
2. 使用 TypeScript 开发
|
|
8
|
+
3. 使用 pnpm 包管理器
|
|
9
|
+
|
|
10
|
+
## 功能
|
|
11
|
+
1. 清理远程已删除的分支 - 自动清理那些在远程仓库已经被删除的本地分支
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
使用 pnpm 安装(推荐):
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add -g gitt
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
或者使用 npm 安装:
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g gitt
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 开发
|
|
26
|
+
|
|
27
|
+
1. 安装依赖
|
|
28
|
+
```bash
|
|
29
|
+
pnpm install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
2. 开发模式运行
|
|
33
|
+
```bash
|
|
34
|
+
# 1. 构建并创建全局链接
|
|
35
|
+
pnpm dev:link
|
|
36
|
+
|
|
37
|
+
# 2. 创建一个测试用的 Git 仓库
|
|
38
|
+
mkdir test-repo
|
|
39
|
+
cd test-repo
|
|
40
|
+
git init
|
|
41
|
+
git remote add origin <你的测试仓库地址>
|
|
42
|
+
|
|
43
|
+
# 3. 现在你可以在这个测试仓库中运行
|
|
44
|
+
gitt
|
|
45
|
+
|
|
46
|
+
# 4. 测试完成后,取消全局链接
|
|
47
|
+
pnpm dev:unlink
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. 构建
|
|
51
|
+
```bash
|
|
52
|
+
pnpm build
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 使用
|
|
56
|
+
|
|
57
|
+
在任何 Git 仓库目录下,运行:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
gitt
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
然后使用方向键选择要执行的操作,按回车确认。
|
package/dist/display.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.display = void 0;
|
|
4
|
+
const readline_1 = require("readline");
|
|
5
|
+
// ANSI 转义序列
|
|
6
|
+
const CLEAR_SCREEN = '\x1b[2J';
|
|
7
|
+
const MOVE_TO_TOP = '\x1b[H';
|
|
8
|
+
const SAVE_CURSOR = '\x1b[s';
|
|
9
|
+
const RESTORE_CURSOR = '\x1b[u';
|
|
10
|
+
const HIDE_CURSOR = '\x1b[?25l';
|
|
11
|
+
const SHOW_CURSOR = '\x1b[?25h';
|
|
12
|
+
class DisplayManager {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.statusSection = [];
|
|
15
|
+
this.menuSection = [];
|
|
16
|
+
this.dividerLine = '';
|
|
17
|
+
this.isLoading = true;
|
|
18
|
+
// 获取终端大小
|
|
19
|
+
const { rows, columns } = process.stdout;
|
|
20
|
+
// 创建分隔线
|
|
21
|
+
this.dividerLine = '─'.repeat(columns);
|
|
22
|
+
// 计算上下区域的大小
|
|
23
|
+
this.statusHeight = Math.floor(rows / 2) - 1;
|
|
24
|
+
this.menuHeight = rows - this.statusHeight - 1;
|
|
25
|
+
}
|
|
26
|
+
// 清屏并初始化显示区域
|
|
27
|
+
initDisplay() {
|
|
28
|
+
process.stdout.write(CLEAR_SCREEN + MOVE_TO_TOP);
|
|
29
|
+
this.drawDivider();
|
|
30
|
+
this.showLoadingStatus();
|
|
31
|
+
}
|
|
32
|
+
// 绘制分隔线
|
|
33
|
+
drawDivider() {
|
|
34
|
+
process.stdout.write(SAVE_CURSOR);
|
|
35
|
+
process.stdout.write(`\x1b[${this.statusHeight}H${this.dividerLine}`);
|
|
36
|
+
process.stdout.write(RESTORE_CURSOR);
|
|
37
|
+
}
|
|
38
|
+
// 显示加载状态
|
|
39
|
+
showLoadingStatus() {
|
|
40
|
+
if (this.isLoading) {
|
|
41
|
+
process.stdout.write(MOVE_TO_TOP);
|
|
42
|
+
process.stdout.write('正在获取仓库信息...\n');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 更新状态区域内容
|
|
46
|
+
updateStatus(lines) {
|
|
47
|
+
this.isLoading = false;
|
|
48
|
+
this.statusSection = lines;
|
|
49
|
+
this.refreshStatus();
|
|
50
|
+
}
|
|
51
|
+
// 刷新状态区域显示
|
|
52
|
+
refreshStatus() {
|
|
53
|
+
process.stdout.write(SAVE_CURSOR);
|
|
54
|
+
process.stdout.write(MOVE_TO_TOP);
|
|
55
|
+
// 清空状态区域
|
|
56
|
+
for (let i = 0; i < this.statusHeight; i++) {
|
|
57
|
+
(0, readline_1.clearLine)(process.stdout, 0);
|
|
58
|
+
process.stdout.write('\n');
|
|
59
|
+
}
|
|
60
|
+
// 重新显示状态信息
|
|
61
|
+
process.stdout.write(MOVE_TO_TOP);
|
|
62
|
+
this.statusSection.forEach(line => {
|
|
63
|
+
process.stdout.write(line + '\n');
|
|
64
|
+
});
|
|
65
|
+
process.stdout.write(RESTORE_CURSOR);
|
|
66
|
+
}
|
|
67
|
+
// 准备菜单区域
|
|
68
|
+
prepareForMenu() {
|
|
69
|
+
process.stdout.write(`\x1b[${this.statusHeight + 1}H\n`);
|
|
70
|
+
}
|
|
71
|
+
// 显示错误信息
|
|
72
|
+
showError(message) {
|
|
73
|
+
const currentPos = process.stdout.rows;
|
|
74
|
+
process.stdout.write(`\x1b[${currentPos}H\x1b[31m${message}\x1b[0m\n`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.display = new DisplayManager();
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserCancelError = exports.GitError = void 0;
|
|
4
|
+
exports.handleError = handleError;
|
|
5
|
+
exports.printSuccess = printSuccess;
|
|
6
|
+
exports.printError = printError;
|
|
7
|
+
// 自定义错误类型
|
|
8
|
+
class GitError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'GitError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.GitError = GitError;
|
|
15
|
+
class UserCancelError extends Error {
|
|
16
|
+
constructor(message = '操作已取消') {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'UserCancelError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.UserCancelError = UserCancelError;
|
|
22
|
+
// 错误消息颜色
|
|
23
|
+
const ERROR_COLOR = '\x1b[31m';
|
|
24
|
+
const SUCCESS_COLOR = '\x1b[32m';
|
|
25
|
+
const RESET_COLOR = '\x1b[0m';
|
|
26
|
+
// 统一错误处理函数
|
|
27
|
+
function handleError(error) {
|
|
28
|
+
if (error instanceof UserCancelError) {
|
|
29
|
+
console.log(error.message);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof GitError) {
|
|
33
|
+
console.error(`${ERROR_COLOR}错误:${RESET_COLOR}${error.message}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
if (error instanceof Error) {
|
|
37
|
+
console.error(`${ERROR_COLOR}程序错误:${RESET_COLOR}${error.message}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
console.error(`${ERROR_COLOR}发生未知错误${RESET_COLOR}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
// 成功消息处理
|
|
44
|
+
function printSuccess(message) {
|
|
45
|
+
console.log(`${SUCCESS_COLOR}✓ ${message}${RESET_COLOR}`);
|
|
46
|
+
}
|
|
47
|
+
// 错误消息处理
|
|
48
|
+
function printError(message) {
|
|
49
|
+
console.error(`${ERROR_COLOR}${message}${RESET_COLOR}`);
|
|
50
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
5
|
+
const simple_git_1 = require("simple-git");
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
const display_1 = require("./display");
|
|
8
|
+
const git = (0, simple_git_1.simpleGit)();
|
|
9
|
+
// 处理 Ctrl+C 和其他终止信号
|
|
10
|
+
process.on('SIGINT', () => {
|
|
11
|
+
throw new errors_1.UserCancelError('\n操作已取消');
|
|
12
|
+
});
|
|
13
|
+
process.on('SIGTERM', () => {
|
|
14
|
+
throw new errors_1.UserCancelError('\n程序被终止');
|
|
15
|
+
});
|
|
16
|
+
// 初始化 Git 仓库
|
|
17
|
+
async function initGitRepo() {
|
|
18
|
+
try {
|
|
19
|
+
await git.init();
|
|
20
|
+
(0, errors_1.printSuccess)('Git 仓库初始化成功');
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw new errors_1.GitError('Git 仓库初始化失败');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// 检查当前目录是否是 Git 仓库
|
|
27
|
+
async function checkGitRepo() {
|
|
28
|
+
const isRepo = await git.checkIsRepo();
|
|
29
|
+
if (!isRepo) {
|
|
30
|
+
const shouldInit = await (0, prompts_1.confirm)({
|
|
31
|
+
message: '当前目录不是 Git 仓库,是否要创建?',
|
|
32
|
+
default: true
|
|
33
|
+
});
|
|
34
|
+
if (shouldInit) {
|
|
35
|
+
await initGitRepo();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
throw new errors_1.UserCancelError();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// 检查是否有远程仓库配置
|
|
42
|
+
const remotes = await git.getRemotes();
|
|
43
|
+
if (remotes.length === 0) {
|
|
44
|
+
throw new errors_1.GitError('当前 Git 仓库未配置远程仓库');
|
|
45
|
+
}
|
|
46
|
+
// 检查是否能访问远程仓库
|
|
47
|
+
try {
|
|
48
|
+
await git.fetch(['--dry-run']);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new errors_1.GitError('无法访问远程仓库,请检查网络连接或仓库权限');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 异步获取并展示仓库状态信息
|
|
55
|
+
async function showRepoStatus() {
|
|
56
|
+
try {
|
|
57
|
+
const statusLines = [];
|
|
58
|
+
// 获取当前分支信息
|
|
59
|
+
const currentBranch = await git.branch();
|
|
60
|
+
const currentBranchName = currentBranch.current;
|
|
61
|
+
statusLines.push(`当前分支: ${currentBranchName}\n`);
|
|
62
|
+
// 获取最新的远程分支信息
|
|
63
|
+
await git.fetch(['--all']);
|
|
64
|
+
// 获取本地与远程的差异统计
|
|
65
|
+
const status = await git.status();
|
|
66
|
+
statusLines.push('📊 本地仓库状态:');
|
|
67
|
+
statusLines.push(`- 未提交的修改: ${status.modified.length + status.not_added.length + status.deleted.length} 个文件`);
|
|
68
|
+
if (status.ahead > 0) {
|
|
69
|
+
statusLines.push(`- 领先远程分支: ${status.ahead} 个提交`);
|
|
70
|
+
}
|
|
71
|
+
if (status.behind > 0) {
|
|
72
|
+
statusLines.push(`- 落后远程分支: ${status.behind} 个提交`);
|
|
73
|
+
}
|
|
74
|
+
statusLines.push('');
|
|
75
|
+
// 获取最近的提交信息
|
|
76
|
+
const recentCommits = await git.log(['--max-count=5']);
|
|
77
|
+
statusLines.push('📝 最近提交记录:');
|
|
78
|
+
recentCommits.all.forEach(commit => {
|
|
79
|
+
statusLines.push(`- ${commit.date.split('T')[0]} ${commit.hash.substring(0, 7)} ${commit.message}`);
|
|
80
|
+
});
|
|
81
|
+
statusLines.push('');
|
|
82
|
+
// 获取所有分支信息
|
|
83
|
+
const branchSummary = await git.branch(['-vv']);
|
|
84
|
+
const localBranches = branchSummary.all.length;
|
|
85
|
+
const remoteBranches = Object.keys(branchSummary.branches).filter(b => { var _a; return (_a = branchSummary.branches[b].label) === null || _a === void 0 ? void 0 : _a.includes('/'); }).length;
|
|
86
|
+
statusLines.push('🌳 分支信息:');
|
|
87
|
+
statusLines.push(`- 本地分支数: ${localBranches}`);
|
|
88
|
+
statusLines.push(`- 远程分支数: ${remoteBranches}`);
|
|
89
|
+
// 更新显示
|
|
90
|
+
display_1.display.updateStatus(statusLines);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const errorMessage = error instanceof Error ? error.message : '未知错误';
|
|
94
|
+
display_1.display.showError(`获取仓库状态信息时发生错误: ${errorMessage}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function cleanDeletedBranches() {
|
|
98
|
+
try {
|
|
99
|
+
console.log('正在切换到 main 分支...');
|
|
100
|
+
await git.checkout('main');
|
|
101
|
+
console.log('正在拉取最新代码...');
|
|
102
|
+
await git.pull();
|
|
103
|
+
console.log('正在清理远程已删除的分支...');
|
|
104
|
+
// 获取最新的远程分支信息
|
|
105
|
+
await git.fetch(['--prune']);
|
|
106
|
+
// 获取所有分支信息
|
|
107
|
+
const branchSummary = await git.branch(['-vv']);
|
|
108
|
+
// 找出已经在远程被删除的分支
|
|
109
|
+
const deletedBranches = branchSummary.all.filter(branch => {
|
|
110
|
+
const branchInfo = branchSummary.branches[branch];
|
|
111
|
+
return branchInfo.label && branchInfo.label.includes(': gone]');
|
|
112
|
+
});
|
|
113
|
+
if (deletedBranches.length === 0) {
|
|
114
|
+
console.log('没有需要清理的分支。');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.log('以下分支将被删除:', deletedBranches.join(', '));
|
|
118
|
+
// 删除这些分支
|
|
119
|
+
for (const branch of deletedBranches) {
|
|
120
|
+
await git.branch(['-D', branch]);
|
|
121
|
+
console.log(`已删除分支: ${branch}`);
|
|
122
|
+
}
|
|
123
|
+
(0, errors_1.printSuccess)('分支清理完成');
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
throw new errors_1.GitError(error instanceof Error ? error.message : '清理分支时发生未知错误');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function showMenu() {
|
|
130
|
+
try {
|
|
131
|
+
// 准备菜单区域
|
|
132
|
+
display_1.display.prepareForMenu();
|
|
133
|
+
const action = await (0, prompts_1.select)({
|
|
134
|
+
message: '请选择要执行的操作:',
|
|
135
|
+
choices: [
|
|
136
|
+
{
|
|
137
|
+
name: '清理远程已删除的分支',
|
|
138
|
+
value: 'clean',
|
|
139
|
+
description: '清理那些在远程仓库已经被删除的本地分支'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: '退出',
|
|
143
|
+
value: 'exit',
|
|
144
|
+
description: '退出程序'
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
});
|
|
148
|
+
return action;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
(0, errors_1.printError)('菜单选择出错');
|
|
152
|
+
return 'exit';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function main() {
|
|
156
|
+
try {
|
|
157
|
+
// 初始化显示
|
|
158
|
+
display_1.display.initDisplay();
|
|
159
|
+
// 在程序启动时只进行基本的 Git 仓库检查
|
|
160
|
+
await checkGitRepo();
|
|
161
|
+
// 在后台异步获取仓库状态
|
|
162
|
+
showRepoStatus().catch(error => {
|
|
163
|
+
const errorMessage = error instanceof Error ? error.message : '未知错误';
|
|
164
|
+
display_1.display.showError(`获取仓库状态时发生错误: ${errorMessage}`);
|
|
165
|
+
});
|
|
166
|
+
while (true) {
|
|
167
|
+
const action = await showMenu();
|
|
168
|
+
if (action === 'exit') {
|
|
169
|
+
console.log('再见!');
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
if (action === 'clean') {
|
|
173
|
+
await cleanDeletedBranches();
|
|
174
|
+
}
|
|
175
|
+
console.log('\n');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
(0, errors_1.handleError)(error);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// 启动程序
|
|
183
|
+
main().catch(errors_1.handleError);
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { select, confirm } from '@inquirer/prompts';
|
|
2
|
+
import { theme } from './theme';
|
|
3
|
+
// 菜单选项
|
|
4
|
+
const menuChoices = [
|
|
5
|
+
{
|
|
6
|
+
name: '清理远程已删除的分支',
|
|
7
|
+
value: 'clean',
|
|
8
|
+
description: '清理那些在远程仓库已经被删除的本地分支'
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: '退出',
|
|
12
|
+
value: 'exit',
|
|
13
|
+
description: '退出程序'
|
|
14
|
+
}
|
|
15
|
+
];
|
|
16
|
+
// 显示主菜单
|
|
17
|
+
export async function showMainMenu() {
|
|
18
|
+
try {
|
|
19
|
+
const options = {
|
|
20
|
+
message: '请选择要执行的操作:',
|
|
21
|
+
choices: menuChoices,
|
|
22
|
+
pageSize: 10, // 一次显示的选项数量
|
|
23
|
+
loop: true, // 循环滚动
|
|
24
|
+
theme, // 使用自定义主题
|
|
25
|
+
};
|
|
26
|
+
return await select(options);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return 'exit';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// 确认提示
|
|
33
|
+
export async function confirmAction(message) {
|
|
34
|
+
try {
|
|
35
|
+
const options = {
|
|
36
|
+
message,
|
|
37
|
+
default: true,
|
|
38
|
+
theme, // 使用自定义主题
|
|
39
|
+
};
|
|
40
|
+
return await confirm(options);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// 初始化 Git 仓库确认
|
|
47
|
+
export async function confirmInitRepo() {
|
|
48
|
+
return await confirmAction('当前目录不是 Git 仓库,是否要创建?');
|
|
49
|
+
}
|
|
50
|
+
// 删除分支确认
|
|
51
|
+
export async function confirmDeleteBranches(branches) {
|
|
52
|
+
if (branches.length === 0)
|
|
53
|
+
return false;
|
|
54
|
+
const branchList = branches.join('\n - ');
|
|
55
|
+
return await confirmAction(`是否删除以下分支?\n - ${branchList}`);
|
|
56
|
+
}
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
// 自定义颜色
|
|
3
|
+
const colors = {
|
|
4
|
+
primary: chalk.cyan,
|
|
5
|
+
success: chalk.green,
|
|
6
|
+
warning: chalk.yellow,
|
|
7
|
+
error: chalk.red,
|
|
8
|
+
muted: chalk.gray,
|
|
9
|
+
highlight: chalk.blue,
|
|
10
|
+
info: chalk.white,
|
|
11
|
+
};
|
|
12
|
+
// 自定义图标
|
|
13
|
+
const icons = {
|
|
14
|
+
pointer: '❯',
|
|
15
|
+
done: '✔',
|
|
16
|
+
error: '✖',
|
|
17
|
+
waiting: '○',
|
|
18
|
+
};
|
|
19
|
+
// 定义主题
|
|
20
|
+
export const theme = {
|
|
21
|
+
prefix: {
|
|
22
|
+
idle: colors.primary('?'),
|
|
23
|
+
done: colors.success(icons.done),
|
|
24
|
+
},
|
|
25
|
+
spinner: {
|
|
26
|
+
interval: 100,
|
|
27
|
+
frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
|
|
28
|
+
},
|
|
29
|
+
icon: {
|
|
30
|
+
cursor: colors.primary(icons.pointer),
|
|
31
|
+
},
|
|
32
|
+
style: {
|
|
33
|
+
// 答案样式
|
|
34
|
+
answer: (text) => colors.success(text),
|
|
35
|
+
// 问题样式
|
|
36
|
+
message: (text, status) => {
|
|
37
|
+
switch (status) {
|
|
38
|
+
case 'loading':
|
|
39
|
+
return colors.muted(text);
|
|
40
|
+
case 'done':
|
|
41
|
+
return colors.success(text);
|
|
42
|
+
default:
|
|
43
|
+
return colors.primary(text);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
// 错误信息样式
|
|
47
|
+
error: (text) => colors.error(icons.error + ' ' + text),
|
|
48
|
+
// 帮助信息样式
|
|
49
|
+
help: (text) => colors.muted(text),
|
|
50
|
+
// 高亮样式
|
|
51
|
+
highlight: (text) => colors.highlight(text),
|
|
52
|
+
// 选项描述样式
|
|
53
|
+
description: (text) => colors.muted(`\n ${text}`),
|
|
54
|
+
// 禁用选项样式
|
|
55
|
+
disabled: (text) => colors.muted(`${icons.waiting} ${text}`),
|
|
56
|
+
},
|
|
57
|
+
// 帮助提示显示模式:'always' | 'never' | 'auto'
|
|
58
|
+
helpMode: 'auto',
|
|
59
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@harryisfish/gitt",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "这是一个命令行工具,用于帮助你管理 Git 仓库与远端仓库,如保持同步、推送、拉取等。",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gitt": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "tsx src/index.ts",
|
|
12
|
+
"dev": "tsx watch src/index.ts",
|
|
13
|
+
"prepare": "pnpm run build",
|
|
14
|
+
"dev:link": "pnpm build && pnpm link --global",
|
|
15
|
+
"dev:unlink": "pnpm unlink --global"
|
|
16
|
+
},
|
|
17
|
+
"keywords": ["git", "cli", "git-tools", "git-automation"],
|
|
18
|
+
"author": "harryisfish",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/harryisfish/gitt.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/harryisfish/gitt/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/harryisfish/gitt#readme",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.11.24",
|
|
30
|
+
"ts-node": "^10.9.2",
|
|
31
|
+
"tsx": "^4.7.1",
|
|
32
|
+
"typescript": "^5.3.3"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@inquirer/prompts": "^3.3.0",
|
|
36
|
+
"simple-git": "^3.22.0"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=14.0.0"
|
|
40
|
+
},
|
|
41
|
+
"packageManager": "pnpm@9.14.2",
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
]
|
|
47
|
+
}
|