zen-gitsync 1.5.4 → 2.0.1

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.
Files changed (34) hide show
  1. package/README.md +9 -0
  2. package/package.json +12 -3
  3. package/src/config.js +2 -1
  4. package/src/gitCommit.js +14 -1
  5. package/src/ui/client/README.md +5 -0
  6. package/src/ui/client/auto-imports.d.ts +10 -0
  7. package/src/ui/client/components.d.ts +31 -0
  8. package/src/ui/client/index.html +13 -0
  9. package/src/ui/client/package.json +27 -0
  10. package/src/ui/client/public/favicon.svg +27 -0
  11. package/src/ui/client/public/logo.svg +27 -0
  12. package/src/ui/client/public/vite.svg +1 -0
  13. package/src/ui/client/src/App.vue +539 -0
  14. package/src/ui/client/src/assets/logo.svg +27 -0
  15. package/src/ui/client/src/components/CommitForm.vue +904 -0
  16. package/src/ui/client/src/components/GitStatus.vue +403 -0
  17. package/src/ui/client/src/components/LogList.vue +270 -0
  18. package/src/ui/client/src/main.ts +4 -0
  19. package/src/ui/client/src/vite-env.d.ts +1 -0
  20. package/src/ui/client/stats.html +4949 -0
  21. package/src/ui/client/tsconfig.app.json +14 -0
  22. package/src/ui/client/tsconfig.json +7 -0
  23. package/src/ui/client/tsconfig.node.json +24 -0
  24. package/src/ui/client/vite.config.ts +48 -0
  25. package/src/ui/public/assets/index-BcTk2R6G.js +9 -0
  26. package/src/ui/public/assets/index-ChUZ1vPG.css +1 -0
  27. package/src/ui/public/assets/vendor-BAXrrwNU.js +41 -0
  28. package/src/ui/public/assets/vendor-Dp0FkvMe.css +1 -0
  29. package/src/ui/public/favicon.svg +27 -0
  30. package/src/ui/public/index.html +16 -0
  31. package/src/ui/public/logo.svg +27 -0
  32. package/src/ui/public/vite.svg +1 -0
  33. package/src/ui/server/index.js +447 -0
  34. package/src/utils/index.js +59 -19
@@ -0,0 +1,27 @@
1
+ <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- 主分支 -->
3
+ <line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
4
+
5
+ <!-- 特性分支 -->
6
+ <path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
7
+
8
+ <!-- 开发分支 -->
9
+ <path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
10
+
11
+ <!-- 修复分支 -->
12
+ <path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
13
+
14
+ <!-- 合并点 -->
15
+ <path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
16
+
17
+ <!-- 节点 -->
18
+ <circle cx="40" cy="40" r="10" fill="#34495e" />
19
+ <circle cx="40" cy="70" r="10" fill="#34495e" />
20
+ <circle cx="40" cy="100" r="10" fill="#34495e" />
21
+ <circle cx="40" cy="130" r="10" fill="#34495e" />
22
+ <circle cx="40" cy="145" r="10" fill="#34495e" />
23
+ <circle cx="40" cy="160" r="10" fill="#34495e" />
24
+ <circle cx="80" cy="130" r="10" fill="#3498db" />
25
+ <circle cx="100" cy="160" r="10" fill="#2ecc71" />
26
+ <circle cx="120" cy="160" r="10" fill="#e74c3c" />
27
+ </svg>
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Zen-GitSync - Git同步工具</title>
8
+ <script type="module" crossorigin src="/assets/index-BcTk2R6G.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/vendor-BAXrrwNU.js">
10
+ <link rel="stylesheet" crossorigin href="/assets/vendor-Dp0FkvMe.css">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-ChUZ1vPG.css">
12
+ </head>
13
+ <body>
14
+ <div id="app"></div>
15
+ </body>
16
+ </html>
@@ -0,0 +1,27 @@
1
+ <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- 主分支 -->
3
+ <line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
4
+
5
+ <!-- 特性分支 -->
6
+ <path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
7
+
8
+ <!-- 开发分支 -->
9
+ <path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
10
+
11
+ <!-- 修复分支 -->
12
+ <path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
13
+
14
+ <!-- 合并点 -->
15
+ <path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
16
+
17
+ <!-- 节点 -->
18
+ <circle cx="40" cy="40" r="10" fill="#34495e" />
19
+ <circle cx="40" cy="70" r="10" fill="#34495e" />
20
+ <circle cx="40" cy="100" r="10" fill="#34495e" />
21
+ <circle cx="40" cy="130" r="10" fill="#34495e" />
22
+ <circle cx="40" cy="145" r="10" fill="#34495e" />
23
+ <circle cx="40" cy="160" r="10" fill="#34495e" />
24
+ <circle cx="80" cy="130" r="10" fill="#3498db" />
25
+ <circle cx="100" cy="160" r="10" fill="#2ecc71" />
26
+ <circle cx="120" cy="160" r="10" fill="#e74c3c" />
27
+ </svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
@@ -0,0 +1,447 @@
1
+ import express from 'express';
2
+ import { createServer } from 'http';
3
+ import { fileURLToPath } from 'url';
4
+ import path from 'path';
5
+ import { execGitCommand } from '../../utils/index.js';
6
+ import open from 'open';
7
+ import config from '../../config.js';
8
+ // import { Server } from 'socket.io';
9
+ // import { exec } from 'child_process';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ const configManager = config; // 确保 configManager 可用
14
+
15
+ async function startUIServer() {
16
+ const app = express();
17
+ const httpServer = createServer(app);
18
+ // const io = new Server(httpServer);
19
+
20
+ // 添加全局中间件来解析JSON请求体
21
+ app.use(express.json());
22
+
23
+ // // 启动前端Vue应用
24
+ // const clientPath = path.join(__dirname, '../client');
25
+ // console.log(`正在启动前端应用,路径: ${clientPath}`);
26
+
27
+ // const vueProcess = exec('npm run dev', { cwd: clientPath }, (error) => {
28
+ // if (error) {
29
+ // console.error('启动前端应用失败:', error);
30
+ // }
31
+ // });
32
+
33
+ // vueProcess.stdout.on('data', (data) => {
34
+ // console.log(`前端输出: ${data}`);
35
+ // });
36
+
37
+ // vueProcess.stderr.on('data', (data) => {
38
+ // console.error(`前端错误: ${data}`);
39
+ // });
40
+
41
+ // 静态文件服务
42
+ app.use(express.static(path.join(__dirname, '../public')));
43
+
44
+ // API路由
45
+ app.get('/api/status', async (req, res) => {
46
+ try {
47
+ const { stdout } = await execGitCommand('git status');
48
+ res.json({ status: stdout });
49
+ } catch (error) {
50
+ res.status(500).json({ error: error.message });
51
+ }
52
+ });
53
+ app.get('/api/status_porcelain', async (req, res) => {
54
+ try {
55
+ const { stdout } = await execGitCommand('git status --porcelain');
56
+ res.json({ status: stdout });
57
+ } catch (error) {
58
+ res.status(500).json({ error: error.message });
59
+ }
60
+ });
61
+
62
+ // 获取当前分支
63
+ app.get('/api/branch', async (req, res) => {
64
+ try {
65
+ const { stdout } = await execGitCommand('git rev-parse --abbrev-ref HEAD');
66
+ res.json({ branch: stdout.trim() });
67
+ } catch (error) {
68
+ res.status(500).json({ error: error.message });
69
+ }
70
+ });
71
+
72
+ // 获取所有分支
73
+ app.get('/api/branches', async (req, res) => {
74
+ try {
75
+ // 获取本地分支
76
+ const { stdout: localBranches } = await execGitCommand('git branch --format="%(refname:short)"');
77
+ // 获取远程分支(过滤掉 origin/HEAD 和 origin)
78
+ const { stdout: remoteBranches } = await execGitCommand('git branch -r --format="%(refname:short)"');
79
+
80
+ // 合并并去重
81
+ const allBranches = [...new Set([
82
+ ...localBranches.split('\n')
83
+ .filter(Boolean)
84
+ .filter(b => !b.startsWith('* ')), // 过滤掉HEAD指针
85
+ ...remoteBranches.split('\n')
86
+ .filter(Boolean)
87
+ .filter(b => b.includes('/')) // 过滤掉单纯的 origin
88
+ .map(b => b.split('/')[1]) // 提取真正的分支名称
89
+ ])];
90
+
91
+ res.json({ branches: allBranches });
92
+ } catch (error) {
93
+ console.error('获取分支列表失败:', error);
94
+ res.status(500).json({ error: error.message });
95
+ }
96
+ });
97
+
98
+ // 创建新分支
99
+ app.post('/api/create-branch', express.json(), async (req, res) => {
100
+ try {
101
+ const { newBranchName, baseBranch } = req.body;
102
+
103
+ if (!newBranchName) {
104
+ return res.status(400).json({ success: false, error: '分支名称不能为空' });
105
+ }
106
+
107
+ // 构建创建分支的命令
108
+ let command = `git branch ${newBranchName}`;
109
+
110
+ // 如果指定了基础分支,则基于该分支创建
111
+ if (baseBranch) {
112
+ command = `git branch ${newBranchName} ${baseBranch}`;
113
+ }
114
+
115
+ // 执行创建分支命令
116
+ await execGitCommand(command);
117
+
118
+ // 切换到新创建的分支
119
+ await execGitCommand(`git checkout ${newBranchName}`);
120
+
121
+ res.json({ success: true, branch: newBranchName });
122
+ } catch (error) {
123
+ console.error('创建分支失败:', error);
124
+ res.status(500).json({ success: false, error: error.message });
125
+ }
126
+ });
127
+
128
+ // 切换分支
129
+ app.post('/api/checkout', async (req, res) => {
130
+ try {
131
+ const { branch } = req.body;
132
+ if (!branch) {
133
+ return res.status(400).json({ success: false, error: '分支名称不能为空' });
134
+ }
135
+
136
+ // 执行分支切换
137
+ await execGitCommand(`git checkout ${branch}`);
138
+
139
+ res.json({ success: true });
140
+ } catch (error) {
141
+ console.error('切换分支失败:', error);
142
+ res.status(500).json({ success: false, error: error.message });
143
+ }
144
+ });
145
+
146
+ // 获取Git用户配置信息
147
+ app.get('/api/user-info', async (req, res) => {
148
+ try {
149
+ // 获取用户名
150
+ const { stdout: userName } = await execGitCommand('git config user.name');
151
+ // 获取用户邮箱
152
+ const { stdout: userEmail } = await execGitCommand('git config user.email');
153
+
154
+ res.json({
155
+ name: userName.trim(),
156
+ email: userEmail.trim()
157
+ });
158
+ } catch (error) {
159
+ res.status(500).json({ error: error.message });
160
+ }
161
+ });
162
+
163
+ // 新增获取当前工作目录接口
164
+ app.get('/api/current_directory', async (req, res) => {
165
+ try {
166
+ const directory = process.cwd();
167
+
168
+ // 检查当前目录是否是Git仓库
169
+ try {
170
+ await execGitCommand('git rev-parse --is-inside-work-tree');
171
+ } catch (error) {
172
+ return res.status(400).json({
173
+ error: '当前目录不是一个Git仓库',
174
+ directory,
175
+ isGitRepo: false
176
+ });
177
+ }
178
+
179
+ res.json({
180
+ directory,
181
+ isGitRepo: true
182
+ });
183
+ } catch (error) {
184
+ res.status(500).json({ error: error.message });
185
+ }
186
+ });
187
+
188
+ // 获取配置
189
+ app.get('/api/config/getConfig', async (req, res) => {
190
+ try {
191
+ const config = await configManager.loadConfig()
192
+ res.json(config)
193
+ } catch (error) {
194
+ res.status(500).json({ error: error.message })
195
+ }
196
+ })
197
+
198
+ // 保存描述模板
199
+ app.post('/api/config/save-template', express.json(), async (req, res) => {
200
+ try {
201
+ const { template, type } = req.body
202
+
203
+ if (!template || !type) {
204
+ return res.status(400).json({ success: false, error: '缺少必要参数' })
205
+ }
206
+
207
+ const config = await configManager.loadConfig()
208
+
209
+ if (type === 'description') {
210
+ // 确保描述模板数组存在
211
+ if (!config.descriptionTemplates) {
212
+ config.descriptionTemplates = []
213
+ }
214
+
215
+ // 检查是否已存在相同模板
216
+ if (!config.descriptionTemplates.includes(template)) {
217
+ config.descriptionTemplates.push(template)
218
+ await configManager.saveConfig(config)
219
+ }
220
+ } else if (type === 'scope') {
221
+ // 确保作用域模板数组存在
222
+ if (!config.scopeTemplates) {
223
+ config.scopeTemplates = []
224
+ }
225
+
226
+ // 检查是否已存在相同模板
227
+ if (!config.scopeTemplates.includes(template)) {
228
+ config.scopeTemplates.push(template)
229
+ await configManager.saveConfig(config)
230
+ }
231
+ } else {
232
+ return res.status(400).json({ success: false, error: '不支持的模板类型' })
233
+ }
234
+
235
+ res.json({ success: true })
236
+ } catch (error) {
237
+ res.status(500).json({ success: false, error: error.message })
238
+ }
239
+ })
240
+
241
+ // 删除描述模板
242
+ app.post('/api/config/delete-template', express.json(), async (req, res) => {
243
+ try {
244
+ const { template, type } = req.body
245
+
246
+ if (!template || !type) {
247
+ return res.status(400).json({ success: false, error: '缺少必要参数' })
248
+ }
249
+
250
+ const config = await configManager.loadConfig()
251
+
252
+ if (type === 'description') {
253
+ // 确保描述模板数组存在
254
+ if (config.descriptionTemplates) {
255
+ const index = config.descriptionTemplates.indexOf(template)
256
+ if (index !== -1) {
257
+ config.descriptionTemplates.splice(index, 1)
258
+ await configManager.saveConfig(config)
259
+ }
260
+ }
261
+ } else if (type === 'scope') {
262
+ // 确保作用域模板数组存在
263
+ if (config.scopeTemplates) {
264
+ const index = config.scopeTemplates.indexOf(template)
265
+ if (index !== -1) {
266
+ config.scopeTemplates.splice(index, 1)
267
+ await configManager.saveConfig(config)
268
+ }
269
+ }
270
+ } else {
271
+ return res.status(400).json({ success: false, error: '不支持的模板类型' })
272
+ }
273
+
274
+ res.json({ success: true })
275
+ } catch (error) {
276
+ res.status(500).json({ success: false, error: error.message })
277
+ }
278
+ })
279
+
280
+ // 提交更改
281
+ app.post('/api/commit', express.json(), async (req, res) => {
282
+ try {
283
+ const { message, hasNewlines, noVerify } = req.body;
284
+
285
+ // 构建 git commit 命令
286
+ let commitCommand = 'git commit';
287
+
288
+ // 如果消息包含换行符,使用文件方式提交
289
+ if (hasNewlines) {
290
+ // 创建临时文件存储提交信息
291
+ const tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
292
+ await fs.writeFile(tempFile, message);
293
+ commitCommand += ` -F "${tempFile}"`;
294
+ } else {
295
+ // 否则直接在命令行中提供消息
296
+ commitCommand += ` -m "${message}"`;
297
+ }
298
+
299
+ // 添加 --no-verify 参数
300
+ if (noVerify) {
301
+ commitCommand += ' --no-verify';
302
+ }
303
+
304
+ console.log(`commitCommand ==>`, commitCommand);
305
+ // 执行提交命令
306
+ await execGitCommand(commitCommand);
307
+
308
+ // 如果使用了临时文件,删除它
309
+ if (hasNewlines) {
310
+ const tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
311
+ await fs.unlink(tempFile).catch(() => {});
312
+ }
313
+
314
+ res.json({ success: true });
315
+ } catch (error) {
316
+ res.status(500).json({ success: false, error: error.message });
317
+ }
318
+ });
319
+
320
+ // 添加 add 接口
321
+ app.post('/api/add', async (req, res) => {
322
+ try {
323
+ // 执行 git add . 命令添加所有更改
324
+ await execGitCommand('git add .');
325
+ res.json({ success: true });
326
+ } catch (error) {
327
+ res.status(500).json({ success: false, error: error.message });
328
+ }
329
+ });
330
+
331
+ // 推送更改
332
+ app.post('/api/push', async (req, res) => {
333
+ try {
334
+ await execGitCommand('git push');
335
+ res.json({ success: true });
336
+ } catch (error) {
337
+ res.status(500).json({ error: error.message });
338
+ }
339
+ });
340
+
341
+ // 获取日志
342
+ app.get('/api/log', async (req, res) => {
343
+ try {
344
+ // 获取请求参数中的数量限制,默认为100
345
+ const limit = req.query.all === 'true' ? '' : '-n 100';
346
+
347
+ // 修改 git log 命令,添加 %ae 参数来获取作者邮箱
348
+ const { stdout } = await execGitCommand(`git log --all --pretty=format:"%h|%an|%ae|%ad|%s|%D" --date=short ${limit}`);
349
+ const logs = stdout.split('\n').map(line => {
350
+ const [hash, author, email, date, message, refs] = line.split('|');
351
+
352
+ // 从引用信息中提取分支名称
353
+ let branch = null;
354
+ if (refs) {
355
+ // 提取所有引用信息,而不仅仅是第一个匹配
356
+ branch = refs.trim();
357
+ }
358
+
359
+ return { hash, author, email, date, message, branch };
360
+ });
361
+ res.json(logs);
362
+ } catch (error) {
363
+ res.status(500).json({ error: error.message });
364
+ }
365
+ });
366
+
367
+ // 获取文件差异
368
+ app.get('/api/diff', async (req, res) => {
369
+ try {
370
+ const filePath = req.query.file;
371
+
372
+ if (!filePath) {
373
+ return res.status(400).json({ error: '缺少文件路径参数' });
374
+ }
375
+
376
+ // 执行git diff命令获取文件差异
377
+ const { stdout } = await execGitCommand(`git diff -- "${filePath}"`);
378
+
379
+ res.json({ diff: stdout });
380
+ } catch (error) {
381
+ res.status(500).json({ error: error.message });
382
+ }
383
+ });
384
+
385
+ // Socket.io 实时更新
386
+ // io.on('connection', (socket) => {
387
+ // console.log('客户端已连接');
388
+
389
+ // // 定期发送状态更新
390
+ // const statusInterval = setInterval(async () => {
391
+ // try {
392
+ // const { stdout } = await execGitCommand('git status');
393
+ // socket.emit('status_update', { status: stdout });
394
+ // } catch (error) {
395
+ // console.error('状态更新错误:', error);
396
+ // }
397
+ // }, 5000);
398
+
399
+ // socket.on('disconnect', () => {
400
+ // clearInterval(statusInterval);
401
+ // console.log('客户端已断开连接');
402
+ // });
403
+ // });
404
+
405
+ // 启动服务器
406
+ const PORT = 3000;
407
+ httpServer.listen(PORT, () => {
408
+ console.log(`后端API服务器已启动: http://localhost:${PORT}`);
409
+ open(`http://localhost:${PORT}`);
410
+ }).on('error', async (err) => {
411
+ if (err.code === 'EADDRINUSE') {
412
+ console.log(`端口 ${PORT} 被占用,尝试其他端口...`);
413
+ let newPort = PORT + 1;
414
+ while (newPort < PORT + 100) {
415
+ try {
416
+ await new Promise((resolve, reject) => {
417
+ httpServer.listen(newPort, () => {
418
+ console.log(`后端API服务器已启动: http://localhost:${newPort}`);
419
+ open(`http://localhost:${newPort}`);
420
+ resolve();
421
+ }).on('error', (e) => {
422
+ if (e.code === 'EADDRINUSE') {
423
+ console.log(`端口 ${newPort} 也被占用,继续尝试...`);
424
+ newPort++;
425
+ reject(e);
426
+ } else {
427
+ reject(e);
428
+ }
429
+ });
430
+ });
431
+ break;
432
+ } catch (e) {
433
+ if (newPort >= PORT + 100) {
434
+ console.error('无法找到可用端口,请手动指定端口');
435
+ process.exit(1);
436
+ }
437
+ }
438
+ }
439
+ } else {
440
+ console.error('服务器启动失败:', err);
441
+ process.exit(1);
442
+ }
443
+ });
444
+ }
445
+
446
+ export default startUIServer;
447
+
@@ -290,30 +290,34 @@ Options:
290
290
  -h, --help Show this help message
291
291
  --set-default-message=<msg> Set default commit message
292
292
  get-config Show current configuration
293
- -y Auto commit with default message
294
- -m <message> Commit message (use quotes if message contains spaces)
295
- -m=<message> Commit message (use this form without spaces around '=')
296
- --path=<path> Set custom working directory
297
- --cwd=<path> Set custom working directory
298
- --interval=<seconds> Set interval time for automatic commits (in seconds)
299
- log Show git commit logs
300
- --n=<number> Number of commits to show with --log
301
- --no-diff Skip displaying git diff
302
- addScript Add "g:y": "g -y" to package.json scripts
293
+ -y Auto commit with default message
294
+ -m <message> Commit message (use quotes if message contains spaces)
295
+ -m=<message> Commit message (use this form without spaces around '=')
296
+ --path=<path> Set custom working directory
297
+ --cwd=<path> Set custom working directory
298
+ --interval=<seconds> Set interval time for automatic commits (in seconds)
299
+ log Show git commit logs
300
+ --n=<number> Number of commits to show with --log
301
+ --no-diff Skip displaying git diff
302
+ addScript Add "g:y": "g -y" to package.json scripts
303
+ addResetScript Add "g:reset": "git reset --hard origin/<current-branch>" to package.json scripts
304
+ ui Launch graphical user interface (v2.0.0)
303
305
 
304
306
  Example:
305
307
  g -m "Initial commit" Commit with a custom message
306
- g -m=Fix-bug Commit with a custom message (no spaces around '=')
307
- g -y Auto commit with the default message
308
- g -y --interval=600 Commit every 10 minutes (600 seconds)
309
- g --path=/path/to/repo Specify a custom working directory
310
- g log Show recent commit logs
311
- g log --n=5 Show the last 5 commits with --log
312
- g addScript Add auto commit script to package.json
308
+ g -m=Fix-bug Commit with a custom message (no spaces around '=')
309
+ g -y Auto commit with the default message
310
+ g -y --interval=600 Commit every 10 minutes (600 seconds)
311
+ g --path=/path/to/repo Specify a custom working directory
312
+ g log Show recent commit logs
313
+ g log --n=5 Show the last 5 commits with --log
314
+ g addScript Add auto commit script to package.json
315
+ g addResetScript Add reset script to package.json
313
316
 
314
317
  Add auto submit in package.json:
315
318
  "scripts": {
316
- "g:y": "g -y"
319
+ "g:y": "g -y",
320
+ "g:reset": "git reset --hard origin/<current-branch>"
317
321
  }
318
322
 
319
323
  Run in the background across platforms:
@@ -323,6 +327,9 @@ Run in the background across platforms:
323
327
  Linux/macOS:
324
328
  nohup g -y --path=your-folder --interval=600 > git-autocommit.log 2>&1 &
325
329
 
330
+ Start GUI interface:
331
+ g ui
332
+
326
333
  Stop all monitoring processes:
327
334
  Windows: Terminate the Node.js process in the Task Manager.
328
335
  Linux/macOS:
@@ -626,10 +633,43 @@ async function addScriptToPackageJson() {
626
633
  }
627
634
  }
628
635
 
636
+ async function addResetScriptToPackageJson() {
637
+ try {
638
+ // 读取当前目录的 package.json
639
+ const packagePath = path.join(process.cwd(), 'package.json');
640
+ const packageJson = JSON.parse(await fs.readFile(packagePath, 'utf8'));
641
+
642
+ // 确保有 scripts 部分
643
+ if (!packageJson.scripts) {
644
+ packageJson.scripts = {};
645
+ }
646
+
647
+ // 获取当前分支名
648
+ const branch = execSyncGitCommand('git branch --show-current', {log: false}).trim();
649
+
650
+ // 添加 g:reset 命令
651
+ if (!packageJson.scripts['g:reset']) {
652
+ packageJson.scripts['g:reset'] = `git reset --hard origin/${branch}`;
653
+ // 写回文件
654
+ await fs.writeFile(packagePath, JSON.stringify(packageJson, null, 2));
655
+ console.log(chalk.green(`✓ 成功添加 g:reset 脚本到 package.json (重置到 origin/${branch})`));
656
+ } else {
657
+ console.log(chalk.yellow('⚠️ g:reset 脚本已存在'));
658
+ }
659
+ } catch (error) {
660
+ if (error.code === 'ENOENT') {
661
+ console.error(chalk.red('❌ 当前目录下未找到 package.json 文件'));
662
+ } else {
663
+ console.error(chalk.red('❌ 添加脚本失败:'), error.message);
664
+ }
665
+ process.exit(1);
666
+ }
667
+ }
668
+
629
669
  export {
630
670
  coloredLog, errorLog, execSyncGitCommand,
631
671
  execGitCommand, getCwd, judgePlatform, showHelp, judgeLog, printGitLog,
632
672
  judgeHelp, exec_exit, judgeUnmerged, delay, formatDuration,
633
673
  exec_push, execPull, judgeRemote, execDiff, execAddAndCommit,
634
- addScriptToPackageJson
674
+ addScriptToPackageJson, addResetScriptToPackageJson
635
675
  };