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.
- package/package.json +1 -1
- package/scripts/README_COLOR_CONVERTER.md +196 -196
- package/scripts/README_FONTSIZE_CONVERTER.md +278 -278
- package/scripts/README_SPACING_CONVERTER.md +126 -126
- package/scripts/README_STYLE_VARS.md +180 -180
- package/src/gitCommit.js +2 -2
- package/src/ui/public/assets/{AppVersionBadge-BUS8w2f8.js → AppVersionBadge-CaVphp8E.js} +1 -1
- package/src/ui/public/assets/{BranchSelector-Dm7nENer.js → BranchSelector-B91Ylbas.js} +1 -1
- package/src/ui/public/assets/{CommandConsole-C1W9ZQkE.js → CommandConsole-bK4iwZvI.js} +2 -2
- package/src/ui/public/assets/{CommitForm-C76W1ZZH.js → CommitForm-zOCxmZ84.js} +1 -1
- package/src/ui/public/assets/{CustomCommandManager-JKx8JYqn.js → CustomCommandManager-C1lRrp5z.js} +1 -1
- package/src/ui/public/assets/{EditorView-CriUYDvi.js → EditorView-DYo2oYtk.js} +0 -0
- package/src/ui/public/assets/{FileDiffViewer-DNWsiIGt.js → FileDiffViewer-Ce17WOZ-.js} +1 -1
- package/src/ui/public/assets/{FlowExecutionViewer-CTDh2bCD.js → FlowExecutionViewer-CRK6lIul.js} +1 -1
- package/src/ui/public/assets/{FlowOrchestrationWorkspace-Chah2aGN.js → FlowOrchestrationWorkspace-BDw7ThWp.js} +1 -1
- package/src/ui/public/assets/{GitStatus-DP-DQsiS.js → GitStatus-DN4_m6sO.js} +1 -1
- package/src/ui/public/assets/{LogList-Z76_5USQ.js → LogList-BshRTCrE.js} +1 -1
- package/src/ui/public/assets/{ProjectStartupButton-C5oF4unq.js → ProjectStartupButton-C-dXxxMO.js} +1 -1
- package/src/ui/public/assets/{RemoteRepoCard-B-fGjb8Y.js → RemoteRepoCard-DDZcP5-V.js} +1 -1
- package/src/ui/public/assets/{ResetToRemoteButton-BixSoEVs.js → ResetToRemoteButton-TXJzvBxE.js} +1 -1
- package/src/ui/public/assets/{SourceMapView-C9HZiJ9N.js → SourceMapView-BUkB6h41.js} +1 -1
- package/src/ui/public/assets/{TemplateManager-C7SuKq0f.js → TemplateManager-DFVzDiu0.js} +1 -1
- package/src/ui/public/assets/{UserInputNode-CYy0UpzR.js → UserInputNode-CXyV1xG4.js} +1 -1
- package/src/ui/public/assets/{WorkbenchView-A9Oik707.js → WorkbenchView-CEYhsa4O.js} +1 -1
- package/src/ui/public/assets/{index-DPSGvy34.js → index-BGmxuKoN.js} +2 -2
- package/src/ui/public/favicon.svg +75 -75
- package/src/ui/public/index.html +18 -18
- package/src/ui/public/logo.svg +74 -74
- package/src/ui/server/index.js +3 -3
- package/src/ui/server/routes/branchStatus.js +4 -4
- package/src/ui/server/routes/config.js +8 -8
- package/src/ui/server/routes/exec.js +7 -1
- package/src/ui/server/routes/fs.js +2 -2
- package/src/ui/server/routes/git/diff.js +17 -16
- package/src/ui/server/routes/git/diffUtils.js +21 -2
- package/src/ui/server/routes/git/stash.js +38 -42
- package/src/ui/server/routes/git/tags.js +10 -10
- package/src/ui/server/routes/git.js +15 -26
- package/src/ui/server/routes/gitOps.js +91 -125
- package/src/ui/server/routes/status.js +3 -3
- 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
|
-
|
|
43
|
+
const commitArgs = ['commit'];
|
|
44
44
|
|
|
45
45
|
// 如果消息包含换行符,使用文件方式提交
|
|
46
|
+
let tempFile
|
|
46
47
|
if (hasNewlines) {
|
|
47
48
|
// 创建临时文件存储提交信息
|
|
48
|
-
|
|
49
|
+
tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
|
|
49
50
|
await fs.writeFile(tempFile, message);
|
|
50
|
-
|
|
51
|
+
commitArgs.push('-F', tempFile);
|
|
51
52
|
} else {
|
|
52
53
|
// 否则直接在命令行中提供消息
|
|
53
|
-
|
|
54
|
+
commitArgs.push('-m', message);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
// 添加 --no-verify 参数
|
|
57
58
|
if (noVerify) {
|
|
58
|
-
|
|
59
|
+
commitArgs.push('--no-verify');
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
console.log(`
|
|
62
|
+
console.log(`commitArgs ==>`, commitArgs);
|
|
62
63
|
// 执行提交命令
|
|
63
|
-
await execGitCommand(
|
|
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('
|
|
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('
|
|
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(
|
|
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
|
-
//
|
|
156
|
-
|
|
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(
|
|
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('
|
|
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('
|
|
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('
|
|
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
|
-
//
|
|
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
|
-
//
|
|
469
|
-
|
|
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
|
-
|
|
476
|
-
commandOptions.push(`--author="${validAuthors[0].trim()}"`);
|
|
464
|
+
logArgs.push(`--author=${validAuthors[0].trim()}`);
|
|
477
465
|
} else if (validAuthors.length > 1) {
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
473
|
+
logArgs.push(`--after=${dateFrom}`, `--before=${dateTo} 23:59:59`);
|
|
491
474
|
} else if (dateFrom) {
|
|
492
|
-
|
|
475
|
+
logArgs.push(`--after=${dateFrom}`);
|
|
493
476
|
} else if (dateTo) {
|
|
494
|
-
|
|
477
|
+
logArgs.push(`--before=${dateTo} 23:59:59`);
|
|
495
478
|
}
|
|
496
479
|
|
|
497
480
|
// 提交信息筛选
|
|
498
481
|
if (message) {
|
|
499
|
-
|
|
482
|
+
logArgs.push(`--grep=${message}`);
|
|
500
483
|
}
|
|
501
484
|
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
530
|
+
logArgs.push(`--author=${authorPattern}`);
|
|
546
531
|
}
|
|
547
532
|
}
|
|
548
533
|
|
|
549
534
|
// 日期范围筛选
|
|
550
535
|
if (dateFrom && dateTo) {
|
|
551
|
-
|
|
536
|
+
logArgs.push(`--after=${dateFrom}`, `--before=${dateTo} 23:59:59`);
|
|
552
537
|
} else if (dateFrom) {
|
|
553
|
-
|
|
538
|
+
logArgs.push(`--after=${dateFrom}`);
|
|
554
539
|
} else if (dateTo) {
|
|
555
|
-
|
|
540
|
+
logArgs.push(`--before=${dateTo} 23:59:59`);
|
|
556
541
|
}
|
|
557
542
|
|
|
558
543
|
// 提交信息筛选
|
|
559
544
|
if (message) {
|
|
560
|
-
|
|
545
|
+
logArgs.push(`--grep=${message}`);
|
|
561
546
|
}
|
|
562
547
|
|
|
563
548
|
// 限制选项
|
|
564
|
-
|
|
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('
|
|
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(
|
|
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('
|
|
724
|
-
|
|
689
|
+
await execGitCommand(['reset', '--hard']);
|
|
690
|
+
|
|
725
691
|
// 2. 执行 git clean -fd 移除未跟踪的文件和目录
|
|
726
|
-
await execGitCommand('
|
|
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(
|
|
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
|
|
753
|
+
const diffCommandArgs = ['show', hash, '--', filePath];
|
|
788
754
|
|
|
789
755
|
// 使用优化的检查函数
|
|
790
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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('
|
|
1011
|
+
const { stdout: userName } = await execGitCommand(['config', '--global', 'user.name']);
|
|
1046
1012
|
if (userName.trim()) {
|
|
1047
|
-
await execGitCommand('
|
|
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('
|
|
1021
|
+
const { stdout: userEmail } = await execGitCommand(['config', '--global', 'user.email']);
|
|
1056
1022
|
if (userEmail.trim()) {
|
|
1057
|
-
await execGitCommand('
|
|
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(
|
|
1085
|
-
await execGitCommand(
|
|
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('
|
|
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(
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
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
|
// 过滤掉以 # 开头的注释行,拼接所有非空非注释行(保留多段信息如合并分支列表)
|