zen-gitsync 1.3.0 → 1.3.5

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 CHANGED
@@ -15,10 +15,8 @@ npm install -g zen-gitsync
15
15
 
16
16
  1. 在终端中,输入 `g` 并按回车。
17
17
  2. 输入提交信息,按回车确认提交。
18
- 3. 工具将自动执行以下操作:
19
- - `git add .`
20
- - `git commit -m "你的提交信息"`
21
- - `git push`
18
+ 3. 工具将自动查看远程分支并自动提交
19
+
22
20
 
23
21
  ### 示例:
24
22
  #### 交互式提交:
@@ -30,18 +28,30 @@ $ g
30
28
  ```bash
31
29
  $ g -y
32
30
  ```
31
+ #### 传入message直接提交:
32
+ ```bash
33
+ $ g -m <message>
34
+ $ g -m=<message>
35
+ ```
36
+
37
+ #### 查看帮助
38
+ ```shell
39
+ $ g -h
40
+ $ g --help
41
+ ```
42
+
33
43
  #### 定时执行自动提交,默认间隔1小时
34
44
  ```bash
35
45
  $ g -y --interval
36
- $ g -y --interval=秒数
46
+ $ g -y --interval=<seconds>
37
47
  ```
38
48
  #### 指定目录提交
39
49
  ```bash
40
- $ g --path=./
50
+ $ g --path=<path>
41
51
  ```
42
52
 
43
53
  ```bash
44
- $ g --cwd=./
54
+ $ g --cwd=<path>
45
55
  ```
46
56
 
47
57
  #### 添加项目script
@@ -62,3 +72,9 @@ start /min cmd /k "g -y --path=你要同步的文件夹 --interval"
62
72
  ```shell
63
73
  $ g --no-diff
64
74
  ```
75
+
76
+ #### 格式化打印git log
77
+ ```shell
78
+ $ g log
79
+ $ g log --n=5
80
+ ```
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import config from './src/config.js'
2
+ export default config
package/package.json CHANGED
@@ -1,21 +1,27 @@
1
1
  {
2
2
  "name": "zen-gitsync",
3
- "version": "1.3.0",
3
+ "version": "1.3.5",
4
4
  "description": "一个 git 自动查看差异并提交的工具",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "g": "node ./src/gitCommit.js",
9
+ "g:m": "node ./src/gitCommit.js -m='test'",
10
+ "g:m2": "node ./src/gitCommit.js -m testmsg",
9
11
  "g:y": "node ./src/gitCommit.js -y",
10
12
  "g:y:interval": "node ./src/gitCommit.js -y --interval",
11
13
  "g:y:interval10": "node ./src/gitCommit.js -y --interval=10",
12
14
  "g:cwd": "node ./src/gitCommit.js --path=./",
13
- "g:no-diff": "node ./src/gitCommit.js --no-diff"
15
+ "g:cwd2": "node ./src/gitCommit.js --path=../",
16
+ "g:no-diff": "node ./src/gitCommit.js --no-diff",
17
+ "g:log": "node ./src/gitCommit.js log",
18
+ "g:log5": "node ./src/gitCommit.js log --n=5",
19
+ "g:h": "node ./src/gitCommit.js -h"
14
20
  },
15
21
  "files": [
16
- "src/gitCommit.js",
17
- "src/utils/index.js",
18
- "package.json"
22
+ "src/*",
23
+ "package.json",
24
+ "index.js"
19
25
  ],
20
26
  "bin": {
21
27
  "g": "./src/gitCommit.js"
package/src/config.js ADDED
@@ -0,0 +1,3 @@
1
+ export default {
2
+ "defaultCommitMessage": "submit"
3
+ }
package/src/gitCommit.js CHANGED
@@ -2,13 +2,53 @@
2
2
 
3
3
  import {exec, execSync} from 'child_process'
4
4
  import os from 'os'
5
- import {coloredLog} from './utils/index.js';
5
+ import {coloredLog, errorLog} from './utils/index.js';
6
6
  import readline from 'readline'
7
7
  import ora from 'ora';
8
8
  import chalk from 'chalk';
9
9
  import boxen from 'boxen';
10
+ import config from './config.js'
11
+
12
+ const {defaultCommitMessage} = config
10
13
 
11
14
  let timer = null
15
+ const showHelp = () => {
16
+ const helpMessage = `
17
+ Usage: g [options]
18
+
19
+ Options:
20
+ -h, --help Show this help message
21
+ -y Auto commit with default message
22
+ -m <message> Commit message (use quotes if message contains spaces)
23
+ -m=<message> Commit message (use this form without spaces around '=')
24
+ --path=<path> Set custom working directory
25
+ --cwd=<path> Set custom working directory
26
+ --interval=<seconds> Set interval time for automatic commits (in seconds)
27
+ log Show git commit logs
28
+ --n=<number> Number of commits to show with --log
29
+ --no-diff Skip displaying git diff
30
+
31
+ Example:
32
+ g -m "Initial commit" Commit with a custom message
33
+ g -m=Fix-bug Commit with a custom message (no spaces around '=')
34
+ g -y Auto commit with the default message
35
+ g -y --interval=600 Commit every 10 minutes (600 seconds)
36
+ g --path=/path/to/repo Specify a custom working directory
37
+ g log Show recent commit logs
38
+ g log --n=5 Show the last 5 commits with --log
39
+
40
+ Add auto submit in package.json:
41
+ "scripts": {
42
+ "g:y": "g -y"
43
+ }
44
+
45
+ Start a background process for automatic commits:
46
+ start /min cmd /k "g -y --path=your-folder --interval"
47
+ `;
48
+
49
+ console.log(helpMessage);
50
+ process.exit();
51
+ };
12
52
 
13
53
  const judgePlatform = () => {
14
54
  // 判断是否是 Windows 系统
@@ -51,7 +91,7 @@ class GitCommit {
51
91
  constructor(options) {
52
92
  this.statusOutput = null
53
93
  this.exit = options.exit
54
- this.commitMessage = `提交`
94
+ this.commitMessage = defaultCommitMessage
55
95
  this.init()
56
96
  }
57
97
 
@@ -60,53 +100,193 @@ class GitCommit {
60
100
  process.exit()
61
101
  }
62
102
  }
63
-
64
- async init() {
65
- try {
66
- judgePlatform()
67
-
68
- this.statusOutput = this.execSyncGitCommand('git status')
69
- if (this.statusOutput.includes('nothing to commit, working tree clean')) {
70
- if (this.statusOutput.includes('use "git push')) {
71
- this.exec_push()
72
- } else {
73
- this.exec_exit();
103
+ judgeLog() {
104
+ const logArg = process.argv.find(arg => arg === 'log');
105
+ if (logArg) {
106
+ this.printGitLog(); // 如果有 log 参数,打印 Git 提交记录
107
+ return true;
108
+ }
109
+ }
110
+ judgeHelp() {
111
+ if (process.argv.includes('-h') || process.argv.includes('--help')) {
112
+ showHelp();
113
+ return true;
114
+ }
115
+ }
116
+ execDiff() {
117
+ const no_diff = process.argv.find(arg => arg.startsWith('--no-diff'))
118
+ if (!no_diff) {
119
+ this.execSyncGitCommand('git diff --color=always', {
120
+ head: `git diff`
121
+ })
122
+ }
123
+ }
124
+ async execAddAndCommit() {
125
+ // 检查 -m 参数(提交信息)
126
+ const commitMessageArg = process.argv.find(arg => arg.startsWith('-m'));
127
+ if (commitMessageArg) {
128
+ if (commitMessageArg.includes('=')) {
129
+ // 处理 -m=<message> 的情况
130
+ this.commitMessage = commitMessageArg.split('=')[1]?.replace(/^['"]|['"]$/g, '') || defaultCommitMessage;
131
+ } else {
132
+ // 处理 -m <message> 的情况
133
+ const index = process.argv.indexOf(commitMessageArg);
134
+ if (index !== -1 && process.argv[index + 1]) {
135
+ this.commitMessage = process.argv[index + 1]?.replace(/^['"]|['"]$/g, '') || defaultCommitMessage;
74
136
  }
75
- return
76
137
  }
138
+ }
77
139
 
78
- const no_diff = process.argv.find(arg => arg.startsWith('--no-diff'))
79
- if(!no_diff){
80
- this.execSyncGitCommand('git diff')
81
- }
140
+ // 检查命令行参数,判断是否有 -y 参数
141
+ const autoCommit = process.argv.includes('-y');
142
+
143
+ if (!autoCommit && !commitMessageArg) {
144
+ // 如果没有 -y 参数,则等待用户输入提交信息
145
+ this.commitMessage = await question('请输入提交信息:');
146
+ }
82
147
 
83
- // 检查命令行参数,判断是否有 -y 参数
84
- const autoCommit = process.argv.includes('-y');
148
+ this.statusOutput.includes('(use "git add') && this.execSyncGitCommand('git add .')
85
149
 
86
- if (!autoCommit) {
87
- // 如果没有 -y 参数,则等待用户输入提交信息
88
- this.commitMessage = await question('请输入提交信息:');
150
+ // 执行 git commit
151
+ if (this.statusOutput.includes('Untracked files:') || this.statusOutput.includes('Changes not staged for commit') || this.statusOutput.includes('Changes to be committed')) {
152
+ this.execSyncGitCommand(`git commit -m "${this.commitMessage || defaultCommitMessage}"`)
153
+ }
154
+ }
155
+ async judgeRemote() {
156
+ try{
157
+ const spinner = ora('正在检查远程更新...').start();
158
+ // 检查是否有远程更新
159
+ // 先获取远程最新状态
160
+ await this.execGitCommand('git remote update', {
161
+ head: 'Fetching remote updates',
162
+ log: false
163
+ });
164
+ // 检查是否需要 pull
165
+ const res = await this.execGitCommand('git rev-list HEAD..@{u} --count', {
166
+ head: 'Checking if behind remote',
167
+ log: false
168
+ });
169
+ const behindCount = res.stdout.trim()
170
+ // 如果本地落后于远程
171
+ if (parseInt(behindCount) > 0) {
172
+ try {
173
+ spinner.stop();
174
+ // const spinner_pull = ora('发现远程更新,正在拉取...').start();
175
+ await this.execPull()
176
+
177
+ // // 尝试使用 --ff-only 拉取更新
178
+ // const res = await this.execGitCommand('git pull --ff-only', {
179
+ // spinner: spinner_pull,
180
+ // head: 'Pulling updates'
181
+ // });
182
+ console.log(chalk.green('✓ 已成功同步远程更新'));
183
+ } catch (pullError) {
184
+ // // 如果 --ff-only 拉取失败,尝试普通的 git pull
185
+ // console.log(chalk.yellow('⚠️ 无法快进合并,尝试普通合并...'));
186
+ // await this.execPull()
187
+ throw new Error(pullError)
188
+ }
189
+ } else {
190
+ spinner.stop();
191
+ console.log(chalk.green('✓ 本地已是最新'));
89
192
  }
193
+ }catch (e) {
194
+ // console.log(`e ==> `, e)
195
+ spinner.stop();
196
+ throw new Error(e)
197
+ }
198
+ }
199
+ async execPull(){
200
+ try{
201
+ // 检查是否需要拉取更新
202
+ const spinner = ora('正在拉取代码...').start();
203
+ await this.execGitCommand('git pull', {
204
+ spinner
205
+ })
206
+ }catch (e) {
207
+ console.log(chalk.yellow('⚠️ 拉取远程更新合并失败,可能存在冲突,请手动处理'));
208
+ throw Error(e)
209
+ }
210
+ }
211
+
212
+ async init() {
213
+ try {
214
+ judgePlatform()
215
+
216
+ // 检查是否有 log 参数
217
+ if(this.judgeLog()) return
90
218
 
91
- // 执行 git add .
92
- this.statusOutput.includes('(use "git add') && this.execSyncGitCommand('git add .')
219
+ // 检查帮助参数
220
+ if(this.judgeHelp()) return
93
221
 
94
- // 执行 git commit
95
- if (this.statusOutput.includes('Untracked files:') || this.statusOutput.includes('Changes not staged for commit') || this.statusOutput.includes('Changes to be committed')) {
96
- this.execSyncGitCommand(`git commit -m "${this.commitMessage || '提交'}"`)
222
+ this.statusOutput = this.execSyncGitCommand('git status')
223
+ const hasUnmerged = this.statusOutput.includes('You have unmerged paths');
224
+ if(hasUnmerged){
225
+ errorLog('错误', '存在未合并的文件,请先解决冲突')
226
+ process.exit(1);
97
227
  }
228
+ // 先检查本地是否有未提交的更改
229
+ const hasLocalChanges = !this.statusOutput.includes('nothing to commit, working tree clean');
230
+ if (hasLocalChanges) {
231
+ // 检查是否有 --no-diff 参数
232
+ this.execDiff()
98
233
 
99
- // 检查是否需要拉取更新
100
- this.statusOutput.includes('use "git pull') && this.execSyncGitCommand('git pull')
234
+ await this.execAddAndCommit()
101
235
 
102
- this.exec_push()
236
+ this.statusOutput.includes('use "git pull') && await this.execPull()
103
237
 
238
+ // 检查是否有远程更新
239
+ await this.judgeRemote() // 等待 judgeRemote 完成
104
240
 
241
+ this.exec_push()
242
+ }else{
243
+ if (this.statusOutput.includes('use "git push')) {
244
+ this.exec_push()
245
+ } else if (this.statusOutput.includes('use "git pull')) {
246
+ await this.execPull()
247
+ } else {
248
+ await this.judgeRemote() // 等待 judgeRemote 完成
249
+ this.exec_exit();
250
+ }
251
+ }
105
252
  } catch (e) {
106
- console.log(`e ==> `, e)
253
+ // console.log(`e.message ==> `, e.message)
254
+ // 应该提供更具体的错误信息
255
+ // console.error('Git operation failed:', e.message);
256
+ // 考虑不同错误类型的处理
257
+ if (e.message.includes('not a git repository')) {
258
+ errorLog('错误', '当前目录不是git仓库')
259
+ } else if (e.message.includes('Permission denied')) {
260
+ errorLog('错误', '权限不足,请检查git配置')
261
+ }
262
+ process.exit(1);
107
263
  }
108
264
  }
109
265
 
266
+ async printGitLog() {
267
+ let n = 20;
268
+ let logArg = process.argv.find(arg => arg.startsWith('--n='));
269
+ if (logArg) {
270
+ n = parseInt(logArg.split('=')[1], 10);
271
+ }
272
+ 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`
273
+ try {
274
+ const logOutput = this.execSyncGitCommand(logCommand, {
275
+ head: `git log`
276
+ });
277
+ // // 格式化输出 Git 提交记录
278
+ // const box = boxen(chalk.green.bold(logOutput), {
279
+ // borderStyle: 'round',
280
+ // borderColor: 'cyan',
281
+ // backgroundColor: 'black',
282
+ // });
283
+ // console.log(box); // 打印优雅的 Git 提交记录
284
+ } catch (error) {
285
+ console.error('无法获取 Git 提交记录:', error.message);
286
+ }
287
+ this.exec_exit(); // 打印完成后退出
288
+ }
289
+
110
290
  exec_push() {
111
291
  // 执行 git push
112
292
  // this.execSyncGitCommand(`git push`);
@@ -117,7 +297,7 @@ class GitCommit {
117
297
 
118
298
  // 使用 boxen 绘制带边框的消息
119
299
  let msg = ` SUCCESS: 提交完成
120
- message: ${this.commitMessage || '提交'}
300
+ message: ${this.commitMessage || defaultCommitMessage}
121
301
  time: ${new Date().toLocaleString()} `
122
302
  const message = chalk.green.bold(msg);
123
303
  const box = boxen(message, {
@@ -134,39 +314,47 @@ class GitCommit {
134
314
 
135
315
  execSyncGitCommand(command, options = {}) {
136
316
  try {
137
- let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024} = options
317
+ let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = command, log = true} = options
138
318
  let cwd = getCwd()
139
319
  const output = execSync(command, {encoding, maxBuffer, cwd})
140
- if(options.spinner){
320
+ if (options.spinner) {
141
321
  options.spinner.stop();
142
322
  }
143
323
  let result = output.trim()
144
- coloredLog(command, result)
324
+ log && coloredLog(head, result)
145
325
  return result
146
326
  } catch (e) {
147
- console.log(`执行命令出错 ==> `, command, e)
327
+ // console.log(`执行命令出错 ==> `, command, e)
328
+ log && coloredLog(command, e, 'error')
148
329
  throw new Error(e)
149
330
  }
150
331
  }
151
332
 
152
333
  execGitCommand(command, options = {}, callback) {
153
- let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024} = options
154
- let cwd = getCwd()
155
- exec(command, {encoding, maxBuffer, cwd}, (error, stdout, stderr) => {
156
- if(options.spinner){
157
- options.spinner.stop();
158
- }
159
- if (error) {
160
- coloredLog(command, error, 'error')
161
- return
162
- }
163
- if (stdout) {
164
- coloredLog(command, stdout)
165
- }
166
- if (stderr) {
167
- coloredLog(command, stderr)
168
- }
169
- callback && callback(error, stdout, stderr)
334
+ return new Promise((resolve, reject) => {
335
+ let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = command, log = true} = options
336
+ let cwd = getCwd()
337
+ exec(command, {encoding, maxBuffer, cwd}, (error, stdout, stderr) => {
338
+ if (options.spinner) {
339
+ options.spinner.stop();
340
+ }
341
+ if (error) {
342
+ log && coloredLog(head, error, 'error')
343
+ reject(error)
344
+ return
345
+ }
346
+ if (stdout) {
347
+ log && coloredLog(head, stdout)
348
+ }
349
+ if (stderr) {
350
+ log && coloredLog(head, stderr)
351
+ }
352
+ resolve({
353
+ stdout,
354
+ stderr
355
+ })
356
+ callback && callback(error, stdout, stderr)
357
+ })
170
358
  })
171
359
  }
172
360
  }
@@ -17,6 +17,7 @@
17
17
  import stringWidth from 'string-width';
18
18
  import Table from 'cli-table3';
19
19
  import chalk from 'chalk';
20
+ import boxen from "boxen";
20
21
 
21
22
  const printTableWithHeaderUnderline = (head, content, style) => {
22
23
  // 获取终端的列数(宽度)
@@ -31,7 +32,7 @@ const printTableWithHeaderUnderline = (head, content, style) => {
31
32
  if (!style) {
32
33
  style = {
33
34
  // head: ['cyan'], // 表头文字颜色为cyan
34
- // border: ['white'], // 边框颜色
35
+ border: [chalk.reset()], // 边框颜色
35
36
  compact: true, // 启用紧凑模式,去掉不必要的空白
36
37
  }
37
38
  }
@@ -41,6 +42,23 @@ const printTableWithHeaderUnderline = (head, content, style) => {
41
42
  colWidths, // 使用动态计算的列宽
42
43
  style: style,
43
44
  wordWrap: true, // 启用自动换行
45
+ // chars: {
46
+ // 'top': '═', // 顶部边框使用长横线
47
+ // 'top-mid': '╤', // 顶部连接符
48
+ // 'top-left': '╔', // 左上角
49
+ // 'top-right': '╗', // 右上角
50
+ // 'bottom': '═', // 底部边框
51
+ // 'bottom-mid': '╧', // 底部连接符
52
+ // 'bottom-left': '╚',// 左下角
53
+ // 'bottom-right': '╝',// 右下角
54
+ // 'left': '║', // 左边框
55
+ // 'left-mid': '╟', // 左连接符
56
+ // 'mid': '═', // 中间分隔符
57
+ // 'mid-mid': '╪', // 中间连接符
58
+ // 'right': '║', // 右边框
59
+ // 'right-mid': '╢', // 右连接符
60
+ // 'middle': '│' // 中间内容的边界
61
+ // }
44
62
  });
45
63
 
46
64
  // 向表格中添加不同颜色的行
@@ -94,15 +112,15 @@ const calcColor = (commandLine, str) => {
94
112
  }
95
113
  break;
96
114
  case 'git diff':
97
- if (str.startsWith('-')) {
98
- color = 'red'
99
- }
100
- if (str.startsWith('+')) {
101
- color = 'green'
102
- }
103
- if (str.startsWith('@@ ')) {
104
- color = 'cyan'
105
- }
115
+ // if (str.startsWith('---')) {
116
+ // color = 'red'
117
+ // }
118
+ // if (str.startsWith('+++')) {
119
+ // color = 'green'
120
+ // }
121
+ // if (str.startsWith('@@ ')) {
122
+ // color = 'cyan'
123
+ // }
106
124
  break;
107
125
  }
108
126
  return color
@@ -112,7 +130,7 @@ const tableLog = (commandLine, content, type) => {
112
130
  let head = chalk.bold.blue(handle_commandLine)
113
131
  let style = {
114
132
  // head: ['cyan'], // 表头文字颜色为cyan
115
- // border: ['whiteBright'], // 边框颜色
133
+ border: [chalk.reset()], // 边框颜色
116
134
  compact: true, // 启用紧凑模式,去掉不必要的空白
117
135
  }
118
136
  switch (type) {
@@ -121,7 +139,7 @@ const tableLog = (commandLine, content, type) => {
121
139
  content = content.toString().split('\n')
122
140
  head = chalk.bold.red(handle_commandLine)
123
141
  break;
124
- case 'log':
142
+ case 'common':
125
143
  style.head = ['blue'];
126
144
  content = content.split('\n')
127
145
  break;
@@ -140,12 +158,15 @@ const coloredLog = (...args) => {
140
158
  // 获取参数内容
141
159
  const commandLine = args[0];
142
160
  const content = args[1];
143
- const type = args[2] || 'log';
144
-
145
-
161
+ const type = args[2] || 'common';
146
162
  tableLog(commandLine, content, type);
147
-
148
- // console.log(`args ==> `, args)
149
-
150
163
  }
151
- export {coloredLog};
164
+ const errorLog = (commandLine, content) => {
165
+ // 使用 boxen 绘制带边框的消息
166
+ let msg = ` FAIL ${commandLine}
167
+ content: ${content} `
168
+ const message = chalk.red.bold(msg);
169
+ const box = boxen(message);
170
+ console.log(box); // 打印带有边框的消息
171
+ }
172
+ export {coloredLog, errorLog};