aws-runtime-bridge 1.7.36 → 1.7.37
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/dist/routes/git.d.ts +16 -0
- package/dist/routes/git.d.ts.map +1 -1
- package/dist/routes/git.js +57 -5
- package/dist/routes/git.test.js +40 -1
- package/package.json +1 -1
package/dist/routes/git.d.ts
CHANGED
|
@@ -26,6 +26,13 @@ interface GitStashItem {
|
|
|
26
26
|
createdAt: string | null;
|
|
27
27
|
}
|
|
28
28
|
type GitDiffFileStatus = 'modified' | 'added' | 'deleted' | 'renamed';
|
|
29
|
+
interface GitDiffSummaryFile {
|
|
30
|
+
path: string;
|
|
31
|
+
status: GitDiffFileStatus;
|
|
32
|
+
additions: number;
|
|
33
|
+
deletions: number;
|
|
34
|
+
staged: boolean;
|
|
35
|
+
}
|
|
29
36
|
/**
|
|
30
37
|
* 判断 Git diff 汇总行是否应该展示在差异树中。
|
|
31
38
|
* 普通 modified 且文本增删均为 0 通常是 filemode/元数据噪声,避免显示为“无差异内容”。
|
|
@@ -61,6 +68,15 @@ export declare function parseLatestGitStash(output: string): LatestGitStash | nu
|
|
|
61
68
|
* 解析 git diff 的 name-status 与 numstat 输出,生成前端文件树需要的汇总数据。
|
|
62
69
|
*/
|
|
63
70
|
export declare function parseGitStatusStagedPaths(output: string): Set<string>;
|
|
71
|
+
/**
|
|
72
|
+
* 解析 git diff 的 name-status 与 numstat 输出,生成前端文件树需要的汇总数据。
|
|
73
|
+
*/
|
|
74
|
+
export declare function parseGitDiffSummary(nameStatusOutput: string, numstatOutput: string, stagedPaths?: Set<string>, forceStaged?: boolean): GitDiffSummaryFile[];
|
|
75
|
+
/**
|
|
76
|
+
* 合并工作区与暂存区差异汇总。
|
|
77
|
+
* 主流程:以路径去重,保留工作区展示顺序;同一路径若也存在暂存变更则标记为 staged 并累加统计。
|
|
78
|
+
*/
|
|
79
|
+
export declare function mergeGitDiffSummaryFiles(unstagedFiles: GitDiffSummaryFile[], stagedFiles: GitDiffSummaryFile[]): GitDiffSummaryFile[];
|
|
64
80
|
export declare function normalizeGitCommitMessage(message: unknown): string;
|
|
65
81
|
export declare function createScopedGitPathspecArgs(context: Pick<GitRepositoryContext, 'relativeWorkspacePath'>): string[];
|
|
66
82
|
export declare function createScopedFilePathspecArgs(filePath: string, context: Pick<GitRepositoryContext, 'relativeWorkspacePath'>): string[];
|
package/dist/routes/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/routes/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/routes/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,eAAO,MAAM,SAAS,4CAAW,CAAC;AAIlC;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAOnD;AAED,UAAU,oBAAoB;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAsB,gCAAgC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3F;AAED,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,KAAK,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAEtE,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB;AAUD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,OAAO,GACpB,OAAO,CAMT;AAED;;;GAGG;AACH,wBAAgB,mCAAmC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAK3E;AAED,wBAAgB,uCAAuC,IAAI,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKjE;AAED,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAmCvE;AAkBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYzE;AA+KD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAmBrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,MAAM,EACrB,WAAW,GAAE,GAAG,CAAC,MAAM,CAAa,EACpC,WAAW,UAAQ,GAClB,kBAAkB,EAAE,CAuCtB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,kBAAkB,EAAE,EACnC,WAAW,EAAE,kBAAkB,EAAE,GAChC,kBAAkB,EAAE,CAwBtB;AAMD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAElE;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,MAAM,EAAE,CAElH;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,MAAM,EAAE,CAErI"}
|
package/dist/routes/git.js
CHANGED
|
@@ -308,7 +308,7 @@ export function parseGitStatusStagedPaths(output) {
|
|
|
308
308
|
/**
|
|
309
309
|
* 解析 git diff 的 name-status 与 numstat 输出,生成前端文件树需要的汇总数据。
|
|
310
310
|
*/
|
|
311
|
-
function parseGitDiffSummary(nameStatusOutput, numstatOutput, stagedPaths = new Set()) {
|
|
311
|
+
export function parseGitDiffSummary(nameStatusOutput, numstatOutput, stagedPaths = new Set(), forceStaged = false) {
|
|
312
312
|
const statusMap = new Map();
|
|
313
313
|
const nameStatusLines = nameStatusOutput.split('\n').filter(line => line.trim());
|
|
314
314
|
for (const line of nameStatusLines) {
|
|
@@ -338,10 +338,35 @@ function parseGitDiffSummary(nameStatusOutput, numstatOutput, stagedPaths = new
|
|
|
338
338
|
if (!filePath || !shouldIncludeGitDiffSummaryFile(status, additions, deletions, isBinaryDiff)) {
|
|
339
339
|
continue;
|
|
340
340
|
}
|
|
341
|
-
files.push({ path: filePath, status, additions, deletions, staged: stagedPaths.has(filePath) });
|
|
341
|
+
files.push({ path: filePath, status, additions, deletions, staged: forceStaged || stagedPaths.has(filePath) });
|
|
342
342
|
}
|
|
343
343
|
return files;
|
|
344
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* 合并工作区与暂存区差异汇总。
|
|
347
|
+
* 主流程:以路径去重,保留工作区展示顺序;同一路径若也存在暂存变更则标记为 staged 并累加统计。
|
|
348
|
+
*/
|
|
349
|
+
export function mergeGitDiffSummaryFiles(unstagedFiles, stagedFiles) {
|
|
350
|
+
const fileMap = new Map();
|
|
351
|
+
for (const file of unstagedFiles) {
|
|
352
|
+
fileMap.set(file.path, { ...file });
|
|
353
|
+
}
|
|
354
|
+
for (const stagedFile of stagedFiles) {
|
|
355
|
+
const existingFile = fileMap.get(stagedFile.path);
|
|
356
|
+
if (!existingFile) {
|
|
357
|
+
fileMap.set(stagedFile.path, { ...stagedFile, staged: true });
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
fileMap.set(stagedFile.path, {
|
|
361
|
+
...existingFile,
|
|
362
|
+
status: existingFile.status === 'modified' ? stagedFile.status : existingFile.status,
|
|
363
|
+
additions: existingFile.additions + stagedFile.additions,
|
|
364
|
+
deletions: existingFile.deletions + stagedFile.deletions,
|
|
365
|
+
staged: true,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
return [...fileMap.values()];
|
|
369
|
+
}
|
|
345
370
|
function normalizeCommitHash(commitHash) {
|
|
346
371
|
return String(commitHash || '').trim();
|
|
347
372
|
}
|
|
@@ -630,12 +655,23 @@ gitRouter.post('/git/diff', validateToken, async (req, res) => {
|
|
|
630
655
|
res.status(400).json({ error: result.stderr || 'git diff failed' });
|
|
631
656
|
return;
|
|
632
657
|
}
|
|
658
|
+
const stagedNameStatusResult = await execGitCommand(context.repositoryRootPath, createScopedGitArgs(['diff', '--cached', '--name-status'], context));
|
|
659
|
+
if (stagedNameStatusResult.exitCode !== 0) {
|
|
660
|
+
res.status(400).json({ error: stagedNameStatusResult.stderr || 'git staged diff failed' });
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const stagedNumstatResult = await execGitCommand(context.repositoryRootPath, createScopedGitArgs(['diff', '--cached', '--numstat'], context));
|
|
664
|
+
if (stagedNumstatResult.exitCode !== 0) {
|
|
665
|
+
res.status(400).json({ error: stagedNumstatResult.stderr || 'git staged diff failed' });
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
633
668
|
const statusResult = await execGitCommand(context.repositoryRootPath, createScopedGitArgs(['status', '--porcelain=v1'], context));
|
|
634
669
|
if (statusResult.exitCode !== 0) {
|
|
635
670
|
res.status(400).json({ error: statusResult.stderr || 'git status failed' });
|
|
636
671
|
return;
|
|
637
672
|
}
|
|
638
|
-
const
|
|
673
|
+
const stagedPaths = parseGitStatusStagedPaths(statusResult.stdout);
|
|
674
|
+
const files = mergeGitDiffSummaryFiles(parseGitDiffSummary(nameStatusResult.stdout, result.stdout, stagedPaths), parseGitDiffSummary(stagedNameStatusResult.stdout, stagedNumstatResult.stdout, stagedPaths, true));
|
|
639
675
|
res.json({
|
|
640
676
|
ok: true,
|
|
641
677
|
isGitRepo: context.isGitRepo,
|
|
@@ -868,12 +904,28 @@ gitRouter.post('/git/diff-file', validateToken, async (req, res) => {
|
|
|
868
904
|
const normalizedCommitHash = normalizeCommitHash(commitHash);
|
|
869
905
|
const gitArgs = normalizedCommitHash
|
|
870
906
|
? ['show', '--format=', '--no-ext-diff', normalizedCommitHash, '--', repositoryRelativeFilePath]
|
|
871
|
-
: ['diff', '--', repositoryRelativeFilePath];
|
|
872
|
-
|
|
907
|
+
: ['diff', 'HEAD', '--', repositoryRelativeFilePath];
|
|
908
|
+
let result = await execGitCommand(context.repositoryRootPath, gitArgs);
|
|
909
|
+
if (!normalizedCommitHash && result.exitCode !== 0) {
|
|
910
|
+
result = await execGitCommand(context.repositoryRootPath, ['diff', '--', repositoryRelativeFilePath]);
|
|
911
|
+
}
|
|
873
912
|
if (result.exitCode !== 0) {
|
|
874
913
|
res.status(400).json({ error: result.stderr || 'git diff file failed' });
|
|
875
914
|
return;
|
|
876
915
|
}
|
|
916
|
+
if (!normalizedCommitHash && !result.stdout.trim()) {
|
|
917
|
+
const stagedResult = await execGitCommand(context.repositoryRootPath, [
|
|
918
|
+
'diff',
|
|
919
|
+
'--cached',
|
|
920
|
+
'--',
|
|
921
|
+
repositoryRelativeFilePath,
|
|
922
|
+
]);
|
|
923
|
+
if (stagedResult.exitCode !== 0) {
|
|
924
|
+
res.status(400).json({ error: stagedResult.stderr || 'git staged diff file failed' });
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
result = stagedResult;
|
|
928
|
+
}
|
|
877
929
|
res.json({
|
|
878
930
|
ok: true,
|
|
879
931
|
filePath: normalizedFilePath,
|
package/dist/routes/git.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createScopedFilePathspecArgs, createScopedGitPathspecArgs, normalizeGitCommitMessage,
|
|
2
|
+
import { createScopedFilePathspecArgs, createScopedGitPathspecArgs, mergeGitDiffSummaryFiles, normalizeGitCommitMessage, parseGitDiffSummary, parseGitStashListLine, parseGitStatusStagedPaths, parseLatestGitStash, } from './git.js';
|
|
3
3
|
describe('git commit helpers', () => {
|
|
4
4
|
it('normalizes commit messages before executing git commit', () => {
|
|
5
5
|
expect(normalizeGitCommitMessage(' chore: update dashboard ')).toBe('chore: update dashboard');
|
|
@@ -20,6 +20,45 @@ describe('git commit helpers', () => {
|
|
|
20
20
|
const stagedPaths = parseGitStatusStagedPaths('M staged.ts\n M unstaged.ts\nA added.ts\n?? untracked.ts\nR old.ts -> renamed.ts\n');
|
|
21
21
|
expect([...stagedPaths].sort()).toEqual(['added.ts', 'renamed.ts', 'staged.ts']);
|
|
22
22
|
});
|
|
23
|
+
it('marks cached diff files as staged when parsing staged summaries', () => {
|
|
24
|
+
const files = parseGitDiffSummary('M\tsrc/staged.ts\n', '2\t1\tsrc/staged.ts\n', new Set(), true);
|
|
25
|
+
expect(files).toEqual([
|
|
26
|
+
{
|
|
27
|
+
path: 'src/staged.ts',
|
|
28
|
+
status: 'modified',
|
|
29
|
+
additions: 2,
|
|
30
|
+
deletions: 1,
|
|
31
|
+
staged: true,
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
it('merges unstaged and staged summaries while preserving staged-only files', () => {
|
|
36
|
+
const unstagedFiles = parseGitDiffSummary('M\tsrc/mixed.ts\nM\tsrc/unstaged.ts\n', '1\t0\tsrc/mixed.ts\n3\t1\tsrc/unstaged.ts\n', new Set(['src/mixed.ts']));
|
|
37
|
+
const stagedFiles = parseGitDiffSummary('M\tsrc/mixed.ts\nA\tsrc/staged-only.ts\n', '2\t1\tsrc/mixed.ts\n5\t0\tsrc/staged-only.ts\n', new Set(['src/mixed.ts', 'src/staged-only.ts']), true);
|
|
38
|
+
expect(mergeGitDiffSummaryFiles(unstagedFiles, stagedFiles)).toEqual([
|
|
39
|
+
{
|
|
40
|
+
path: 'src/mixed.ts',
|
|
41
|
+
status: 'modified',
|
|
42
|
+
additions: 3,
|
|
43
|
+
deletions: 1,
|
|
44
|
+
staged: true,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
path: 'src/unstaged.ts',
|
|
48
|
+
status: 'modified',
|
|
49
|
+
additions: 3,
|
|
50
|
+
deletions: 1,
|
|
51
|
+
staged: false,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
path: 'src/staged-only.ts',
|
|
55
|
+
status: 'added',
|
|
56
|
+
additions: 5,
|
|
57
|
+
deletions: 0,
|
|
58
|
+
staged: true,
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
23
62
|
});
|
|
24
63
|
describe('git stash reference helpers', () => {
|
|
25
64
|
it('parses stable stash object hashes while preserving the display selector', () => {
|