zen-gitsync 1.5.2 → 1.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zen-gitsync",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "一个 git 自动查看差异并提交的工具",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/src/gitCommit.js CHANGED
@@ -28,9 +28,10 @@ function startCountdown(interval) {
28
28
  const duration = formatDuration(remaining);
29
29
 
30
30
  const message = [
31
- chalk.green.bold('🕒 倒计时'),
32
- chalk.cyan(`下次提交: ${formattedTime}`),
33
- chalk.yellow(`剩余时间: ${duration}`),
31
+ `🕒 ${chalk.green.bold('倒计时')}`,
32
+ `工作目录: ${chalk.green(getCwd())}`,
33
+ `下次提交: ${chalk.blue(formattedTime)}`,
34
+ `剩余时间: ${chalk.yellow(duration)}`,
34
35
  chalk.dim('按 Ctrl+C 终止进程')
35
36
  ].join('\n');
36
37
 
@@ -1,596 +1,601 @@
1
- // const chalk = require('chalk');
2
- // const boxen = require('boxen');
3
- // const message = chalk.blue('git diff') + '\n' +
4
- // chalk.red('- line1') + '\n' +
5
- // chalk.green('+ line2') + '\n' +
6
- // chalk.cyan('@@ line diff @@');
7
- //
8
- // const options = {
9
- // padding: 1,
10
- // margin: 1,
11
- // borderStyle: 'round', // 可以选择 'single', 'double', 'round' 等边框样式
12
- // borderColor: 'yellow'
13
- // };
14
- //
15
- // const boxedMessage = boxen(message, options);
16
- // console.log(boxedMessage);
17
- import stringWidth from 'string-width';
18
- import Table from 'cli-table3';
19
- import chalk from 'chalk';
20
- import boxen from "boxen";
21
- import {exec, execSync} from 'child_process'
22
- import os from 'os'
23
- import ora from "ora";
24
- import readline from 'readline'
25
- import path from 'path'
26
-
27
-
28
- const printTableWithHeaderUnderline = (head, content, style) => {
29
- // 获取终端的列数(宽度)
30
- const terminalWidth = process.stdout.columns;
31
-
32
- // 计算表格的宽度,保证至少有 2 个字符留给边框
33
- const tableWidth = terminalWidth - 2; // 左右边框和分隔符的宽度
34
-
35
- // 计算每列的宽度
36
- const colWidths = [tableWidth]; // 只有一列,因此宽度设置为终端宽度
37
-
38
- if (!style) {
39
- style = {
40
- // head: ['cyan'], // 表头文字颜色为cyan
41
- border: [chalk.reset()], // 边框颜色
42
- compact: true, // 启用紧凑模式,去掉不必要的空白
43
- }
44
- }
45
- // 创建表格实例
46
- const table = new Table({
47
- head: [head], // 只有一个表头
48
- colWidths, // 使用动态计算的列宽
49
- style: style,
50
- wordWrap: true, // 启用自动换行
51
- // chars: {
52
- // 'top': '─',
53
- // 'top-mid': '┬',
54
- // 'bottom': '─',
55
- // 'mid': '─',
56
- // 'left': '│',
57
- // 'right': '│'
58
- // },
59
- // chars: {
60
- // 'top': '═', // 顶部边框使用长横线
61
- // 'top-mid': '╤', // 顶部连接符
62
- // 'top-left': '╔', // 左上角
63
- // 'top-right': '╗', // 右上角
64
- // 'bottom': '═', // 底部边框
65
- // 'bottom-mid': '╧', // 底部连接符
66
- // 'bottom-left': '╚',// 左下角
67
- // 'bottom-right': '╝',// 右下角
68
- // 'left': '║', // 左边框
69
- // 'left-mid': '╟', // 左连接符
70
- // 'mid': '═', // 中间分隔符
71
- // 'mid-mid': '╪', // 中间连接符
72
- // 'right': '║', // 右边框
73
- // 'right-mid': '╢', // 右连接符
74
- // 'middle': '│' // 中间内容的边界
75
- // }
76
- });
77
-
78
- // 向表格中添加不同颜色的行
79
- // eg:
80
- // table.push(
81
- // [chalk.red('张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三')],
82
- // [chalk.green('李四')],
83
- // );
84
- content.forEach(item => {
85
- table.push([item]);
86
- })
87
-
88
- console.log(table.toString()); // 输出表格
89
- };
90
-
91
- // printTableWithHeaderUnderline();
92
-
93
- const colors = [
94
- '\x1b[31m', // 红色
95
- '\x1b[32m', // 绿色
96
- '\x1b[33m', // 黄色
97
- '\x1b[34m', // 蓝色
98
- '\x1b[35m', // 紫色
99
- '\x1b[36m', // 青色
100
- ];
101
-
102
- function getRandomColor() {
103
- return `\x1b[0m`;
104
- // const randomIndex = Math.floor(Math.random() * colors.length);
105
- // return colors[randomIndex];
106
- }
107
-
108
- function resetColor() {
109
- return '\x1b[0m';
110
- }
111
-
112
- const calcColor = (commandLine, str) => {
113
- let color = 'reset'
114
- switch (commandLine) {
115
- case 'git status':
116
- if (str.startsWith('\t')) {
117
- color = 'red'
118
- if (str.startsWith('new file:')) {
119
- color = 'red'
120
- }
121
- if (str.startsWith('modified:')) {
122
- color = 'green'
123
- }
124
- if (str.startsWith('deleted:')) {
125
- color = 'red'
126
- }
127
- }
128
- break;
129
- case 'git diff':
130
- // if (str.startsWith('---')) {
131
- // color = 'red'
132
- // }
133
- // if (str.startsWith('+++')) {
134
- // color = 'green'
135
- // }
136
- // if (str.startsWith('@@ ')) {
137
- // color = 'cyan'
138
- // }
139
- break;
140
- }
141
- return color
142
- }
143
- const tableLog = (commandLine, content, type) => {
144
- let handle_commandLine = `> ${commandLine}`
145
- let head = chalk.bold.blue(handle_commandLine)
146
- let style = {
147
- // head: ['cyan'], // 表头文字颜色为cyan
148
- border: [chalk.reset()], // 边框颜色
149
- compact: true, // 启用紧凑模式,去掉不必要的空白
150
- }
151
- switch (type) {
152
- case 'error':
153
- style.head = ['red'];
154
- content = content.toString().split('\n')
155
- head = chalk.bold.red(handle_commandLine)
156
- break;
157
- case 'common':
158
- style.head = ['blue'];
159
- content = content.split('\n')
160
- break;
161
- default:
162
- break;
163
- }
164
- content = content.map(item => {
165
- let fontColor = calcColor(commandLine, item)
166
- let row = item.replaceAll('\t', ' ')
167
- return chalk[fontColor](row)
168
- })
169
-
170
- printTableWithHeaderUnderline(head, content, style)
171
- }
172
- const coloredLog = (...args) => {
173
- // 获取参数内容
174
- const commandLine = args[0];
175
- const content = args[1];
176
- const type = args[2] || 'common';
177
- // console.log(`commandLine ==> `, commandLine)
178
- // console.log(`content ==> `, content)
179
- // console.log(`type ==> `, type)
180
- tableLog(commandLine, content, type);
181
- }
182
- const errorLog = (commandLine, content) => {
183
- // 使用 boxen 绘制带边框的消息
184
- let msg = ` FAIL ${commandLine}
185
- content: ${content} `
186
- const message = chalk.red.bold(msg);
187
- const box = boxen(message);
188
- console.log(box); // 打印带有边框的消息
189
- }
190
-
191
- function execSyncGitCommand(command, options = {}) {
192
- let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = command, log = true} = options
193
- try {
194
- let cwd = getCwd()
195
- const output = execSync(command, {
196
- env: {
197
- ...process.env,
198
- // LANG: 'en_US.UTF-8', // Linux/macOS
199
- // LC_ALL: 'en_US.UTF-8', // Linux/macOS
200
- GIT_CONFIG_PARAMETERS: "'core.quotepath=false'" // 关闭路径转义
201
- }, encoding, maxBuffer, cwd
202
- })
203
- if (options.spinner) {
204
- options.spinner.stop();
205
- }
206
- let result = output.trim()
207
- log && coloredLog(head, result)
208
- return result
209
- } catch (e) {
210
- // console.log(`执行命令出错 ==> `, command, e)
211
- log && coloredLog(command, e, 'error')
212
- throw new Error(e)
213
- }
214
- }
215
-
216
- function execGitCommand(command, options = {}) {
217
- return new Promise((resolve, reject) => {
218
- let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = command, log = true} = options
219
- let cwd = getCwd()
220
-
221
- // setTimeout(() => {
222
- exec(command, {
223
- env: {
224
- ...process.env,
225
- // LANG: 'en_US.UTF-8', // Linux/macOS
226
- // LC_ALL: 'en_US.UTF-8', // Linux/macOS
227
- GIT_CONFIG_PARAMETERS: "'core.quotepath=false'" // 关闭路径转义
228
- },
229
- encoding,
230
- maxBuffer,
231
- cwd
232
- }, (error, stdout, stderr) => {
233
- if (options.spinner) {
234
- options.spinner.stop();
235
- }
236
-
237
- if (stdout) {
238
- log && coloredLog(head, stdout)
239
- }
240
- if (stderr) {
241
- log && coloredLog(head, stderr)
242
- }
243
- if (error) {
244
- log && coloredLog(head, error, 'error')
245
- reject(error)
246
- return
247
- }
248
- resolve({
249
- stdout,
250
- stderr
251
- })
252
- })
253
- // }, 1000)
254
-
255
- })
256
- }
257
-
258
- const getCwd = () => {
259
- const cwdArg = process.argv.find(arg => arg.startsWith('--path')) || process.argv.find(arg => arg.startsWith('--cwd'));
260
- if (cwdArg) {
261
- // console.log(`cwdArg ==> `, cwdArg)
262
- const [, value] = cwdArg.split('=')
263
- // console.log(`value ==> `, value)
264
- return value || process.cwd()
265
- }
266
- return process.cwd()
267
- }
268
- const judgePlatform = () => {
269
- // 判断是否是 Windows 系统
270
- if (os.platform() === 'win32') {
271
- try {
272
- // 设置终端字符编码为 UTF-8
273
- execSync('chcp 65001');
274
- } catch (e) {
275
- console.error('设置字符编码失败:', e.message);
276
- }
277
- }
278
- };
279
- const showHelp = () => {
280
- const helpMessage = `
281
- Usage: g [options]
282
-
283
- Options:
284
- -h, --help Show this help message
285
- --set-default-message=<msg> Set default commit message
286
- get-config Show current configuration
287
- -y Auto commit with default message
288
- -m <message> Commit message (use quotes if message contains spaces)
289
- -m=<message> Commit message (use this form without spaces around '=')
290
- --path=<path> Set custom working directory
291
- --cwd=<path> Set custom working directory
292
- --interval=<seconds> Set interval time for automatic commits (in seconds)
293
- log Show git commit logs
294
- --n=<number> Number of commits to show with --log
295
- --no-diff Skip displaying git diff
296
-
297
- Example:
298
- g -m "Initial commit" Commit with a custom message
299
- g -m=Fix-bug Commit with a custom message (no spaces around '=')
300
- g -y Auto commit with the default message
301
- g -y --interval=600 Commit every 10 minutes (600 seconds)
302
- g --path=/path/to/repo Specify a custom working directory
303
- g log Show recent commit logs
304
- g log --n=5 Show the last 5 commits with --log
305
-
306
- Add auto submit in package.json:
307
- "scripts": {
308
- "g:y": "g -y"
309
- }
310
-
311
- Run in the background across platforms:
312
- Windows:
313
- start /min cmd /k "g -y --path=your-folder --interval=600"
314
-
315
- Linux/macOS:
316
- nohup g -y --path=your-folder --interval=600 > git-autocommit.log 2>&1 &
317
-
318
- Stop all monitoring processes:
319
- Windows: Terminate the Node.js process in the Task Manager.
320
- Linux/macOS:
321
- pkill -f "g -y" # Terminate all auto-commit processes
322
- ps aux | grep "g -y" # Find the specific process ID
323
- kill [PID] # Terminate the specified process
324
- `;
325
-
326
- console.log(helpMessage);
327
- process.exit();
328
- };
329
-
330
- function judgeLog() {
331
- const logArg = process.argv.find(arg => arg === 'log');
332
- if (logArg) {
333
- printGitLog(); // 如果有 log 参数,打印 Git 提交记录
334
- // 打印完成后退出
335
- process.exit();
336
- }
337
- }
338
-
339
- function judgeHelp() {
340
- if (process.argv.includes('-h') || process.argv.includes('--help')) {
341
- showHelp();
342
- }
343
- }
344
-
345
- async function printGitLog() {
346
- let n = 20;
347
- let logArg = process.argv.find(arg => arg.startsWith('--n='));
348
- if (logArg) {
349
- n = parseInt(logArg.split('=')[1], 10);
350
- }
351
- const logCommand = `git log -n ${n} --pretty=format:"%C(green)%h%C(reset) | %C(cyan)%an%C(reset) | %C(yellow)%ad%C(reset) | %C(blue)%D%C(reset) | %C(magenta)%s%C(reset)" --date=format:"%Y-%m-%d %H:%M" --graph --decorate --color`
352
- try {
353
- const logOutput = execSyncGitCommand(logCommand, {
354
- head: `git log`
355
- });
356
- } catch (error) {
357
- console.error('无法获取 Git 提交记录:', error.message);
358
- }
359
- // 打印完成后退出
360
- process.exit();
361
- }
362
-
363
- function exec_exit(exit) {
364
- if (exit) {
365
- process.exit()
366
- }
367
- }
368
-
369
- function judgeUnmerged(statusOutput) {
370
- const hasUnmerged = statusOutput.includes('You have unmerged paths');
371
- if (hasUnmerged) {
372
- errorLog('错误', '存在未合并的文件,请先解决冲突')
373
- process.exit(1);
374
- }
375
- }
376
-
377
- function exec_push({exit, commitMessage}) {
378
- // 执行 git push
379
- // execSyncGitCommand(`git push`);
380
- return new Promise((resolve, reject) => {
381
- const spinner = ora('正在推送代码...').start();
382
- execGitCommand('git push', {
383
- spinner
384
- }).then(({stdout, stderr}) => {
385
- printCommitLog({commitMessage})
386
- resolve({stdout, stderr})
387
- })
388
- });
389
- }
390
-
391
- function printCommitLog({commitMessage}) {
392
- try {
393
- // 获取项目名称(取git仓库根目录名)
394
- const projectRoot = execSyncGitCommand('git rev-parse --show-toplevel', {log: false});
395
- const projectName = chalk.blueBright(path.basename(projectRoot.trim()));
396
-
397
- // 获取当前提交hash(取前7位)
398
- const commitHash = execSyncGitCommand('git rev-parse --short HEAD', {log: false}).trim();
399
- const hashDisplay = chalk.yellow(commitHash);
400
-
401
- // 获取分支信息
402
- const branch = execSyncGitCommand('git branch --show-current', {log: false}).trim();
403
- const branchDisplay = chalk.magenta(branch);
404
-
405
- // 构建信息内容
406
- const message = [
407
- `${chalk.cyan.bold('Project:')} ${projectName}`,
408
- `${chalk.cyan.bold('Commit:')} ${hashDisplay} ${chalk.dim('on')} ${branchDisplay}`,
409
- `${chalk.cyan.bold('Message:')} ${chalk.reset(commitMessage)}`,
410
- `${chalk.cyan.bold('Time:')} ${new Date().toLocaleString()}`
411
- ].join('\n');
412
-
413
- // 使用boxen创建装饰框
414
- const box = boxen(message, {
415
- padding: 1,
416
- margin: 1,
417
- borderStyle: 'round',
418
- borderColor: 'green',
419
- title: chalk.bold.green('✅ COMMIT SUCCESS'),
420
- titleAlignment: 'center',
421
- float: 'left',
422
- textAlignment: 'left'
423
- });
424
-
425
- console.log(box);
426
- } catch (error) {
427
- // 异常处理
428
- const errorBox = boxen(chalk.red(`Failed to get commit details: ${error.message}`), {
429
- borderColor: 'red',
430
- padding: 1
431
- });
432
- console.log(errorBox);
433
- }
434
- }
435
-
436
- async function execPull() {
437
- try {
438
- // 检查是否需要拉取更新
439
- const spinner = ora('正在拉取代码...').start();
440
- await execGitCommand('git pull', {
441
- spinner
442
- })
443
- } catch (e) {
444
- console.log(chalk.yellow('⚠️ 拉取远程更新合并失败,可能存在冲突,请手动处理'));
445
- throw Error(e)
446
- }
447
- }
448
-
449
- function delay(timeout) {
450
- return new Promise(resolve => setTimeout(resolve, timeout));
451
- }
452
-
453
- async function judgeRemote() {
454
- const spinner = ora('正在检查远程更新...').start();
455
- try {
456
- // 检查是否有远程更新
457
- // 先获取远程最新状态
458
- await execGitCommand('git remote update', {
459
- head: 'Fetching remote updates',
460
- log: false
461
- });
462
- // 检查是否需要 pull
463
- const res = await execGitCommand('git rev-list HEAD..@{u} --count', {
464
- head: 'Checking if behind remote',
465
- log: false
466
- });
467
- const behindCount = res.stdout.trim()
468
- const { green, black, bgGreen, white } = chalk;
469
- // 如果本地落后于远程
470
- if (parseInt(behindCount) > 0) {
471
- try {
472
- spinner.stop();
473
- // const spinner_pull = ora('发现远程更新,正在拉取...').start();
474
- await execPull()
475
-
476
- // // 尝试使用 --ff-only 拉取更新
477
- // const res = await execGitCommand('git pull --ff-only', {
478
- // spinner: spinner_pull,
479
- // head: 'Pulling updates'
480
- // });
481
- // console.log(
482
- // bgGreen.white.bold(' SYNC ') +
483
- // green` ➔ ` +
484
- // chalk.blue.bold('远程仓库已同步') +
485
- // green(' ✔')
486
- // );
487
- const message = '已成功同步远程更新'.split('').map((char, i) =>
488
- chalk.rgb(0, 255 - i*10, 0)(char)
489
- ).join('');
490
-
491
- console.log(chalk.bold(`✅ ${message}`));
492
- } catch (pullError) {
493
- // // 如果 --ff-only 拉取失败,尝试普通的 git pull
494
- // console.log(chalk.yellow('⚠️ 无法快进合并,尝试普通合并...'));
495
- // await this.execPull()
496
- throw new Error(pullError)
497
- }
498
- } else {
499
- spinner.stop();
500
- const message = '本地已是最新'.split('').map((char, i) =>
501
- chalk.rgb(0, 255 - i*10, 0)(char)
502
- ).join('');
503
- console.log(chalk.bold(`✅ ${message}`));
504
- }
505
- } catch (e) {
506
- // console.log(`e ==> `, e)
507
- spinner.stop();
508
- throw new Error(e)
509
- }
510
- }
511
-
512
- async function execDiff() {
513
- const no_diff = process.argv.find(arg => arg.startsWith('--no-diff'))
514
- if (!no_diff) {
515
- await execGitCommand('git diff --color=always', {
516
- head: `git diff`
517
- })
518
- }
519
- }
520
-
521
- async function execAddAndCommit({statusOutput, commitMessage, exit}) {
522
- // 检查 -m 参数(提交信息)
523
- const commitMessageArg = process.argv.find(arg => arg.startsWith('-m'));
524
- if (commitMessageArg) {
525
- if (commitMessageArg.includes('=')) {
526
- // 处理 -m=<message> 的情况
527
- commitMessage = commitMessageArg.split('=')[1]?.replace(/^['"]|['"]$/g, '');
528
- } else {
529
- // 处理 -m <message> 的情况
530
- const index = process.argv.indexOf(commitMessageArg);
531
- if (index !== -1 && process.argv[index + 1]) {
532
- commitMessage = process.argv[index + 1]?.replace(/^['"]|['"]$/g, '');
533
- }
534
- }
535
- }
536
-
537
- // 检查命令行参数,判断是否有 -y 参数
538
- const autoCommit = process.argv.includes('-y');
539
-
540
- if (!autoCommit && !commitMessageArg) {
541
- // 如果没有 -y 参数,则等待用户输入提交信息
542
- const rl = readline.createInterface({
543
- input: process.stdin,
544
- output: process.stdout
545
- })
546
-
547
- function rlPromisify(fn) {
548
- return async (...args) => {
549
- return new Promise((resolve, reject) => fn(...args, resolve, reject))
550
- }
551
- }
552
-
553
- const question = rlPromisify(rl.question.bind(rl))
554
- commitMessage = await question('请输入提交信息:') || commitMessage;
555
- }
556
-
557
- statusOutput.includes('(use "git add') && await execGitCommand('git add .')
558
- // 强制添加所有变更
559
- // await execGitCommand('git add -A .');
560
-
561
- // 提交前二次校验
562
- const checkStatus = await execGitCommand('git status --porcelain', {log: false});
563
- if (!checkStatus.stdout.trim()) {
564
- console.log(chalk.yellow('⚠️ 没有检测到可提交的变更'));
565
- // exec_exit(exit)
566
- return;
567
- }
568
-
569
- // 执行 git commit
570
- if (statusOutput.includes('Untracked files:') || statusOutput.includes('Changes not staged for commit') || statusOutput.includes('Changes to be committed')) {
571
- await execGitCommand(`git commit -m "${commitMessage}"`)
572
- }
573
- }
574
-
575
- // 添加时间格式化函数
576
- function formatDuration(ms) {
577
- const totalSeconds = Math.floor(ms / 1000);
578
- const days = Math.floor(totalSeconds / (3600 * 24));
579
- const hours = Math.floor((totalSeconds % (3600 * 24)) / 3600);
580
- const minutes = Math.floor((totalSeconds % 3600) / 60);
581
- const seconds = totalSeconds % 60;
582
-
583
- return [
584
- days && `${days}天`,
585
- hours && `${hours}小时`,
586
- minutes && `${minutes}分`,
587
- `${seconds}秒`
588
- ].filter(Boolean).join('');
589
- }
590
-
591
- export {
592
- coloredLog, errorLog, execSyncGitCommand,
593
- execGitCommand, getCwd, judgePlatform, showHelp, judgeLog, printGitLog,
594
- judgeHelp, exec_exit, judgeUnmerged, delay, formatDuration,
595
- exec_push, execPull, judgeRemote, execDiff, execAddAndCommit
596
- };
1
+ // const chalk = require('chalk');
2
+ // const boxen = require('boxen');
3
+ // const message = chalk.blue('git diff') + '\n' +
4
+ // chalk.red('- line1') + '\n' +
5
+ // chalk.green('+ line2') + '\n' +
6
+ // chalk.cyan('@@ line diff @@');
7
+ //
8
+ // const options = {
9
+ // padding: 1,
10
+ // margin: 1,
11
+ // borderStyle: 'round', // 可以选择 'single', 'double', 'round' 等边框样式
12
+ // borderColor: 'yellow'
13
+ // };
14
+ //
15
+ // const boxedMessage = boxen(message, options);
16
+ // console.log(boxedMessage);
17
+ import stringWidth from 'string-width';
18
+ import Table from 'cli-table3';
19
+ import chalk from 'chalk';
20
+ import boxen from "boxen";
21
+ import {exec, execSync} from 'child_process'
22
+ import os from 'os'
23
+ import ora from "ora";
24
+ import readline from 'readline'
25
+ import path from 'path'
26
+
27
+
28
+ const printTableWithHeaderUnderline = (head, content, style) => {
29
+ // 获取终端的列数(宽度)
30
+ const terminalWidth = process.stdout.columns;
31
+
32
+ // 计算表格的宽度,保证至少有 2 个字符留给边框
33
+ const tableWidth = terminalWidth - 2; // 左右边框和分隔符的宽度
34
+
35
+ // 计算每列的宽度
36
+ const colWidths = [tableWidth]; // 只有一列,因此宽度设置为终端宽度
37
+
38
+ if (!style) {
39
+ style = {
40
+ // head: ['cyan'], // 表头文字颜色为cyan
41
+ border: [chalk.reset()], // 边框颜色
42
+ compact: true, // 启用紧凑模式,去掉不必要的空白
43
+ }
44
+ }
45
+ // 创建表格实例
46
+ const table = new Table({
47
+ head: [head], // 只有一个表头
48
+ colWidths, // 使用动态计算的列宽
49
+ style: style,
50
+ wordWrap: true, // 启用自动换行
51
+ // chars: {
52
+ // 'top': '─',
53
+ // 'top-mid': '┬',
54
+ // 'bottom': '─',
55
+ // 'mid': '─',
56
+ // 'left': '│',
57
+ // 'right': '│'
58
+ // },
59
+ // chars: {
60
+ // 'top': '═', // 顶部边框使用长横线
61
+ // 'top-mid': '╤', // 顶部连接符
62
+ // 'top-left': '╔', // 左上角
63
+ // 'top-right': '╗', // 右上角
64
+ // 'bottom': '═', // 底部边框
65
+ // 'bottom-mid': '╧', // 底部连接符
66
+ // 'bottom-left': '╚',// 左下角
67
+ // 'bottom-right': '╝',// 右下角
68
+ // 'left': '║', // 左边框
69
+ // 'left-mid': '╟', // 左连接符
70
+ // 'mid': '═', // 中间分隔符
71
+ // 'mid-mid': '╪', // 中间连接符
72
+ // 'right': '║', // 右边框
73
+ // 'right-mid': '╢', // 右连接符
74
+ // 'middle': '│' // 中间内容的边界
75
+ // }
76
+ });
77
+
78
+ // 向表格中添加不同颜色的行
79
+ // eg:
80
+ // table.push(
81
+ // [chalk.red('张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三张三')],
82
+ // [chalk.green('李四')],
83
+ // );
84
+ content.forEach(item => {
85
+ table.push([item]);
86
+ })
87
+
88
+ console.log(table.toString()); // 输出表格
89
+ };
90
+
91
+ // printTableWithHeaderUnderline();
92
+
93
+ const colors = [
94
+ '\x1b[31m', // 红色
95
+ '\x1b[32m', // 绿色
96
+ '\x1b[33m', // 黄色
97
+ '\x1b[34m', // 蓝色
98
+ '\x1b[35m', // 紫色
99
+ '\x1b[36m', // 青色
100
+ ];
101
+
102
+ function getRandomColor() {
103
+ return `\x1b[0m`;
104
+ // const randomIndex = Math.floor(Math.random() * colors.length);
105
+ // return colors[randomIndex];
106
+ }
107
+
108
+ function resetColor() {
109
+ return '\x1b[0m';
110
+ }
111
+
112
+ const calcColor = (commandLine, str) => {
113
+ let color = 'reset'
114
+ switch (commandLine) {
115
+ case 'git status':
116
+ if (str.startsWith('\t')) {
117
+ color = 'red'
118
+ if (str.startsWith('new file:')) {
119
+ color = 'red'
120
+ }
121
+ if (str.startsWith('modified:')) {
122
+ color = 'green'
123
+ }
124
+ if (str.startsWith('deleted:')) {
125
+ color = 'red'
126
+ }
127
+ }
128
+ break;
129
+ case 'git diff':
130
+ // if (str.startsWith('---')) {
131
+ // color = 'red'
132
+ // }
133
+ // if (str.startsWith('+++')) {
134
+ // color = 'green'
135
+ // }
136
+ // if (str.startsWith('@@ ')) {
137
+ // color = 'cyan'
138
+ // }
139
+ break;
140
+ }
141
+ return color
142
+ }
143
+ const tableLog = (commandLine, content, type) => {
144
+ let handle_commandLine = `> ${commandLine}`
145
+ let head = chalk.bold.blue(handle_commandLine)
146
+ let style = {
147
+ // head: ['cyan'], // 表头文字颜色为cyan
148
+ border: [chalk.reset()], // 边框颜色
149
+ compact: true, // 启用紧凑模式,去掉不必要的空白
150
+ }
151
+ switch (type) {
152
+ case 'error':
153
+ style.head = ['red'];
154
+ content = content.toString().split('\n')
155
+ head = chalk.bold.red(handle_commandLine)
156
+ break;
157
+ case 'common':
158
+ style.head = ['blue'];
159
+ content = content.split('\n')
160
+ break;
161
+ default:
162
+ break;
163
+ }
164
+ content = content.map(item => {
165
+ let fontColor = calcColor(commandLine, item)
166
+ let row = item.replaceAll('\t', ' ')
167
+ return chalk[fontColor](row)
168
+ })
169
+
170
+ printTableWithHeaderUnderline(head, content, style)
171
+ }
172
+ const coloredLog = (...args) => {
173
+ // 获取参数内容
174
+ const commandLine = args[0];
175
+ const content = args[1];
176
+ const type = args[2] || 'common';
177
+ // console.log(`commandLine ==> `, commandLine)
178
+ // console.log(`content ==> `, content)
179
+ // console.log(`type ==> `, type)
180
+ tableLog(commandLine, content, type);
181
+ }
182
+ const errorLog = (commandLine, content) => {
183
+ // 使用 boxen 绘制带边框的消息
184
+ let msg = ` FAIL ${commandLine}
185
+ content: ${content} `
186
+ const message = chalk.red.bold(msg);
187
+ const box = boxen(message);
188
+ console.log(box); // 打印带有边框的消息
189
+ }
190
+
191
+ function execSyncGitCommand(command, options = {}) {
192
+ let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = command, log = true} = options
193
+ try {
194
+ let cwd = getCwd()
195
+ const output = execSync(command, {
196
+ env: {
197
+ ...process.env,
198
+ // LANG: 'en_US.UTF-8', // Linux/macOS
199
+ // LC_ALL: 'en_US.UTF-8', // Linux/macOS
200
+ GIT_CONFIG_PARAMETERS: "'core.quotepath=false'" // 关闭路径转义
201
+ }, encoding, maxBuffer, cwd
202
+ })
203
+ if (options.spinner) {
204
+ options.spinner.stop();
205
+ }
206
+ let result = output.trim()
207
+ log && coloredLog(head, result)
208
+ return result
209
+ } catch (e) {
210
+ // console.log(`执行命令出错 ==> `, command, e)
211
+ log && coloredLog(command, e, 'error')
212
+ throw new Error(e)
213
+ }
214
+ }
215
+
216
+ function execGitCommand(command, options = {}) {
217
+ return new Promise((resolve, reject) => {
218
+ let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = command, log = true} = options
219
+ let cwd = getCwd()
220
+
221
+ // setTimeout(() => {
222
+ exec(command, {
223
+ env: {
224
+ ...process.env,
225
+ // LANG: 'en_US.UTF-8', // Linux/macOS
226
+ // LC_ALL: 'en_US.UTF-8', // Linux/macOS
227
+ GIT_CONFIG_PARAMETERS: "'core.quotepath=false'" // 关闭路径转义
228
+ },
229
+ encoding,
230
+ maxBuffer,
231
+ cwd
232
+ }, (error, stdout, stderr) => {
233
+ if (options.spinner) {
234
+ options.spinner.stop();
235
+ }
236
+
237
+ if (stdout) {
238
+ log && coloredLog(head, stdout)
239
+ }
240
+ if (stderr) {
241
+ log && coloredLog(head, stderr)
242
+ }
243
+ if (error) {
244
+ log && coloredLog(head, error, 'error')
245
+ reject(error)
246
+ return
247
+ }
248
+ resolve({
249
+ stdout,
250
+ stderr
251
+ })
252
+ })
253
+ // }, 1000)
254
+
255
+ })
256
+ }
257
+
258
+ const getCwd = () => {
259
+ const cwdArg = process.argv.find(arg => arg.startsWith('--path')) || process.argv.find(arg => arg.startsWith('--cwd'));
260
+ if (cwdArg) {
261
+ // console.log(`cwdArg ==> `, cwdArg)
262
+ const [, value] = cwdArg.split('=')
263
+ // console.log(`value ==> `, value)
264
+ return value || process.cwd()
265
+ }
266
+ return process.cwd()
267
+ }
268
+ const judgePlatform = () => {
269
+ // 判断是否是 Windows 系统
270
+ if (os.platform() === 'win32') {
271
+ try {
272
+ // 设置终端字符编码为 UTF-8
273
+ execSync('chcp 65001');
274
+ execSync('git config --global core.autocrlf true');
275
+ // 设置Git不转义路径(避免中文显示为八进制)
276
+ execSync('git config --global core.quotepath false');
277
+ } catch (e) {
278
+ console.error('设置字符编码失败:', e.message);
279
+ }
280
+ }else{
281
+ execSync('git config --global core.autocrlf input');
282
+ }
283
+ };
284
+ const showHelp = () => {
285
+ const helpMessage = `
286
+ Usage: g [options]
287
+
288
+ Options:
289
+ -h, --help Show this help message
290
+ --set-default-message=<msg> Set default commit message
291
+ get-config Show current configuration
292
+ -y Auto commit with default message
293
+ -m <message> Commit message (use quotes if message contains spaces)
294
+ -m=<message> Commit message (use this form without spaces around '=')
295
+ --path=<path> Set custom working directory
296
+ --cwd=<path> Set custom working directory
297
+ --interval=<seconds> Set interval time for automatic commits (in seconds)
298
+ log Show git commit logs
299
+ --n=<number> Number of commits to show with --log
300
+ --no-diff Skip displaying git diff
301
+
302
+ Example:
303
+ g -m "Initial commit" Commit with a custom message
304
+ g -m=Fix-bug Commit with a custom message (no spaces around '=')
305
+ g -y Auto commit with the default message
306
+ g -y --interval=600 Commit every 10 minutes (600 seconds)
307
+ g --path=/path/to/repo Specify a custom working directory
308
+ g log Show recent commit logs
309
+ g log --n=5 Show the last 5 commits with --log
310
+
311
+ Add auto submit in package.json:
312
+ "scripts": {
313
+ "g:y": "g -y"
314
+ }
315
+
316
+ Run in the background across platforms:
317
+ Windows:
318
+ start /min cmd /k "g -y --path=your-folder --interval=600"
319
+
320
+ Linux/macOS:
321
+ nohup g -y --path=your-folder --interval=600 > git-autocommit.log 2>&1 &
322
+
323
+ Stop all monitoring processes:
324
+ Windows: Terminate the Node.js process in the Task Manager.
325
+ Linux/macOS:
326
+ pkill -f "g -y" # Terminate all auto-commit processes
327
+ ps aux | grep "g -y" # Find the specific process ID
328
+ kill [PID] # Terminate the specified process
329
+ `;
330
+
331
+ console.log(helpMessage);
332
+ process.exit();
333
+ };
334
+
335
+ function judgeLog() {
336
+ const logArg = process.argv.find(arg => arg === 'log');
337
+ if (logArg) {
338
+ printGitLog(); // 如果有 log 参数,打印 Git 提交记录
339
+ // 打印完成后退出
340
+ process.exit();
341
+ }
342
+ }
343
+
344
+ function judgeHelp() {
345
+ if (process.argv.includes('-h') || process.argv.includes('--help')) {
346
+ showHelp();
347
+ }
348
+ }
349
+
350
+ async function printGitLog() {
351
+ let n = 20;
352
+ let logArg = process.argv.find(arg => arg.startsWith('--n='));
353
+ if (logArg) {
354
+ n = parseInt(logArg.split('=')[1], 10);
355
+ }
356
+ const logCommand = `git log -n ${n} --pretty=format:"%C(green)%h%C(reset) | %C(cyan)%an%C(reset) | %C(yellow)%ad%C(reset) | %C(blue)%D%C(reset) | %C(magenta)%s%C(reset)" --date=format:"%Y-%m-%d %H:%M" --graph --decorate --color`
357
+ try {
358
+ const logOutput = execSyncGitCommand(logCommand, {
359
+ head: `git log`
360
+ });
361
+ } catch (error) {
362
+ console.error('无法获取 Git 提交记录:', error.message);
363
+ }
364
+ // 打印完成后退出
365
+ process.exit();
366
+ }
367
+
368
+ function exec_exit(exit) {
369
+ if (exit) {
370
+ process.exit()
371
+ }
372
+ }
373
+
374
+ function judgeUnmerged(statusOutput) {
375
+ const hasUnmerged = statusOutput.includes('You have unmerged paths');
376
+ if (hasUnmerged) {
377
+ errorLog('错误', '存在未合并的文件,请先解决冲突')
378
+ process.exit(1);
379
+ }
380
+ }
381
+
382
+ function exec_push({exit, commitMessage}) {
383
+ // 执行 git push
384
+ // execSyncGitCommand(`git push`);
385
+ return new Promise((resolve, reject) => {
386
+ const spinner = ora('正在推送代码...').start();
387
+ execGitCommand('git push', {
388
+ spinner
389
+ }).then(({stdout, stderr}) => {
390
+ printCommitLog({commitMessage})
391
+ resolve({stdout, stderr})
392
+ })
393
+ });
394
+ }
395
+
396
+ function printCommitLog({commitMessage}) {
397
+ try {
398
+ // 获取项目名称(取git仓库根目录名)
399
+ const projectRoot = execSyncGitCommand('git rev-parse --show-toplevel', {log: false});
400
+ const projectName = chalk.blueBright(path.basename(projectRoot.trim()));
401
+
402
+ // 获取当前提交hash(取前7位)
403
+ const commitHash = execSyncGitCommand('git rev-parse --short HEAD', {log: false}).trim();
404
+ const hashDisplay = chalk.yellow(commitHash);
405
+
406
+ // 获取分支信息
407
+ const branch = execSyncGitCommand('git branch --show-current', {log: false}).trim();
408
+ const branchDisplay = chalk.magenta(branch);
409
+
410
+ // 构建信息内容
411
+ const message = [
412
+ `${chalk.cyan.bold('Project:')} ${projectName}`,
413
+ `${chalk.cyan.bold('Commit:')} ${hashDisplay} ${chalk.dim('on')} ${branchDisplay}`,
414
+ `${chalk.cyan.bold('Message:')} ${chalk.reset(commitMessage)}`,
415
+ `${chalk.cyan.bold('Time:')} ${new Date().toLocaleString()}`
416
+ ].join('\n');
417
+
418
+ // 使用boxen创建装饰框
419
+ const box = boxen(message, {
420
+ padding: 1,
421
+ margin: 1,
422
+ borderStyle: 'round',
423
+ borderColor: 'green',
424
+ title: chalk.bold.green('✅ COMMIT SUCCESS'),
425
+ titleAlignment: 'center',
426
+ float: 'left',
427
+ textAlignment: 'left'
428
+ });
429
+
430
+ console.log(box);
431
+ } catch (error) {
432
+ // 异常处理
433
+ const errorBox = boxen(chalk.red(`Failed to get commit details: ${error.message}`), {
434
+ borderColor: 'red',
435
+ padding: 1
436
+ });
437
+ console.log(errorBox);
438
+ }
439
+ }
440
+
441
+ async function execPull() {
442
+ try {
443
+ // 检查是否需要拉取更新
444
+ const spinner = ora('正在拉取代码...').start();
445
+ await execGitCommand('git pull', {
446
+ spinner
447
+ })
448
+ } catch (e) {
449
+ console.log(chalk.yellow('⚠️ 拉取远程更新合并失败,可能存在冲突,请手动处理'));
450
+ throw Error(e)
451
+ }
452
+ }
453
+
454
+ function delay(timeout) {
455
+ return new Promise(resolve => setTimeout(resolve, timeout));
456
+ }
457
+
458
+ async function judgeRemote() {
459
+ const spinner = ora('正在检查远程更新...').start();
460
+ try {
461
+ // 检查是否有远程更新
462
+ // 先获取远程最新状态
463
+ await execGitCommand('git remote update', {
464
+ head: 'Fetching remote updates',
465
+ log: false
466
+ });
467
+ // 检查是否需要 pull
468
+ const res = await execGitCommand('git rev-list HEAD..@{u} --count', {
469
+ head: 'Checking if behind remote',
470
+ log: false
471
+ });
472
+ const behindCount = res.stdout.trim()
473
+ const { green, black, bgGreen, white } = chalk;
474
+ // 如果本地落后于远程
475
+ if (parseInt(behindCount) > 0) {
476
+ try {
477
+ spinner.stop();
478
+ // const spinner_pull = ora('发现远程更新,正在拉取...').start();
479
+ await execPull()
480
+
481
+ // // 尝试使用 --ff-only 拉取更新
482
+ // const res = await execGitCommand('git pull --ff-only', {
483
+ // spinner: spinner_pull,
484
+ // head: 'Pulling updates'
485
+ // });
486
+ // console.log(
487
+ // bgGreen.white.bold(' SYNC ') +
488
+ // green` ` +
489
+ // chalk.blue.bold('远程仓库已同步') +
490
+ // green(' ✔')
491
+ // );
492
+ const message = '已成功同步远程更新'.split('').map((char, i) =>
493
+ chalk.rgb(0, 255 - i*10, 0)(char)
494
+ ).join('');
495
+
496
+ console.log(chalk.bold(`✅ ${message}`));
497
+ } catch (pullError) {
498
+ // // 如果 --ff-only 拉取失败,尝试普通的 git pull
499
+ // console.log(chalk.yellow('⚠️ 无法快进合并,尝试普通合并...'));
500
+ // await this.execPull()
501
+ throw new Error(pullError)
502
+ }
503
+ } else {
504
+ spinner.stop();
505
+ const message = '本地已是最新'.split('').map((char, i) =>
506
+ chalk.rgb(0, 255 - i*10, 0)(char)
507
+ ).join('');
508
+ console.log(chalk.bold(`✅ ${message}`));
509
+ }
510
+ } catch (e) {
511
+ // console.log(`e ==> `, e)
512
+ spinner.stop();
513
+ throw new Error(e)
514
+ }
515
+ }
516
+
517
+ async function execDiff() {
518
+ const no_diff = process.argv.find(arg => arg.startsWith('--no-diff'))
519
+ if (!no_diff) {
520
+ await execGitCommand('git diff --color=always', {
521
+ head: `git diff`
522
+ })
523
+ }
524
+ }
525
+
526
+ async function execAddAndCommit({statusOutput, commitMessage, exit}) {
527
+ // 检查 -m 参数(提交信息)
528
+ const commitMessageArg = process.argv.find(arg => arg.startsWith('-m'));
529
+ if (commitMessageArg) {
530
+ if (commitMessageArg.includes('=')) {
531
+ // 处理 -m=<message> 的情况
532
+ commitMessage = commitMessageArg.split('=')[1]?.replace(/^['"]|['"]$/g, '');
533
+ } else {
534
+ // 处理 -m <message> 的情况
535
+ const index = process.argv.indexOf(commitMessageArg);
536
+ if (index !== -1 && process.argv[index + 1]) {
537
+ commitMessage = process.argv[index + 1]?.replace(/^['"]|['"]$/g, '');
538
+ }
539
+ }
540
+ }
541
+
542
+ // 检查命令行参数,判断是否有 -y 参数
543
+ const autoCommit = process.argv.includes('-y');
544
+
545
+ if (!autoCommit && !commitMessageArg) {
546
+ // 如果没有 -y 参数,则等待用户输入提交信息
547
+ const rl = readline.createInterface({
548
+ input: process.stdin,
549
+ output: process.stdout
550
+ })
551
+
552
+ function rlPromisify(fn) {
553
+ return async (...args) => {
554
+ return new Promise((resolve, reject) => fn(...args, resolve, reject))
555
+ }
556
+ }
557
+
558
+ const question = rlPromisify(rl.question.bind(rl))
559
+ commitMessage = await question('请输入提交信息:') || commitMessage;
560
+ }
561
+
562
+ statusOutput.includes('(use "git add') && await execGitCommand('git add .')
563
+ // 强制添加所有变更
564
+ // await execGitCommand('git add -A .');
565
+
566
+ // 提交前二次校验
567
+ const checkStatus = await execGitCommand('git status --porcelain', {log: false});
568
+ if (!checkStatus.stdout.trim()) {
569
+ console.log(chalk.yellow('⚠️ 没有检测到可提交的变更'));
570
+ // exec_exit(exit)
571
+ return;
572
+ }
573
+
574
+ // 执行 git commit
575
+ if (statusOutput.includes('Untracked files:') || statusOutput.includes('Changes not staged for commit') || statusOutput.includes('Changes to be committed')) {
576
+ await execGitCommand(`git commit -m "${commitMessage}"`)
577
+ }
578
+ }
579
+
580
+ // 添加时间格式化函数
581
+ function formatDuration(ms) {
582
+ const totalSeconds = Math.floor(ms / 1000);
583
+ const days = Math.floor(totalSeconds / (3600 * 24));
584
+ const hours = Math.floor((totalSeconds % (3600 * 24)) / 3600);
585
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
586
+ const seconds = totalSeconds % 60;
587
+
588
+ return [
589
+ days && `${days}天`,
590
+ hours && `${hours}小时`,
591
+ minutes && `${minutes}分`,
592
+ `${seconds}秒`
593
+ ].filter(Boolean).join('');
594
+ }
595
+
596
+ export {
597
+ coloredLog, errorLog, execSyncGitCommand,
598
+ execGitCommand, getCwd, judgePlatform, showHelp, judgeLog, printGitLog,
599
+ judgeHelp, exec_exit, judgeUnmerged, delay, formatDuration,
600
+ exec_push, execPull, judgeRemote, execDiff, execAddAndCommit
601
+ };