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
@@ -31,8 +31,27 @@ export function createDiffHelpers({ execGitCommand }) {
31
31
 
32
32
  // 2. 使用 --numstat 快速检查变更量(不获取实际内容,速度快)
33
33
  try {
34
- const numstatCommand = diffCommand.replace(/git (diff|show)/, 'git $1 --numstat');
35
- const { stdout: numstat } = await execGitCommand(numstatCommand, { log: false });
34
+ // 把传入的 diffCommand 字符串改写成 git diff/show --numstat 的 argv 数组。
35
+ // 不再走 shell 模式,避免 Windows cmd.exe 下引号兼容问题( utils execGitCommand execFile )
36
+ const numstatCommand = diffCommand.replace(/git (diff|show)/, 'git $1 --numstat')
37
+ const argvMatch = numstatCommand.trim().match(/^git\s+(\S+)\s*(.*)$/)
38
+ let numstatArgs
39
+ if (argvMatch) {
40
+ const subCmd = argvMatch[1]
41
+ const rest = argvMatch[2]
42
+ // 简单按空白拆分,带引号段整体保留 -- "--" + 路径
43
+ // 这里最常见形态是 'git diff/show -- "--path"' 或 'git diff/show hash -- "--path"'
44
+ const tokens = []
45
+ const re = /"([^"]*)"|(\S+)/g
46
+ let m
47
+ while ((m = re.exec(rest)) !== null) tokens.push(m[1] !== undefined ? m[1] : m[2])
48
+ // 找到 '--numstat' 位置,把它加到 subCmd 后面作为第一个参数
49
+ numstatArgs = [subCmd, '--numstat', ...tokens]
50
+ } else {
51
+ // fallback: 走旧逻辑(字符串)
52
+ numstatArgs = numstatCommand
53
+ }
54
+ const { stdout: numstat } = await execGitCommand(numstatArgs, { log: false });
36
55
 
37
56
  if (numstat.trim()) {
38
57
  const lines = numstat.trim().split('\n');
@@ -20,7 +20,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
20
20
  // 获取stash列表
21
21
  app.get('/api/stash-list', async (req, res) => {
22
22
  try {
23
- const { stdout } = await execGitCommand('git stash list');
23
+ const { stdout } = await execGitCommand(['stash', 'list']);
24
24
 
25
25
  // 解析stash列表
26
26
  const stashList = stdout.split('\n')
@@ -53,7 +53,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
53
53
  if (excludeLocked) {
54
54
  const lockedFiles = await configManager.getLockedFiles();
55
55
  // 包含未跟踪文件,确保状态与 UI 一致
56
- const { stdout: statusStdout } = await execGitCommand('git status --porcelain --untracked-files=all', { log: false });
56
+ const { stdout: statusStdout } = await execGitCommand(['status', '--porcelain', '--untracked-files=all'], { log: false });
57
57
  const changedFiles = statusStdout
58
58
  .split('\n')
59
59
  .filter(line => line.trim())
@@ -100,7 +100,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
100
100
  if (includeUntracked) {
101
101
  try {
102
102
  // 使用 git 列出该目录下的未跟踪和已修改文件
103
- const { stdout: listStdout } = await execGitCommand(`git ls-files -mo --exclude-standard "${filename}"`, { log: false });
103
+ const { stdout: listStdout } = await execGitCommand(['ls-files', '-mo', '--exclude-standard', '--', filename], { log: false });
104
104
  const listed = listStdout
105
105
  .split('\n')
106
106
  .map(l => l.trim())
@@ -145,14 +145,13 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
145
145
  // 在执行 stash 前进行候选校验:
146
146
  // 1) 仍有跟踪差异的文件
147
147
  try {
148
- const args = filesToStash.map(f => `"${f}"`).join(' ');
149
- const { stdout: diffNames } = await execGitCommand(`git diff --name-only -- ${args}`, { log: false });
148
+ const { stdout: diffNames } = await execGitCommand(['diff', '--name-only', '--', ...filesToStash], { log: false });
150
149
  const trackedChanged = new Set(diffNames.split('\n').map(s => s.trim()).filter(Boolean));
151
150
 
152
151
  // 2) 仍为未跟踪的文件(当 includeUntracked 才检查)
153
152
  let untrackedExisting = new Set();
154
153
  if (includeUntracked) {
155
- const { stdout: others } = await execGitCommand(`git ls-files --others --exclude-standard -- ${args}`, { log: false });
154
+ const { stdout: others } = await execGitCommand(['ls-files', '--others', '--exclude-standard', '--', ...filesToStash], { log: false });
156
155
  untrackedExisting = new Set(others.split('\n').map(s => s.trim()).filter(Boolean));
157
156
  }
158
157
 
@@ -174,27 +173,22 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
174
173
  return res.json({ success: false, message: '没有可储藏的更改(可能刚刚已储藏,或被锁定过滤)' });
175
174
  }
176
175
 
177
- let command = 'git stash push';
178
- if (message) command += ` -m "${message}"`;
179
- if (includeUntracked) command += ' --include-untracked';
180
- const args = filesToStash.map(f => `"${f}"`).join(' ');
181
- command += ` -- ${args}`;
176
+ const stashArgs = ['stash', 'push'];
177
+ if (message) stashArgs.push('-m', message);
178
+ if (includeUntracked) stashArgs.push('--include-untracked');
179
+ if (filesToStash.length > 0) stashArgs.push('--', ...filesToStash);
182
180
 
183
- const { stdout } = await execGitCommand(command);
181
+ const { stdout } = await execGitCommand(stashArgs);
184
182
  if (stdout.includes('No local changes to save')) {
185
183
  return res.json({ success: false, message: '没有本地更改需要保存' });
186
184
  }
187
185
  return res.json({ success: true, message: '成功保存未锁定的工作区更改', output: stdout });
188
186
  }
189
187
 
190
- let command = 'git stash push';
191
- if (message) {
192
- command += ` -m "${message}"`;
193
- }
194
- if (includeUntracked) {
195
- command += ' --include-untracked';
196
- }
197
- const { stdout } = await execGitCommand(command);
188
+ const stashArgs = ['stash', 'push'];
189
+ if (message) stashArgs.push('-m', message);
190
+ if (includeUntracked) stashArgs.push('--include-untracked');
191
+ const { stdout } = await execGitCommand(stashArgs);
198
192
  if (stdout.includes('No local changes to save')) {
199
193
  return res.json({ success: false, message: '没有本地更改需要保存' });
200
194
  }
@@ -220,19 +214,18 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
220
214
  }
221
215
 
222
216
  // 构建 git stash push 命令
223
- let command = 'git stash push';
217
+ const stashArgs = ['stash', 'push'];
224
218
  if (message) {
225
- command += ` -m "${message}"`;
219
+ stashArgs.push('-m', message);
226
220
  }
227
221
  if (includeUntracked) {
228
- command += ' --include-untracked';
222
+ stashArgs.push('--include-untracked');
229
223
  }
230
224
 
231
225
  // 添加文件列表
232
- const args = files.map(f => `"${f}"`).join(' ');
233
- command += ` -- ${args}`;
226
+ stashArgs.push('--', ...files);
234
227
 
235
- const { stdout } = await execGitCommand(command);
228
+ const { stdout } = await execGitCommand(stashArgs);
236
229
 
237
230
  if (stdout.includes('No local changes to save')) {
238
231
  return res.json({ success: false, message: '没有本地更改需要保存' });
@@ -266,7 +259,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
266
259
  }
267
260
 
268
261
  // 决定是使用apply(保留stash)还是pop(应用后删除stash)
269
- const command = pop ? `git stash pop ${stashId}` : `git stash apply ${stashId}`;
262
+ const command = pop ? ['stash', 'pop', stashId] : ['stash', 'apply', stashId];
270
263
 
271
264
  try {
272
265
  const { stdout } = await execGitCommand(command);
@@ -306,7 +299,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
306
299
  });
307
300
  }
308
301
 
309
- const { stdout } = await execGitCommand(`git stash drop ${stashId}`);
302
+ const { stdout } = await execGitCommand(['stash', 'drop', stashId]);
310
303
 
311
304
  res.json({
312
305
  success: true,
@@ -322,7 +315,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
322
315
  // 清空所有stash
323
316
  app.post('/api/stash-clear', async (req, res) => {
324
317
  try {
325
- const { stdout } = await execGitCommand('git stash clear');
318
+ const { stdout } = await execGitCommand(['stash', 'clear']);
326
319
 
327
320
  res.json({
328
321
  success: true,
@@ -350,7 +343,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
350
343
  console.log(`获取stash文件列表: stashId=${stashId}`);
351
344
 
352
345
  // 0) 解析出当前 stash 提交及其父提交哈希,避免在 Windows 上使用 ^ 语法
353
- const { stdout: parentsLine } = await execGitCommand(`git rev-list --parents -n 1 ${stashId}`, { log: false });
346
+ const { stdout: parentsLine } = await execGitCommand(['rev-list', '--parents', '-n', '1', stashId], { log: false });
354
347
  const hashes = parentsLine.trim().split(/\s+/).filter(Boolean);
355
348
  const stashCommit = hashes[0] || '';
356
349
  const parent1 = hashes[1] || '';
@@ -359,14 +352,14 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
359
352
  // 1) 跟踪文件的变更列表:父1 与 stash 提交的差异(若无父1则为空)
360
353
  let trackedFiles = [];
361
354
  if (parent1) {
362
- const { stdout: trackedOut } = await execGitCommand(`git diff --name-only ${parent1} ${stashCommit}`, { log: false });
355
+ const { stdout: trackedOut } = await execGitCommand(['diff', '--name-only', parent1, stashCommit], { log: false });
363
356
  trackedFiles = trackedOut.split('\n').map(s => s.trim()).filter(Boolean);
364
357
  }
365
358
 
366
359
  // 2) 未跟踪文件:来自第三父(若存在)
367
360
  let untrackedFiles = [];
368
361
  if (parent3) {
369
- const { stdout: untrackedOut } = await execGitCommand(`git ls-tree -r --name-only ${parent3}`, { log: false });
362
+ const { stdout: untrackedOut } = await execGitCommand(['ls-tree', '-r', '--name-only', parent3], { log: false });
370
363
  untrackedFiles = untrackedOut.split('\n').map(s => s.trim()).filter(Boolean);
371
364
  }
372
365
 
@@ -403,7 +396,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
403
396
  console.log(`获取stash文件差异: stashId=${stashId}, file=${file}`);
404
397
 
405
398
  // 先解析父提交哈希,避免使用 ^ 语法
406
- const { stdout: parentsLine } = await execGitCommand(`git rev-list --parents -n 1 ${stashId}`, { log: false });
399
+ const { stdout: parentsLine } = await execGitCommand(['rev-list', '--parents', '-n', '1', stashId], { log: false });
407
400
  const hashes = parentsLine.trim().split(/\s+/).filter(Boolean);
408
401
  const stashCommit = hashes[0] || '';
409
402
  const parent1 = hashes[1] || '';
@@ -413,7 +406,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
413
406
  let isFromThirdParent = false;
414
407
  if (parent3) {
415
408
  try {
416
- await execGitCommand(`git cat-file -e ${parent3}:"${file}"`, { log: false });
409
+ await execGitCommand(['cat-file', '-e', `${parent3}:${file}`], { log: false });
417
410
  isFromThirdParent = true;
418
411
  } catch (_) {
419
412
  isFromThirdParent = false;
@@ -422,7 +415,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
422
415
 
423
416
  if (isFromThirdParent) {
424
417
  // 未跟踪文件:读取第三父中的内容,构造新增文件的统一diff
425
- const { stdout: blob } = await execGitCommand(`git show ${parent3}:"${file}"`, { log: false });
418
+ const { stdout: blob } = await execGitCommand(['show', `${parent3}:${file}`], { log: false });
426
419
 
427
420
  // 检查文件大小
428
421
  const sizeCheck = checkDiffSize(blob, 500);
@@ -457,10 +450,13 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
457
450
  }
458
451
 
459
452
  // 否则,使用原有方式获取与父1的变更
460
- const diffCommand = `git show ${stashCommit} -- "${file}"`;
453
+ // checkShouldSkipDiff 接受字符串命令用于日志展示,这里只用于大小判断;
454
+ // 走 execGitCommand 时用 argv 数组,避免 Windows 下 cmd.exe 拼引号被破坏。
455
+ const diffCommandForCheck = `git show ${stashCommit} -- "${file}"`;
456
+ const diffCommandArgs = ['show', stashCommit, '--', file];
461
457
 
462
458
  // 使用优化的检查函数
463
- const skipCheck = await checkShouldSkipDiff(file, diffCommand);
459
+ const skipCheck = await checkShouldSkipDiff(file, diffCommandForCheck);
464
460
  if (skipCheck.shouldSkip) {
465
461
  return res.json({
466
462
  success: true,
@@ -470,7 +466,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
470
466
  });
471
467
  }
472
468
 
473
- const { stdout } = await execGitCommand(diffCommand);
469
+ const { stdout } = await execGitCommand(diffCommandArgs);
474
470
 
475
471
  console.log(`获取到差异内容,长度: ${stdout.length}`);
476
472
 
@@ -503,7 +499,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
503
499
  }
504
500
 
505
501
  // 解析父提交哈希
506
- const { stdout: parentsLine } = await execGitCommand(`git rev-list --parents -n 1 ${stashId}`, { log: false });
502
+ const { stdout: parentsLine } = await execGitCommand(['rev-list', '--parents', '-n', '1', stashId], { log: false });
507
503
  const hashes = parentsLine.trim().split(/\s+/).filter(Boolean);
508
504
  const stashCommit = hashes[0] || '';
509
505
  const parent1 = hashes[1] || '';
@@ -513,7 +509,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
513
509
  let isFromThirdParent = false;
514
510
  if (parent3) {
515
511
  try {
516
- await execGitCommand(`git cat-file -e ${parent3}:"${file}"`, { log: false });
512
+ await execGitCommand(['cat-file', '-e', `${parent3}:${file}`], { log: false });
517
513
  isFromThirdParent = true;
518
514
  } catch (_) {
519
515
  isFromThirdParent = false;
@@ -524,7 +520,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
524
520
  let original = '';
525
521
  if (!isFromThirdParent && parent1) {
526
522
  try {
527
- const { stdout: origOut } = await execGitCommand(`git show ${parent1}:"${file}"`, { log: false });
523
+ const { stdout: origOut } = await execGitCommand(['show', `${parent1}:${file}`], { log: false });
528
524
  original = origOut ?? '';
529
525
  } catch (_) {
530
526
  original = ''; // 文件在储藏前不存在
@@ -536,7 +532,7 @@ export function registerGitStashRoutes({ app, execGitCommand, configManager }) {
536
532
  const modRef = isFromThirdParent ? parent3 : stashCommit;
537
533
  if (modRef) {
538
534
  try {
539
- const { stdout: modOut } = await execGitCommand(`git show ${modRef}:"${file}"`, { log: false });
535
+ const { stdout: modOut } = await execGitCommand(['show', `${modRef}:${file}`], { log: false });
540
536
  modified = modOut ?? '';
541
537
  } catch (_) {
542
538
  modified = ''; // 文件已被删除
@@ -24,25 +24,25 @@ export function registerGitTagRoutes({ app, execGitCommand, clearCommandHistory
24
24
  return res.status(400).json({ success: false, error: '缺少标签名称' });
25
25
  }
26
26
 
27
- let command = 'git tag';
27
+ const tagArgs = ['tag'];
28
28
 
29
29
  if (type === 'annotated') {
30
30
  // 附注标签
31
31
  if (!message) {
32
32
  return res.status(400).json({ success: false, error: '附注标签需要提供说明信息' });
33
33
  }
34
- command += ` -a "${tagName}" -m "${message}"`;
34
+ tagArgs.push('-a', tagName, '-m', message);
35
35
  } else {
36
36
  // 轻量标签
37
- command += ` "${tagName}"`;
37
+ tagArgs.push(tagName);
38
38
  }
39
39
 
40
40
  // 如果指定了commit,添加到命令中
41
41
  if (commit && commit.trim()) {
42
- command += ` ${commit.trim()}`;
42
+ tagArgs.push(commit.trim());
43
43
  }
44
44
 
45
- const { stdout } = await execGitCommand(command);
45
+ const { stdout } = await execGitCommand(tagArgs);
46
46
 
47
47
  res.json({
48
48
  success: true,
@@ -60,7 +60,7 @@ export function registerGitTagRoutes({ app, execGitCommand, clearCommandHistory
60
60
  try {
61
61
  // 使用 git tag -n --format 获取详细信息
62
62
  const { stdout } = await execGitCommand(
63
- 'git tag -n --format="%(refname:short)|%(objectname:short)|%(creatordate:iso8601)|%(subject)"'
63
+ ['tag', '-n', '--format=%(refname:short)|%(objectname:short)|%(creatordate:iso8601)|%(subject)']
64
64
  );
65
65
 
66
66
  if (!stdout.trim()) {
@@ -82,7 +82,7 @@ export function registerGitTagRoutes({ app, execGitCommand, clearCommandHistory
82
82
  for (const tag of tags) {
83
83
  try {
84
84
  const { stdout: typeCheck } = await execGitCommand(
85
- `git cat-file -t ${tag.name}`,
85
+ ['cat-file', '-t', tag.name],
86
86
  { log: false }
87
87
  );
88
88
  if (typeCheck.trim() === 'tag') {
@@ -109,7 +109,7 @@ export function registerGitTagRoutes({ app, execGitCommand, clearCommandHistory
109
109
  return res.status(400).json({ success: false, error: '缺少标签名称' });
110
110
  }
111
111
 
112
- const { stdout } = await execGitCommand(`git push origin ${tagName}`);
112
+ const { stdout } = await execGitCommand(['push', 'origin', tagName]);
113
113
 
114
114
  res.json({
115
115
  success: true,
@@ -125,7 +125,7 @@ export function registerGitTagRoutes({ app, execGitCommand, clearCommandHistory
125
125
  // 推送所有标签到远程
126
126
  app.post('/api/push-all-tags', async (req, res) => {
127
127
  try {
128
- const { stdout } = await execGitCommand('git push origin --tags');
128
+ const { stdout } = await execGitCommand(['push', 'origin', '--tags']);
129
129
 
130
130
  res.json({
131
131
  success: true,
@@ -147,7 +147,7 @@ export function registerGitTagRoutes({ app, execGitCommand, clearCommandHistory
147
147
  return res.status(400).json({ success: false, error: '缺少标签名称' });
148
148
  }
149
149
 
150
- const { stdout } = await execGitCommand(`git tag -d ${tagName}`);
150
+ const { stdout } = await execGitCommand(['tag', '-d', tagName]);
151
151
 
152
152
  res.json({
153
153
  success: true,
@@ -23,10 +23,10 @@ export function registerGitRoutes({
23
23
  app.get('/api/branches', async (req, res) => {
24
24
  try {
25
25
  // 获取本地分支 - 使用简单的git branch命令
26
- const { stdout: localBranches } = await execGitCommand('git branch');
26
+ const { stdout: localBranches } = await execGitCommand(['branch']);
27
27
 
28
28
  // 获取远程分支
29
- const { stdout: remoteBranches } = await execGitCommand('git branch -r');
29
+ const { stdout: remoteBranches } = await execGitCommand(['branch', '-r']);
30
30
 
31
31
  // 处理本地分支 - 正确解析git branch的标准输出格式
32
32
  const localBranchList = localBranches.split('\n')
@@ -63,18 +63,18 @@ export function registerGitRoutes({
63
63
  }
64
64
 
65
65
  // 构建创建分支的命令
66
- let command = `git branch ${newBranchName}`;
66
+ let commandArgs = ['branch', newBranchName];
67
67
 
68
68
  // 如果指定了基础分支,则基于该分支创建
69
69
  if (baseBranch) {
70
- command = `git branch ${newBranchName} ${baseBranch}`;
70
+ commandArgs = ['branch', newBranchName, baseBranch];
71
71
  }
72
72
 
73
73
  // 执行创建分支命令
74
- await execGitCommand(command);
74
+ await execGitCommand(commandArgs);
75
75
 
76
76
  // 切换到新创建的分支
77
- await execGitCommand(`git checkout ${newBranchName}`);
77
+ await execGitCommand(['checkout', newBranchName]);
78
78
 
79
79
  // 清除分支缓存,因为分支已切换
80
80
  clearBranchCache();
@@ -95,7 +95,7 @@ export function registerGitRoutes({
95
95
  }
96
96
 
97
97
  // 执行分支切换
98
- await execGitCommand(`git checkout ${branch}`);
98
+ await execGitCommand(['checkout', branch]);
99
99
 
100
100
  // 清除分支缓存,因为分支已切换
101
101
  clearBranchCache();
@@ -117,28 +117,17 @@ export function registerGitRoutes({
117
117
  }
118
118
 
119
119
  // 构建Git合并命令 - 直接使用传入的分支名(可能包含origin/前缀)
120
- let command = `git merge ${branch}`;
120
+ const commandArgs = ['merge', branch];
121
121
 
122
122
  // 添加可选参数
123
- if (noCommit) {
124
- command += ' --no-commit';
125
- }
126
-
127
- if (noFf) {
128
- command += ' --no-ff';
129
- }
130
-
131
- if (squash) {
132
- command += ' --squash';
133
- }
134
-
135
- if (message) {
136
- command += ` -m "${message}"`;
137
- }
123
+ if (noCommit) commandArgs.push('--no-commit');
124
+ if (noFf) commandArgs.push('--no-ff');
125
+ if (squash) commandArgs.push('--squash');
126
+ if (message) commandArgs.push('-m', message);
138
127
 
139
128
  try {
140
129
  // 执行合并命令
141
- const { stdout } = await execGitCommand(command);
130
+ const { stdout } = await execGitCommand(commandArgs);
142
131
 
143
132
  res.json({
144
133
  success: true,
@@ -175,9 +164,9 @@ export function registerGitRoutes({
175
164
  app.get('/api/user-info', async (req, res) => {
176
165
  try {
177
166
  // 获取全局用户名
178
- const { stdout: userName } = await execGitCommand('git config --global user.name');
167
+ const { stdout: userName } = await execGitCommand(['config', '--global', 'user.name']);
179
168
  // 获取全局用户邮箱
180
- const { stdout: userEmail } = await execGitCommand('git config --global user.email');
169
+ const { stdout: userEmail } = await execGitCommand(['config', '--global', 'user.email']);
181
170
 
182
171
  res.json({
183
172
  name: userName.trim(),