zen-gitsync 2.13.23 → 2.13.24

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 (41) hide show
  1. package/package.json +1 -1
  2. package/scripts/README_COLOR_CONVERTER.md +196 -196
  3. package/scripts/README_FONTSIZE_CONVERTER.md +278 -278
  4. package/scripts/README_SPACING_CONVERTER.md +126 -126
  5. package/scripts/README_STYLE_VARS.md +180 -180
  6. package/src/gitCommit.js +2 -2
  7. package/src/ui/public/assets/{AppVersionBadge-BUS8w2f8.js → AppVersionBadge-CaVphp8E.js} +1 -1
  8. package/src/ui/public/assets/{BranchSelector-Dm7nENer.js → BranchSelector-B91Ylbas.js} +1 -1
  9. package/src/ui/public/assets/{CommandConsole-C1W9ZQkE.js → CommandConsole-bK4iwZvI.js} +2 -2
  10. package/src/ui/public/assets/{CommitForm-C76W1ZZH.js → CommitForm-zOCxmZ84.js} +1 -1
  11. package/src/ui/public/assets/{CustomCommandManager-JKx8JYqn.js → CustomCommandManager-C1lRrp5z.js} +1 -1
  12. package/src/ui/public/assets/{EditorView-CriUYDvi.js → EditorView-DYo2oYtk.js} +0 -0
  13. package/src/ui/public/assets/{FileDiffViewer-DNWsiIGt.js → FileDiffViewer-Ce17WOZ-.js} +1 -1
  14. package/src/ui/public/assets/{FlowExecutionViewer-CTDh2bCD.js → FlowExecutionViewer-CRK6lIul.js} +1 -1
  15. package/src/ui/public/assets/{FlowOrchestrationWorkspace-Chah2aGN.js → FlowOrchestrationWorkspace-BDw7ThWp.js} +1 -1
  16. package/src/ui/public/assets/{GitStatus-DP-DQsiS.js → GitStatus-DN4_m6sO.js} +1 -1
  17. package/src/ui/public/assets/{LogList-Z76_5USQ.js → LogList-BshRTCrE.js} +1 -1
  18. package/src/ui/public/assets/{ProjectStartupButton-C5oF4unq.js → ProjectStartupButton-C-dXxxMO.js} +1 -1
  19. package/src/ui/public/assets/{RemoteRepoCard-B-fGjb8Y.js → RemoteRepoCard-DDZcP5-V.js} +1 -1
  20. package/src/ui/public/assets/{ResetToRemoteButton-BixSoEVs.js → ResetToRemoteButton-TXJzvBxE.js} +1 -1
  21. package/src/ui/public/assets/{SourceMapView-C9HZiJ9N.js → SourceMapView-BUkB6h41.js} +1 -1
  22. package/src/ui/public/assets/{TemplateManager-C7SuKq0f.js → TemplateManager-DFVzDiu0.js} +1 -1
  23. package/src/ui/public/assets/{UserInputNode-CYy0UpzR.js → UserInputNode-CXyV1xG4.js} +1 -1
  24. package/src/ui/public/assets/{WorkbenchView-A9Oik707.js → WorkbenchView-CEYhsa4O.js} +1 -1
  25. package/src/ui/public/assets/{index-DPSGvy34.js → index-BGmxuKoN.js} +2 -2
  26. package/src/ui/public/favicon.svg +75 -75
  27. package/src/ui/public/index.html +18 -18
  28. package/src/ui/public/logo.svg +74 -74
  29. package/src/ui/server/index.js +3 -3
  30. package/src/ui/server/routes/branchStatus.js +4 -4
  31. package/src/ui/server/routes/config.js +8 -8
  32. package/src/ui/server/routes/exec.js +7 -1
  33. package/src/ui/server/routes/fs.js +2 -2
  34. package/src/ui/server/routes/git/diff.js +17 -16
  35. package/src/ui/server/routes/git/diffUtils.js +21 -2
  36. package/src/ui/server/routes/git/stash.js +38 -42
  37. package/src/ui/server/routes/git/tags.js +10 -10
  38. package/src/ui/server/routes/git.js +15 -26
  39. package/src/ui/server/routes/gitOps.js +91 -125
  40. package/src/ui/server/routes/status.js +3 -3
  41. package/src/utils/index.js +32 -29
@@ -40,31 +40,31 @@ export function registerGitOpsRoutes({
40
40
  const { message, hasNewlines, noVerify } = req.body;
41
41
 
42
42
  // 构建 git commit 命令
43
- let commitCommand = 'git commit';
43
+ const commitArgs = ['commit'];
44
44
 
45
45
  // 如果消息包含换行符,使用文件方式提交
46
+ let tempFile
46
47
  if (hasNewlines) {
47
48
  // 创建临时文件存储提交信息
48
- const tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
49
+ tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
49
50
  await fs.writeFile(tempFile, message);
50
- commitCommand += ` -F "${tempFile}"`;
51
+ commitArgs.push('-F', tempFile);
51
52
  } else {
52
53
  // 否则直接在命令行中提供消息
53
- commitCommand += ` -m "${message}"`;
54
+ commitArgs.push('-m', message);
54
55
  }
55
56
 
56
57
  // 添加 --no-verify 参数
57
58
  if (noVerify) {
58
- commitCommand += ' --no-verify';
59
+ commitArgs.push('--no-verify');
59
60
  }
60
61
 
61
- console.log(`commitCommand ==>`, commitCommand);
62
+ console.log(`commitArgs ==>`, commitArgs);
62
63
  // 执行提交命令
63
- await execGitCommand(commitCommand);
64
+ await execGitCommand(commitArgs);
64
65
 
65
66
  // 如果使用了临时文件,删除它
66
- if (hasNewlines) {
67
- const tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
67
+ if (hasNewlines && tempFile) {
68
68
  await fs.unlink(tempFile).catch(() => {});
69
69
  }
70
70
 
@@ -78,7 +78,7 @@ export function registerGitOpsRoutes({
78
78
  app.post('/api/add', async (req, res) => {
79
79
  try {
80
80
  // 直接执行 git add . - 前端已经做了锁定文件过滤判断
81
- await execGitCommand('git add .');
81
+ await execGitCommand(['add', '.']);
82
82
  res.json({ success: true });
83
83
  } catch (error) {
84
84
  res.status(500).json({ success: false, error: error.message });
@@ -100,7 +100,7 @@ export function registerGitOpsRoutes({
100
100
  app.post('/api/add-all', async (req, res) => {
101
101
  try {
102
102
  // 直接执行 git add . 不考虑锁定文件
103
- await execGitCommand('git add .');
103
+ await execGitCommand(['add', '.']);
104
104
  res.json({ success: true });
105
105
  } catch (error) {
106
106
  res.status(500).json({ success: false, error: error.message });
@@ -120,7 +120,7 @@ export function registerGitOpsRoutes({
120
120
  }
121
121
 
122
122
  // 执行 git add 命令添加特定文件
123
- await execGitCommand(`git add "${filePath}"`);
123
+ await execGitCommand(['add', '--', filePath]);
124
124
  res.json({ success: true });
125
125
  } catch (error) {
126
126
  res.status(500).json({ success: false, error: error.message });
@@ -152,9 +152,8 @@ export function registerGitOpsRoutes({
152
152
  });
153
153
  }
154
154
 
155
- // 用一个 git add 命令暂存所有文件(按路径转义双引号防注入)
156
- const escaped = uniquePaths.map(p => `"${String(p).replace(/"/g, '\\"')}"`).join(' ');
157
- await execGitCommand(`git add ${escaped}`);
155
+ // 一次 git add 命令暂存所有文件(不再需要 shell 转义,execFile argv 数组天然支持任意路径)
156
+ await execGitCommand(['add', ...uniquePaths]);
158
157
 
159
158
  res.json({
160
159
  success: true,
@@ -180,7 +179,7 @@ export function registerGitOpsRoutes({
180
179
  }
181
180
 
182
181
  // 执行 git reset HEAD 命令移除特定文件的暂存
183
- await execGitCommand(`git reset HEAD -- "${filePath}"`);
182
+ await execGitCommand(['reset', 'HEAD', '--', filePath]);
184
183
  res.json({ success: true });
185
184
  } catch (error) {
186
185
  res.status(500).json({ success: false, error: error.message });
@@ -190,7 +189,7 @@ export function registerGitOpsRoutes({
190
189
  // 推送更改
191
190
  app.post('/api/push', async (req, res) => {
192
191
  try {
193
- const { stdout } = await execGitCommand('git push');
192
+ const { stdout } = await execGitCommand(['push']);
194
193
 
195
194
  // 推送成功后,设置推送状态标记
196
195
  setRecentPushStatus({
@@ -395,7 +394,7 @@ export function registerGitOpsRoutes({
395
394
  // 添加git pull API端点
396
395
  app.post('/api/pull', async (req, res) => {
397
396
  try {
398
- const { stdout } = await execGitCommand('git pull');
397
+ const { stdout } = await execGitCommand(['pull']);
399
398
  res.json({ success: true, message: stdout });
400
399
  } catch (error) {
401
400
  // 改进错误处理,检查是否需要合并
@@ -426,7 +425,7 @@ export function registerGitOpsRoutes({
426
425
  // 添加git fetch --all API端点
427
426
  app.post('/api/fetch-all', async (req, res) => {
428
427
  try {
429
- const { stdout } = await execGitCommand('git fetch --all');
428
+ const { stdout } = await execGitCommand(['fetch', '--all']);
430
429
  res.json({ success: true, message: stdout });
431
430
  } catch (error) {
432
431
  res.status(500).json({ success: false, error: error.message });
@@ -449,78 +448,54 @@ export function registerGitOpsRoutes({
449
448
  const branch = req.query.branch ? req.query.branch.split(',') : [];
450
449
  const withParents = req.query.with_parents === 'true';
451
450
 
452
- // 构建Git命令选项
453
- let commandOptions = [];
454
-
455
- // 修改分支筛选处理 - 使用正确的引用格式
451
+ // 如果指定了分支,直接走 executeGitLogCommand 处理 refs/ 前缀
456
452
  if (branch.length > 0) {
457
- // 不再简单拼接分支名,而是将它们作为引用路径处理
458
- // 如果指定了分支,不再使用--all参数,而是直接用分支名
459
- commandOptions = commandOptions.filter(opt => opt !== '--all');
460
-
461
- // 将分支名格式化为Git可理解的引用格式
462
453
  const branchRefs = branch.map(b => b.trim()).join(' ');
463
-
464
- // 直接将分支名作为命令参数,并确保后面添加 -- 分隔符防止歧义
465
454
  return executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, req.query.all === 'true', withParents);
466
455
  }
467
456
 
468
- // 如果没有指定分支,则使用--all参数
469
- // 作者筛选(支持多作者,使用正则表达式OR操作)
457
+ // 构建 git log 的 argv 数组
458
+ const logArgs = ['log', '--all'];
459
+
460
+ // 作者筛选(支持多作者用正则 OR)
470
461
  if (author.length > 0) {
471
- // 过滤掉空作者
472
462
  const validAuthors = author.filter(a => a.trim() !== '');
473
-
474
463
  if (validAuthors.length === 1) {
475
- // 单个作者,直接使用--author
476
- commandOptions.push(`--author="${validAuthors[0].trim()}"`);
464
+ logArgs.push(`--author=${validAuthors[0].trim()}`);
477
465
  } else if (validAuthors.length > 1) {
478
- // 多个作者,使用正则表达式OR条件
479
- // 只转义OR运算符,保持其他内容不变
480
- const authorPattern = validAuthors
481
- .map(a => a.trim())
482
- .join('\\|'); // 在JavaScript字符串中\\|会变成\|,在shell中会成为|
483
-
484
- commandOptions.push(`--author="${authorPattern}"`);
466
+ const authorPattern = validAuthors.map(a => a.trim()).join('\\|');
467
+ logArgs.push(`--author=${authorPattern}`);
485
468
  }
486
469
  }
487
470
 
488
471
  // 日期范围筛选
489
472
  if (dateFrom && dateTo) {
490
- commandOptions.push(`--after="${dateFrom}" --before="${dateTo} 23:59:59"`);
473
+ logArgs.push(`--after=${dateFrom}`, `--before=${dateTo} 23:59:59`);
491
474
  } else if (dateFrom) {
492
- commandOptions.push(`--after="${dateFrom}"`);
475
+ logArgs.push(`--after=${dateFrom}`);
493
476
  } else if (dateTo) {
494
- commandOptions.push(`--before="${dateTo} 23:59:59"`);
477
+ logArgs.push(`--before=${dateTo} 23:59:59`);
495
478
  }
496
479
 
497
480
  // 提交信息筛选
498
481
  if (message) {
499
- commandOptions.push(`--grep="${message}"`);
482
+ logArgs.push(`--grep=${message}`);
500
483
  }
501
484
 
502
- // 如果all=true,则不使用限制,否则按页码和limit精确获取
503
- // 修复:只获取当前页的数据,而不是累计所有之前页的数据
504
- const limitOption = req.query.all === 'true' ? '' : `-n ${limit} --skip=${skip}`;
505
-
506
- // 合并所有命令选项
507
- const options = [...commandOptions, limitOption].filter(Boolean).join(' ');
485
+ // 分页
486
+ if (req.query.all !== 'true') {
487
+ logArgs.push('-n', String(limit), `--skip=${skip}`);
488
+ }
508
489
 
509
- // 添加父提交信息的格式
490
+ // format 字符串直接作为 argv 单元素(不再需要外层 shell 双引号)
510
491
  let formatString = '%H%x1E%an%x1E%ae%x1E%ad%x1E%B%x1E%D';
511
492
  if (withParents) {
512
493
  formatString = '%H%x1E%an%x1E%ae%x1E%ad%x1E%B%x1E%D%x1E%P';
513
494
  }
495
+ logArgs.push(`--pretty=format:${formatString}`);
496
+ logArgs.push('--date=format-local:%Y-%m-%d %H:%M');
514
497
 
515
- // console.log(`执行Git命令: git log --all --pretty=format:"${formatString}" --date=format-local:"%Y-%m-%d %H:%M" ${options}`);
516
-
517
- // 使用 git log 命令获取提交历史
518
- let { stdout: logOutput } = await execGitCommand(
519
- `git log --all --pretty=format:"${formatString}" --date=format-local:"%Y-%m-%d %H:%M" ${options}`
520
- );
521
-
522
- // 分页加载优化:不需要获取总数,通过实际返回的数据量判断是否还有更多
523
- // 这里直接处理已获取的数据,通过返回数据量判断是否还有更多
498
+ let { stdout: logOutput } = await execGitCommand(logArgs);
524
499
  processAndSendLogOutput(res, logOutput, page, limit, withParents);
525
500
  } catch (error) {
526
501
  console.error('获取Git日志失败:', error);
@@ -531,69 +506,60 @@ export function registerGitOpsRoutes({
531
506
  // 抽取执行Git日志命令的函数
532
507
  async function executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, isAll, withParents = false) {
533
508
  try {
534
- // 构建命令选项
535
- const commandOptions = [];
509
+ // 构建 git log argv 数组
510
+ const logArgs = ['log'];
511
+
512
+ // 准备分支引用,确保它们被正确识别为分支而不是文件名
513
+ // 使用 refs/heads/ 前缀明确指示这是分支
514
+ const formattedBranchRefs = branchRefs.split(' ')
515
+ .map(branch => {
516
+ if (branch.startsWith('refs/') || branch.includes('/')) {
517
+ return branch;
518
+ }
519
+ return `refs/heads/${branch}`;
520
+ });
521
+ logArgs.push(...formattedBranchRefs);
536
522
 
537
523
  // 作者筛选
538
524
  if (author.length > 0) {
539
525
  const validAuthors = author.filter(a => a.trim() !== '');
540
-
541
526
  if (validAuthors.length === 1) {
542
- commandOptions.push(`--author="${validAuthors[0].trim()}"`);
527
+ logArgs.push(`--author=${validAuthors[0].trim()}`);
543
528
  } else if (validAuthors.length > 1) {
544
529
  const authorPattern = validAuthors.map(a => a.trim()).join('\\|');
545
- commandOptions.push(`--author="${authorPattern}"`);
530
+ logArgs.push(`--author=${authorPattern}`);
546
531
  }
547
532
  }
548
533
 
549
534
  // 日期范围筛选
550
535
  if (dateFrom && dateTo) {
551
- commandOptions.push(`--after="${dateFrom}" --before="${dateTo} 23:59:59"`);
536
+ logArgs.push(`--after=${dateFrom}`, `--before=${dateTo} 23:59:59`);
552
537
  } else if (dateFrom) {
553
- commandOptions.push(`--after="${dateFrom}"`);
538
+ logArgs.push(`--after=${dateFrom}`);
554
539
  } else if (dateTo) {
555
- commandOptions.push(`--before="${dateTo} 23:59:59"`);
540
+ logArgs.push(`--before=${dateTo} 23:59:59`);
556
541
  }
557
542
 
558
543
  // 提交信息筛选
559
544
  if (message) {
560
- commandOptions.push(`--grep="${message}"`);
545
+ logArgs.push(`--grep=${message}`);
561
546
  }
562
547
 
563
548
  // 限制选项
564
- const limitOption = isAll ? '' : `-n ${limit} --skip=${skip}`;
565
-
566
- // 合并所有选项
567
- const options = [...commandOptions, limitOption].filter(Boolean).join(' ');
568
-
569
- // 准备分支引用,确保它们被正确识别为分支而不是文件名
570
- // 使用 refs/heads/ 前缀明确指示这是分支
571
- const formattedBranchRefs = branchRefs.split(' ')
572
- .map(branch => {
573
- // 检查是否已经是完整引用
574
- if (branch.startsWith('refs/') || branch.includes('/')) {
575
- return branch;
576
- }
577
- // 添加refs/heads/前缀
578
- return `refs/heads/${branch}`;
579
- })
580
- .join(' ');
549
+ if (!isAll) {
550
+ logArgs.push('-n', String(limit), `--skip=${skip}`);
551
+ }
581
552
 
582
- // 添加父提交信息的格式
583
- // 确认格式字符串使用 %x1E 作为分隔符
584
553
  let formatString = '%H%x1E%an%x1E%ae%x1E%ad%x1E%B%x1E%D';
585
554
  if (withParents) {
586
555
  formatString = '%H%x1E%an%x1E%ae%x1E%ad%x1E%B%x1E%D%x1E%P';
587
556
  }
557
+ logArgs.push(`--pretty=format:${formatString}`);
558
+ logArgs.push('--date=format-local:%Y-%m-%d %H:%M');
588
559
 
589
- // 构建执行的命令
590
- const command = `git log ${formattedBranchRefs} --pretty=format:"${formatString}" --date=format-local:"%Y-%m-%d %H:%M" ${options}`;
591
- console.log(`执行Git命令(带分支引用): ${command}`);
592
-
593
- // 执行命令
594
- const { stdout: logOutput } = await execGitCommand(command);
560
+ console.log(`执行Git log 命令 argv:`, logArgs);
595
561
 
596
- // 分页加载优化:不需要获取总数,通过实际返回的数据量判断是否还有更多
562
+ const { stdout: logOutput } = await execGitCommand(logArgs);
597
563
  processAndSendLogOutput(res, logOutput, skip / limit + 1, limit, withParents);
598
564
  } catch (error) {
599
565
  console.error('执行Git日志命令失败:', error);
@@ -675,7 +641,7 @@ export function registerGitOpsRoutes({
675
641
  app.post('/api/reset-head', async (req, res) => {
676
642
  try {
677
643
  // 执行 git reset HEAD 命令
678
- await execGitCommand('git reset HEAD');
644
+ await execGitCommand(['reset', 'HEAD']);
679
645
  res.json({ success: true });
680
646
  } catch (error) {
681
647
  console.error('重置暂存区失败:', error);
@@ -702,7 +668,7 @@ export function registerGitOpsRoutes({
702
668
  await checkAndClearGitLock();
703
669
 
704
670
  // 执行 git reset --hard origin/branch 命令
705
- await execGitCommand(`git reset --hard origin/${branch}`);
671
+ await execGitCommand(['reset', '--hard', `origin/${branch}`]);
706
672
  res.json({ success: true });
707
673
  } catch (error) {
708
674
  console.error('重置到远程分支失败:', error);
@@ -720,10 +686,10 @@ export function registerGitOpsRoutes({
720
686
  await checkAndClearGitLock();
721
687
 
722
688
  // 1. 执行 git reset --hard 丢弃已跟踪文件的更改
723
- await execGitCommand('git reset --hard');
724
-
689
+ await execGitCommand(['reset', '--hard']);
690
+
725
691
  // 2. 执行 git clean -fd 移除未跟踪的文件和目录
726
- await execGitCommand('git clean -fd');
692
+ await execGitCommand(['clean', '-fd']);
727
693
 
728
694
  res.json({ success: true });
729
695
  } catch (error) {
@@ -750,7 +716,7 @@ export function registerGitOpsRoutes({
750
716
  console.log(`获取提交文件列表: hash=${hash}`);
751
717
 
752
718
  // 执行命令获取提交中修改的文件列表
753
- const { stdout } = await execGitCommand(`git show --name-only --format="" ${hash}`);
719
+ const { stdout } = await execGitCommand(['show', '--name-only', '--format=', hash]);
754
720
 
755
721
  // 将输出按行分割,并过滤掉空行
756
722
  const files = stdout.split('\n').filter(line => line.trim());
@@ -784,10 +750,12 @@ export function registerGitOpsRoutes({
784
750
 
785
751
  console.log(`获取提交文件差异: hash=${hash}, file=${filePath}`);
786
752
 
787
- const diffCommand = `git show ${hash} -- "${filePath}"`;
753
+ const diffCommandArgs = ['show', hash, '--', filePath];
788
754
 
789
755
  // 使用优化的检查函数
790
- const skipCheck = await checkShouldSkipDiff(filePath, diffCommand);
756
+ // checkShouldSkipDiff 接受字符串命令,这里只用于大小判断,先按字面占位;
757
+ // 走 execGitCommand 时仍用 argv 数组保证转义正确。
758
+ const skipCheck = await checkShouldSkipDiff(filePath, `git show ${hash} -- "${filePath}"`);
791
759
  if (skipCheck.shouldSkip) {
792
760
  return res.json({
793
761
  success: true,
@@ -798,7 +766,7 @@ export function registerGitOpsRoutes({
798
766
  }
799
767
 
800
768
  // 执行命令获取文件差异
801
- const { stdout } = await execGitCommand(diffCommand);
769
+ const { stdout } = await execGitCommand(diffCommandArgs);
802
770
 
803
771
  console.log(`获取到差异内容,长度: ${stdout.length}`);
804
772
 
@@ -854,11 +822,10 @@ export function registerGitOpsRoutes({
854
822
  }
855
823
 
856
824
  const spec = `${targetHash}:${filePath}`;
857
- const sizeCmd = `git cat-file -s "${spec}"`;
858
825
 
859
826
  let sizeBytes = 0;
860
827
  try {
861
- const { stdout: sizeOut } = await execGitCommand(sizeCmd, { log: false });
828
+ const { stdout: sizeOut } = await execGitCommand(['cat-file', '-s', spec], { log: false });
862
829
  sizeBytes = parseInt(String(sizeOut).trim(), 10) || 0;
863
830
  } catch (e) {
864
831
  return res.json({
@@ -880,9 +847,8 @@ export function registerGitOpsRoutes({
880
847
  });
881
848
  }
882
849
 
883
- const showCmd = `git show "${spec}"`;
884
850
  try {
885
- const { stdout } = await execGitCommand(showCmd, { log: false });
851
+ const { stdout } = await execGitCommand(['show', spec], { log: false });
886
852
  res.json({
887
853
  success: true,
888
854
  content: stdout ?? ''
@@ -916,7 +882,7 @@ export function registerGitOpsRoutes({
916
882
  console.log(`执行撤销提交操作: hash=${hash}`);
917
883
 
918
884
  // 执行git revert命令
919
- await execGitCommand(`git revert --no-edit ${hash}`);
885
+ await execGitCommand(['revert', '--no-edit', hash]);
920
886
 
921
887
  res.json({
922
888
  success: true,
@@ -946,7 +912,7 @@ export function registerGitOpsRoutes({
946
912
  console.log(`执行Cherry-pick操作: hash=${hash}`);
947
913
 
948
914
  // 执行git cherry-pick命令
949
- await execGitCommand(`git cherry-pick ${hash}`);
915
+ await execGitCommand(['cherry-pick', hash]);
950
916
 
951
917
  res.json({
952
918
  success: true,
@@ -976,7 +942,7 @@ export function registerGitOpsRoutes({
976
942
  console.log(`执行重置到指定提交操作: hash=${hash}`);
977
943
 
978
944
  // 执行git reset --hard命令
979
- await execGitCommand(`git reset --hard ${hash}`);
945
+ await execGitCommand(['reset', '--hard', hash]);
980
946
 
981
947
  res.json({
982
948
  success: true,
@@ -998,7 +964,7 @@ export function registerGitOpsRoutes({
998
964
  if (!hash) {
999
965
  return res.status(400).json({ success: false, error: '缺少提交哈希参数' });
1000
966
  }
1001
- const { stdout } = await execGitCommand(`git show ${hash}`);
967
+ const { stdout } = await execGitCommand(['show', hash]);
1002
968
  // 限制最大 200KB 防止内容过大
1003
969
  const MAX = 200 * 1024;
1004
970
  const content = stdout.length > MAX ? stdout.slice(0, MAX) + '\n\n[内容过大,已截断]' : stdout;
@@ -1042,9 +1008,9 @@ export function registerGitOpsRoutes({
1042
1008
  try {
1043
1009
  // 检查全局配置是否存在,如果存在才删除
1044
1010
  try {
1045
- const { stdout: userName } = await execGitCommand('git config --global user.name');
1011
+ const { stdout: userName } = await execGitCommand(['config', '--global', 'user.name']);
1046
1012
  if (userName.trim()) {
1047
- await execGitCommand('git config --global --unset user.name');
1013
+ await execGitCommand(['config', '--global', '--unset', 'user.name']);
1048
1014
  }
1049
1015
  } catch (error) {
1050
1016
  console.log('全局用户名配置检查失败,可能不存在:', error.message);
@@ -1052,9 +1018,9 @@ export function registerGitOpsRoutes({
1052
1018
  }
1053
1019
 
1054
1020
  try {
1055
- const { stdout: userEmail } = await execGitCommand('git config --global user.email');
1021
+ const { stdout: userEmail } = await execGitCommand(['config', '--global', 'user.email']);
1056
1022
  if (userEmail.trim()) {
1057
- await execGitCommand('git config --global --unset user.email');
1023
+ await execGitCommand(['config', '--global', '--unset', 'user.email']);
1058
1024
  }
1059
1025
  } catch (error) {
1060
1026
  console.log('全局邮箱配置检查失败,可能不存在:', error.message);
@@ -1081,8 +1047,8 @@ export function registerGitOpsRoutes({
1081
1047
  });
1082
1048
  }
1083
1049
 
1084
- await execGitCommand(`git config --global user.name "${name}"`);
1085
- await execGitCommand(`git config --global user.email "${email}"`);
1050
+ await execGitCommand(['config', '--global', 'user.name', name]);
1051
+ await execGitCommand(['config', '--global', 'user.email', email]);
1086
1052
  res.json({ success: true, message: '已更新全局Git用户配置' });
1087
1053
  } catch (error) {
1088
1054
  res.status(500).json({
@@ -1095,7 +1061,7 @@ export function registerGitOpsRoutes({
1095
1061
  // 初始化Git仓库
1096
1062
  app.post('/api/git-init', async (req, res) => {
1097
1063
  try {
1098
- const { stdout } = await execGitCommand('git init');
1064
+ const { stdout } = await execGitCommand(['init']);
1099
1065
  res.json({ success: true, output: stdout.trim() });
1100
1066
  } catch (error) {
1101
1067
  console.error('git init 失败:', error);
@@ -1113,7 +1079,7 @@ export function registerGitOpsRoutes({
1113
1079
  if (!url || !url.trim()) {
1114
1080
  return res.json({ success: false, error: '远程仓库地址不能为空' });
1115
1081
  }
1116
- await execGitCommand(`git remote add ${name} ${url.trim()}`);
1082
+ await execGitCommand(['remote', 'add', name, url.trim()]);
1117
1083
  res.json({ success: true });
1118
1084
  } catch (error) {
1119
1085
  console.error('添加远程仓库失败:', error);
@@ -1130,7 +1096,7 @@ export function registerGitOpsRoutes({
1130
1096
  }
1131
1097
 
1132
1098
  // 执行git命令获取远程仓库URL
1133
- const { stdout } = await execGitCommand('git config --get remote.origin.url');
1099
+ const { stdout } = await execGitCommand(['config', '--get', 'remote.origin.url']);
1134
1100
 
1135
1101
  // 返回远程仓库URL
1136
1102
  res.json({
@@ -1150,7 +1116,7 @@ export function registerGitOpsRoutes({
1150
1116
  app.get('/api/authors', async (req, res) => {
1151
1117
  try {
1152
1118
  // 使用git命令获取所有提交者,不依赖Unix命令
1153
- const { stdout } = await execGitCommand('git log --format="%an"');
1119
+ const { stdout } = await execGitCommand(['log', '--format=%an']);
1154
1120
 
1155
1121
  // 将结果按行分割并过滤空行
1156
1122
  const lines = stdout.split('\n').filter(author => author.trim() !== '');
@@ -32,12 +32,12 @@ export function registerStatusRoutes({
32
32
 
33
33
  app.get('/api/status_porcelain', async (req, res) => {
34
34
  try {
35
- const { stdout } = await execGitCommand('git status --porcelain --untracked-files=all');
35
+ const { stdout } = await execGitCommand(['status', '--porcelain', '--untracked-files=all']);
36
36
  // 检测是否处于 MERGING 状态(MERGE_HEAD 存在)
37
37
  let isMergeInProgress = false;
38
38
  let mergeMessage = '';
39
39
  try {
40
- const { stdout: mergeHead } = await execGitCommand('git rev-parse -q --verify MERGE_HEAD');
40
+ const { stdout: mergeHead } = await execGitCommand(['rev-parse', '-q', '--verify', 'MERGE_HEAD']);
41
41
  isMergeInProgress = mergeHead.trim().length > 0;
42
42
  } catch (_) {
43
43
  // MERGE_HEAD 不存在时命令会报错,属正常情况
@@ -45,7 +45,7 @@ export function registerStatusRoutes({
45
45
  }
46
46
  if (isMergeInProgress) {
47
47
  try {
48
- const { stdout: gitDir } = await execGitCommand('git rev-parse --git-dir');
48
+ const { stdout: gitDir } = await execGitCommand(['rev-parse', '--git-dir']);
49
49
  const mergeMsgPath = path.resolve(gitDir.trim(), 'MERGE_MSG');
50
50
  const raw = await fs.readFile(mergeMsgPath, 'utf-8');
51
51
  // 过滤掉以 # 开头的注释行,拼接所有非空非注释行(保留多段信息如合并分支列表)