zen-gitsync 2.1.3 → 2.1.8

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 (38) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +96 -96
  3. package/index.js +2 -2
  4. package/package.json +69 -66
  5. package/src/config.js +51 -51
  6. package/src/gitCommit.js +261 -261
  7. package/src/ui/public/assets/index-B7Bmz9T9.js +22 -0
  8. package/src/ui/public/assets/index-DNVXEFTm.css +1 -0
  9. package/src/ui/public/assets/{vendor-Dy1zosHw.js → vendor-Bm8yNvvz.js} +1 -1
  10. package/src/ui/public/favicon.svg +26 -26
  11. package/src/ui/public/index.html +3 -3
  12. package/src/ui/public/logo.svg +26 -26
  13. package/src/ui/server/index.js +231 -60
  14. package/src/ui/client/README.md +0 -5
  15. package/src/ui/client/auto-imports.d.ts +0 -10
  16. package/src/ui/client/components.d.ts +0 -33
  17. package/src/ui/client/index.html +0 -13
  18. package/src/ui/client/package.json +0 -28
  19. package/src/ui/client/public/favicon.svg +0 -27
  20. package/src/ui/client/public/logo.svg +0 -27
  21. package/src/ui/client/public/vite.svg +0 -1
  22. package/src/ui/client/src/App.vue +0 -984
  23. package/src/ui/client/src/assets/logo.svg +0 -27
  24. package/src/ui/client/src/components/CommitForm.vue +0 -2158
  25. package/src/ui/client/src/components/GitStatus.vue +0 -1621
  26. package/src/ui/client/src/components/LogList.vue +0 -1937
  27. package/src/ui/client/src/main.ts +0 -7
  28. package/src/ui/client/src/stores/configStore.ts +0 -212
  29. package/src/ui/client/src/stores/gitLogStore.ts +0 -790
  30. package/src/ui/client/src/stores/gitStore.ts +0 -443
  31. package/src/ui/client/src/vite-env.d.ts +0 -1
  32. package/src/ui/client/stats.html +0 -4949
  33. package/src/ui/client/tsconfig.app.json +0 -14
  34. package/src/ui/client/tsconfig.json +0 -7
  35. package/src/ui/client/tsconfig.node.json +0 -24
  36. package/src/ui/client/vite.config.ts +0 -50
  37. package/src/ui/public/assets/index-CekdGwXY.js +0 -20
  38. package/src/ui/public/assets/index-SwSkrgGk.css +0 -1
@@ -1,27 +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" />
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
27
  </svg>
@@ -5,10 +5,10 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Zen-GitSync - Git同步工具</title>
8
- <script type="module" crossorigin src="/assets/index-CekdGwXY.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/vendor-Dy1zosHw.js">
8
+ <script type="module" crossorigin src="/assets/index-B7Bmz9T9.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/vendor-Bm8yNvvz.js">
10
10
  <link rel="stylesheet" crossorigin href="/assets/vendor-Dp0FkvMe.css">
11
- <link rel="stylesheet" crossorigin href="/assets/index-SwSkrgGk.css">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-DNVXEFTm.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="app"></div>
@@ -1,27 +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" />
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
27
  </svg>
@@ -52,7 +52,7 @@ async function startUIServer() {
52
52
  });
53
53
  app.get('/api/status_porcelain', async (req, res) => {
54
54
  try {
55
- const { stdout } = await execGitCommand('git status --porcelain');
55
+ const { stdout } = await execGitCommand('git status --porcelain --untracked-files=all');
56
56
  res.json({ status: stdout });
57
57
  } catch (error) {
58
58
  res.status(500).json({ error: error.message });
@@ -72,67 +72,37 @@ async function startUIServer() {
72
72
  // 获取分支与远程的差异状态(领先/落后提交数)
73
73
  app.get('/api/branch-status', async (req, res) => {
74
74
  try {
75
- // 先获取当前分支名
76
- const { stdout: branchName } = await execGitCommand('git rev-parse --abbrev-ref HEAD');
77
- const currentBranch = branchName.trim();
75
+ // 检查当前目录是否是Git仓库
76
+ if (!isGitRepo) {
77
+ return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
78
+ }
78
79
 
79
- // 获取远程仓库名称
80
- const { stdout: remoteName } = await execGitCommand('git remote');
81
- const remote = remoteName.trim() || 'origin';
80
+ // 获取当前分支
81
+ const { stdout: branchOutput } = await execGitCommand('git symbolic-ref --short HEAD');
82
+ const currentBranch = branchOutput.trim();
82
83
 
83
- // 尝试获取当前分支的上游分支
84
- let upstreamBranch = '';
85
- let hasUpstream = false;
84
+ // 获取上游分支
85
+ const { stdout: upstreamOutput } = await execGitCommand('git rev-parse --abbrev-ref --symbolic-full-name @{u}', { ignoreError: true });
86
86
 
87
- try {
88
- const { stdout: upstream } = await execGitCommand(`git rev-parse --abbrev-ref ${currentBranch}@{upstream}`);
89
- upstreamBranch = upstream.trim();
90
- hasUpstream = true;
91
- } catch (error) {
92
- // 没有上游分支设置,这是正常的
93
- console.log(`分支 ${currentBranch} 没有设置上游分支`);
94
- hasUpstream = false;
87
+ if (!upstreamOutput.trim()) {
88
+ // 没有上游分支
89
+ return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
95
90
  }
96
91
 
97
- // 如果分支没有上游设置,直接返回结果
98
- if (!hasUpstream) {
99
- return res.json({
100
- branch: currentBranch,
101
- remote,
102
- hasUpstream: false,
103
- ahead: 0,
104
- behind: 0
105
- });
106
- }
92
+ const upstreamBranch = upstreamOutput.trim();
107
93
 
108
- // 获取领先和落后的提交数
109
- const { stdout: statusCompare } = await execGitCommand(`git rev-list --count --left-right ${currentBranch}...${upstreamBranch}`);
110
-
111
- // 解析结果,格式通常是 "1 2" 表示领先1个提交,落后2个提交
112
- let ahead = 0;
113
- let behind = 0;
114
-
115
- if (statusCompare && statusCompare.trim()) {
116
- const parts = statusCompare.trim().split(/\s+/);
117
- if (parts.length >= 2) {
118
- ahead = parseInt(parts[0]) || 0;
119
- behind = parseInt(parts[1]) || 0;
120
- } else if (parts.length === 1) {
121
- // 只有一个数字的情况,通常是领先的提交数
122
- ahead = parseInt(parts[0]) || 0;
123
- }
124
- }
94
+ // 获取领先/落后提交数
95
+ const { stdout: aheadBehindOutput } = await execGitCommand(`git rev-list --left-right --count ${currentBranch}...${upstreamBranch}`);
96
+ const [ahead, behind] = aheadBehindOutput.trim().split('\t').map(Number);
125
97
 
126
98
  res.json({
127
- branch: currentBranch,
128
- remote,
129
- upstreamBranch,
130
99
  hasUpstream: true,
100
+ upstreamBranch,
131
101
  ahead,
132
102
  behind
133
103
  });
134
104
  } catch (error) {
135
- console.error("获取分支状态失败:", error);
105
+ console.error('获取分支状态失败:', error);
136
106
  res.status(500).json({ error: error.message });
137
107
  }
138
108
  });
@@ -359,6 +329,132 @@ async function startUIServer() {
359
329
  }
360
330
  });
361
331
 
332
+ // POST接口版本的浏览目录功能
333
+ app.post('/api/browse_directory', async (req, res) => {
334
+ try {
335
+ // 获取要浏览的目录路径,如果没有提供,则使用当前目录
336
+ const directoryPath = req.body.currentPath || process.cwd();
337
+
338
+ try {
339
+ // 读取目录内容
340
+ const items = await fs.readdir(directoryPath, { withFileTypes: true });
341
+
342
+ // 分离文件夹和文件
343
+ const directories = [];
344
+ const files = [];
345
+
346
+ for (const item of items) {
347
+ const fullPath = path.join(directoryPath, item.name);
348
+ if (item.isDirectory()) {
349
+ directories.push({
350
+ name: item.name,
351
+ path: fullPath,
352
+ type: 'directory'
353
+ });
354
+ }
355
+ }
356
+
357
+ // 只返回目录,不返回文件
358
+ directories.sort((a, b) => a.name.localeCompare(b.name));
359
+
360
+ // 获取父目录路径
361
+ const parentPath = path.dirname(directoryPath);
362
+
363
+ // 返回选择的目录路径
364
+ res.json({
365
+ success: true,
366
+ path: directoryPath,
367
+ parentPath: parentPath !== directoryPath ? parentPath : null,
368
+ items: directories
369
+ });
370
+ } catch (error) {
371
+ res.status(400).json({
372
+ success: false,
373
+ error: `无法读取目录 "${directoryPath}": ${error.message}`
374
+ });
375
+ }
376
+ } catch (error) {
377
+ res.status(500).json({
378
+ success: false,
379
+ error: error.message
380
+ });
381
+ }
382
+ });
383
+
384
+ // 获取最近访问的目录列表
385
+ app.get('/api/recent_directories', async (req, res) => {
386
+ try {
387
+ // 尝试从配置中获取最近的目录
388
+ const recentDirs = await configManager.getRecentDirectories();
389
+ res.json({
390
+ success: true,
391
+ directories: recentDirs || []
392
+ });
393
+ } catch (error) {
394
+ res.status(500).json({
395
+ success: false,
396
+ error: error.message
397
+ });
398
+ }
399
+ });
400
+
401
+ // 在资源管理器/访达中打开当前目录
402
+ app.post('/api/open_directory', async (req, res) => {
403
+ try {
404
+ // 获取要打开的目录路径,如果没有提供,则使用当前目录
405
+ const directoryPath = req.body.path || process.cwd();
406
+
407
+ try {
408
+ // 检查目录是否存在
409
+ await fs.access(directoryPath);
410
+
411
+ // 使用open模块打开目录,自动处理不同操作系统
412
+ await open(directoryPath, { wait: false });
413
+
414
+ res.json({
415
+ success: true,
416
+ message: '已在文件管理器中打开目录'
417
+ });
418
+ } catch (error) {
419
+ res.status(400).json({
420
+ success: false,
421
+ error: `无法打开目录 "${directoryPath}": ${error.message}`
422
+ });
423
+ }
424
+ } catch (error) {
425
+ res.status(500).json({
426
+ success: false,
427
+ error: error.message
428
+ });
429
+ }
430
+ });
431
+
432
+ // 保存最近访问的目录
433
+ app.post('/api/save_recent_directory', async (req, res) => {
434
+ try {
435
+ const { path } = req.body;
436
+
437
+ if (!path) {
438
+ return res.status(400).json({
439
+ success: false,
440
+ error: '目录路径不能为空'
441
+ });
442
+ }
443
+
444
+ // 保存到配置
445
+ await configManager.saveRecentDirectory(path);
446
+
447
+ res.json({
448
+ success: true
449
+ });
450
+ } catch (error) {
451
+ res.status(500).json({
452
+ success: false,
453
+ error: error.message
454
+ });
455
+ }
456
+ });
457
+
362
458
  // 获取配置
363
459
  app.get('/api/config/getConfig', async (req, res) => {
364
460
  try {
@@ -702,6 +798,7 @@ async function startUIServer() {
702
798
  const dateFrom = req.query.dateFrom || '';
703
799
  const dateTo = req.query.dateTo || '';
704
800
  const branch = req.query.branch ? req.query.branch.split(',') : [];
801
+ const withParents = req.query.with_parents === 'true';
705
802
 
706
803
  // 构建Git命令选项
707
804
  let commandOptions = [];
@@ -716,7 +813,7 @@ async function startUIServer() {
716
813
  const branchRefs = branch.map(b => b.trim()).join(' ');
717
814
 
718
815
  // 直接将分支名作为命令参数,并确保后面添加 -- 分隔符防止歧义
719
- return executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, req.query.all === 'true');
816
+ return executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, req.query.all === 'true', withParents);
720
817
  }
721
818
 
722
819
  // 如果没有指定分支,则使用--all参数
@@ -760,11 +857,17 @@ async function startUIServer() {
760
857
  // 合并所有命令选项
761
858
  const options = [...commandOptions, limitOption].filter(Boolean).join(' ');
762
859
 
763
- console.log(`执行Git命令: git log --all --pretty=format:"%H|%an|%ae|%ad|%B|%D" --date=short ${options}`);
860
+ // 添加父提交信息的格式
861
+ let formatString = '%H|%an|%ae|%ad|%B|%D';
862
+ if (withParents) {
863
+ formatString = '%H|%an|%ae|%ad|%B|%D|%P';
864
+ }
865
+
866
+ console.log(`执行Git命令: git log --all --pretty=format:"${formatString}" --date=short ${options}`);
764
867
 
765
868
  // 使用 git log 命令获取提交历史
766
869
  let { stdout: logOutput } = await execGitCommand(
767
- `git log --all --pretty=format:"%H|%an|%ae|%ad|%B|%D" --date=short ${options}`
870
+ `git log --all --pretty=format:"${formatString}" --date=short ${options}`
768
871
  );
769
872
 
770
873
  // 获取总提交数量(考虑筛选条件)
@@ -793,7 +896,7 @@ async function startUIServer() {
793
896
  totalCommits = 0;
794
897
  }
795
898
 
796
- processAndSendLogOutput(res, logOutput, totalCommits, page, limit);
899
+ processAndSendLogOutput(res, logOutput, totalCommits, page, limit, withParents);
797
900
  } catch (error) {
798
901
  console.error('获取Git日志失败:', error);
799
902
  res.status(500).json({ error: '获取日志失败: ' + error.message });
@@ -801,7 +904,7 @@ async function startUIServer() {
801
904
  });
802
905
 
803
906
  // 抽取执行Git日志命令的函数
804
- async function executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, isAll) {
907
+ async function executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, isAll, withParents = false) {
805
908
  try {
806
909
  // 构建命令选项
807
910
  const commandOptions = [];
@@ -851,8 +954,14 @@ async function startUIServer() {
851
954
  })
852
955
  .join(' ');
853
956
 
957
+ // 添加父提交信息的格式
958
+ let formatString = '%H|%an|%ae|%ad|%B|%D';
959
+ if (withParents) {
960
+ formatString = '%H|%an|%ae|%ad|%B|%D|%P';
961
+ }
962
+
854
963
  // 构建执行的命令
855
- const command = `git log ${formattedBranchRefs} --pretty=format:"%H|%an|%ae|%ad|%B|%D" --date=short ${options}`;
964
+ const command = `git log ${formattedBranchRefs} --pretty=format:"${formatString}" --date=short ${options}`;
856
965
  console.log(`执行Git命令(带分支引用): ${command}`);
857
966
 
858
967
  // 执行命令
@@ -872,7 +981,7 @@ async function startUIServer() {
872
981
  totalCommits = 0;
873
982
  }
874
983
 
875
- processAndSendLogOutput(res, logOutput, totalCommits, skip / limit + 1, limit);
984
+ processAndSendLogOutput(res, logOutput, totalCommits, skip / limit + 1, limit, withParents);
876
985
  } catch (error) {
877
986
  console.error('执行Git日志命令失败:', error);
878
987
  res.status(500).json({ error: '获取日志失败: ' + error.message });
@@ -880,7 +989,7 @@ async function startUIServer() {
880
989
  }
881
990
 
882
991
  // 抽取处理输出并发送响应的函数
883
- function processAndSendLogOutput(res, logOutput, totalCommits, page, limit) {
992
+ function processAndSendLogOutput(res, logOutput, totalCommits, page, limit, withParents = false) {
884
993
  // 替换提交记录之间的换行符
885
994
  logOutput = logOutput.replace(/\n(?=[a-f0-9]{40}\|)/g, "<<<RECORD_SEPARATOR>>>");
886
995
 
@@ -891,7 +1000,7 @@ async function startUIServer() {
891
1000
  const data = logEntries.map(entry => {
892
1001
  const parts = entry.split('|');
893
1002
  if (parts.length >= 5) {
894
- return {
1003
+ const result = {
895
1004
  hash: parts[0],
896
1005
  author: parts[1],
897
1006
  email: parts[2],
@@ -899,6 +1008,13 @@ async function startUIServer() {
899
1008
  message: parts[4],
900
1009
  branch: parts[5] || ''
901
1010
  };
1011
+
1012
+ // 如果请求了父提交信息,添加到结果中
1013
+ if (withParents && parts[6]) {
1014
+ result.parents = parts[6].split(' ');
1015
+ }
1016
+
1017
+ return result;
902
1018
  }
903
1019
  return null;
904
1020
  }).filter(item => item !== null);
@@ -1179,6 +1295,36 @@ async function startUIServer() {
1179
1295
  }
1180
1296
  });
1181
1297
 
1298
+ // 重置到指定提交(hard)
1299
+ app.post('/api/reset-to-commit', async (req, res) => {
1300
+ try {
1301
+ const { hash } = req.body;
1302
+
1303
+ if (!hash) {
1304
+ return res.status(400).json({
1305
+ success: false,
1306
+ error: '缺少提交哈希参数'
1307
+ });
1308
+ }
1309
+
1310
+ console.log(`执行重置到指定提交操作: hash=${hash}`);
1311
+
1312
+ // 执行git reset --hard命令
1313
+ await execGitCommand(`git reset --hard ${hash}`);
1314
+
1315
+ res.json({
1316
+ success: true,
1317
+ message: `已成功重置到提交 ${hash}`
1318
+ });
1319
+ } catch (error) {
1320
+ console.error('重置到指定提交失败:', error);
1321
+ res.status(500).json({
1322
+ success: false,
1323
+ error: `重置到指定提交失败: ${error.message}`
1324
+ });
1325
+ }
1326
+ });
1327
+
1182
1328
  // 添加清理Git锁定文件的接口
1183
1329
  app.post('/api/remove-lock', async (req, res) => {
1184
1330
  try {
@@ -1263,7 +1409,32 @@ async function startUIServer() {
1263
1409
  }
1264
1410
  });
1265
1411
 
1266
- // 获取所有作者列表
1412
+ // 获取远程仓库URL的API
1413
+ app.get('/api/remote-url', async (req, res) => {
1414
+ try {
1415
+ // 检查当前目录是否是Git仓库
1416
+ if (!isGitRepo) {
1417
+ return res.json({ success: false, error: '当前目录不是Git仓库' });
1418
+ }
1419
+
1420
+ // 执行git命令获取远程仓库URL
1421
+ const { stdout } = await execGitCommand('git config --get remote.origin.url');
1422
+
1423
+ // 返回远程仓库URL
1424
+ res.json({
1425
+ success: true,
1426
+ url: stdout.trim()
1427
+ });
1428
+ } catch (error) {
1429
+ console.error('获取远程仓库URL失败:', error);
1430
+ res.json({
1431
+ success: false,
1432
+ error: error.message || '获取远程仓库URL失败'
1433
+ });
1434
+ }
1435
+ });
1436
+
1437
+ // 获取所有作者列表
1267
1438
  app.get('/api/authors', async (req, res) => {
1268
1439
  try {
1269
1440
  // 使用git命令获取所有提交者,不依赖Unix命令
@@ -1394,7 +1565,7 @@ async function startUIServer() {
1394
1565
  const { stdout: statusOutput } = await execGitCommand('git status');
1395
1566
 
1396
1567
  // 获取porcelain格式状态
1397
- const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain');
1568
+ const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain --untracked-files=all');
1398
1569
 
1399
1570
  // 广播到所有连接的客户端
1400
1571
  io.emit('git_status_update', {
@@ -1,5 +0,0 @@
1
- # Vue 3 + TypeScript + Vite
2
-
3
- This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4
-
5
- Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
@@ -1,10 +0,0 @@
1
- /* eslint-disable */
2
- /* prettier-ignore */
3
- // @ts-nocheck
4
- // noinspection JSUnusedGlobalSymbols
5
- // Generated by unplugin-auto-import
6
- // biome-ignore lint: disable
7
- export {}
8
- declare global {
9
-
10
- }
@@ -1,33 +0,0 @@
1
- /* eslint-disable */
2
- // @ts-nocheck
3
- // Generated by unplugin-vue-components
4
- // Read more: https://github.com/vuejs/core/pull/3399
5
- // biome-ignore lint: disable
6
- export {}
7
-
8
- /* prettier-ignore */
9
- declare module 'vue' {
10
- export interface GlobalComponents {
11
- CommitForm: typeof import('./src/components/CommitForm.vue')['default']
12
- ElAlert: typeof import('element-plus/es')['ElAlert']
13
- ElButton: typeof import('element-plus/es')['ElButton']
14
- ElCard: typeof import('element-plus/es')['ElCard']
15
- ElDialog: typeof import('element-plus/es')['ElDialog']
16
- ElEmpty: typeof import('element-plus/es')['ElEmpty']
17
- ElForm: typeof import('element-plus/es')['ElForm']
18
- ElFormItem: typeof import('element-plus/es')['ElFormItem']
19
- ElIcon: typeof import('element-plus/es')['ElIcon']
20
- ElInput: typeof import('element-plus/es')['ElInput']
21
- ElOption: typeof import('element-plus/es')['ElOption']
22
- ElRow: typeof import('element-plus/es')['ElRow']
23
- ElSelect: typeof import('element-plus/es')['ElSelect']
24
- ElSwitch: typeof import('element-plus/es')['ElSwitch']
25
- ElTag: typeof import('element-plus/es')['ElTag']
26
- ElTooltip: typeof import('element-plus/es')['ElTooltip']
27
- GitStatus: typeof import('./src/components/GitStatus.vue')['default']
28
- LogList: typeof import('./src/components/LogList.vue')['default']
29
- }
30
- export interface ComponentCustomProperties {
31
- vLoading: typeof import('element-plus/es')['ElLoadingDirective']
32
- }
33
- }
@@ -1,13 +0,0 @@
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
- </head>
9
- <body>
10
- <div id="app"></div>
11
- <script type="module" src="/src/main.ts"></script>
12
- </body>
13
- </html>
@@ -1,28 +0,0 @@
1
- {
2
- "name": "client",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vue-tsc -b && vite build",
9
- "preview": "vite preview"
10
- },
11
- "dependencies": {
12
- "@gitgraph/js": "^1.4.0",
13
- "element-plus": "^2.9.9",
14
- "pinia": "^3.0.2",
15
- "socket.io-client": "^4.8.1",
16
- "vue": "^3.5.13"
17
- },
18
- "devDependencies": {
19
- "@vitejs/plugin-vue": "^5.2.2",
20
- "@vue/tsconfig": "^0.7.0",
21
- "rollup-plugin-visualizer": "^5.14.0",
22
- "typescript": "~5.7.2",
23
- "unplugin-auto-import": "^19.2.0",
24
- "unplugin-vue-components": "^28.5.0",
25
- "vite": "^6.3.1",
26
- "vue-tsc": "^2.2.8"
27
- }
28
- }