aws-runtime-bridge 1.7.35 → 1.7.36
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 +14 -1
- package/dist/routes/git.d.ts.map +1 -1
- package/dist/routes/git.js +70 -34
- package/dist/routes/git.test.js +21 -1
- package/package.json +1 -1
package/dist/routes/git.d.ts
CHANGED
|
@@ -18,6 +18,13 @@ interface GitRepositoryContext {
|
|
|
18
18
|
relativeWorkspacePath: string;
|
|
19
19
|
}
|
|
20
20
|
export declare function assertGitWorkspacePathAccessible(workspacePath: string): Promise<void>;
|
|
21
|
+
interface GitStashItem {
|
|
22
|
+
ref: string;
|
|
23
|
+
stashName?: string;
|
|
24
|
+
message: string;
|
|
25
|
+
branch: string;
|
|
26
|
+
createdAt: string | null;
|
|
27
|
+
}
|
|
21
28
|
type GitDiffFileStatus = 'modified' | 'added' | 'deleted' | 'renamed';
|
|
22
29
|
/**
|
|
23
30
|
* 判断 Git diff 汇总行是否应该展示在差异树中。
|
|
@@ -37,11 +44,17 @@ export declare function createMissingInitialCommitSnapshotError(): string;
|
|
|
37
44
|
export declare function isGitStashNoChangesOutput(output: string): boolean;
|
|
38
45
|
interface LatestGitStash {
|
|
39
46
|
ref: string;
|
|
47
|
+
stashName: string;
|
|
40
48
|
message: string;
|
|
41
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* 解析 git stash list --format 输出行。
|
|
52
|
+
* 主流程:优先返回稳定 stash commit hash 作为 ref,同时保留 stash@{n} 供界面展示。
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseGitStashListLine(line: string): GitStashItem | null;
|
|
42
55
|
/**
|
|
43
56
|
* 从 git stash list -n 1 输出解析最近一次 stash。
|
|
44
|
-
*
|
|
57
|
+
* 主流程:提取稳定 stash commit hash,供无变更快照复用最近快照引用。
|
|
45
58
|
*/
|
|
46
59
|
export declare function parseLatestGitStash(output: string): LatestGitStash | null;
|
|
47
60
|
/**
|
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;AASH,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;
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/routes/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,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;AAkBtE;;;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;AAkDD,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
|
@@ -71,20 +71,73 @@ export function isGitStashNoChangesOutput(output) {
|
|
|
71
71
|
|| normalized.includes('no changes to save')
|
|
72
72
|
|| normalized.includes('没有本地变更需要保存');
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* 解析 git stash list --format 输出行。
|
|
76
|
+
* 主流程:优先返回稳定 stash commit hash 作为 ref,同时保留 stash@{n} 供界面展示。
|
|
77
|
+
*/
|
|
78
|
+
export function parseGitStashListLine(line) {
|
|
79
|
+
const rawLine = String(line || '').trim();
|
|
80
|
+
if (!rawLine)
|
|
81
|
+
return null;
|
|
82
|
+
const fields = rawLine.split('\0');
|
|
83
|
+
if (fields.length >= 3) {
|
|
84
|
+
const stableRef = fields[0]?.trim();
|
|
85
|
+
const stashName = fields[1]?.trim();
|
|
86
|
+
const subject = fields[2]?.trim() || stashName;
|
|
87
|
+
const createdAt = fields[3]?.trim() || null;
|
|
88
|
+
if (!stableRef || !stashName)
|
|
89
|
+
return null;
|
|
90
|
+
const parsedSubject = parseGitStashSubject(subject || '');
|
|
91
|
+
return {
|
|
92
|
+
ref: stableRef,
|
|
93
|
+
stashName,
|
|
94
|
+
message: parsedSubject.message,
|
|
95
|
+
branch: parsedSubject.branch,
|
|
96
|
+
createdAt,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const match = rawLine.match(/^(stash@\{\d+\})(?::\s*)?(.*)$/);
|
|
100
|
+
if (!match)
|
|
101
|
+
return null;
|
|
102
|
+
const stashName = match[1];
|
|
103
|
+
const parsedSubject = parseGitStashSubject(match[2]?.trim() || stashName);
|
|
104
|
+
return {
|
|
105
|
+
ref: stashName,
|
|
106
|
+
stashName,
|
|
107
|
+
message: parsedSubject.message,
|
|
108
|
+
branch: parsedSubject.branch,
|
|
109
|
+
createdAt: null,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 从 stash subject 中拆出分支和用户消息。
|
|
114
|
+
*/
|
|
115
|
+
function parseGitStashSubject(subject) {
|
|
116
|
+
const normalized = String(subject || '').trim();
|
|
117
|
+
const branchMatch = normalized.match(/^(?:On|WIP on)\s+([^:]+):\s*(.*)$/);
|
|
118
|
+
if (branchMatch) {
|
|
119
|
+
return {
|
|
120
|
+
branch: branchMatch[1].trim(),
|
|
121
|
+
message: branchMatch[2].trim() || normalized,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return { branch: '', message: normalized };
|
|
125
|
+
}
|
|
74
126
|
/**
|
|
75
127
|
* 从 git stash list -n 1 输出解析最近一次 stash。
|
|
76
|
-
*
|
|
128
|
+
* 主流程:提取稳定 stash commit hash,供无变更快照复用最近快照引用。
|
|
77
129
|
*/
|
|
78
130
|
export function parseLatestGitStash(output) {
|
|
79
131
|
const firstLine = String(output || '').split('\n').find(line => line.trim());
|
|
80
132
|
if (!firstLine)
|
|
81
133
|
return null;
|
|
82
|
-
const
|
|
83
|
-
if (!
|
|
134
|
+
const parsed = parseGitStashListLine(firstLine);
|
|
135
|
+
if (!parsed)
|
|
84
136
|
return null;
|
|
85
137
|
return {
|
|
86
|
-
ref:
|
|
87
|
-
|
|
138
|
+
ref: parsed.ref,
|
|
139
|
+
stashName: parsed.stashName || parsed.ref,
|
|
140
|
+
message: parsed.message || parsed.ref,
|
|
88
141
|
};
|
|
89
142
|
}
|
|
90
143
|
/**
|
|
@@ -374,13 +427,14 @@ gitRouter.post('/git/stash/create', validateToken, async (req, res) => {
|
|
|
374
427
|
: defaultMessage;
|
|
375
428
|
const result = await execGitCommand(normalizedPath, ['stash', 'push', '-m', stashMessage]);
|
|
376
429
|
if (isGitStashNoChangesOutput(`${result.stdout}\n${result.stderr}`)) {
|
|
377
|
-
const latestResult = await execGitCommand(normalizedPath, ['stash', 'list', '-n', '1']);
|
|
430
|
+
const latestResult = await execGitCommand(normalizedPath, ['stash', 'list', '-n', '1', '--format=%H%x00%gd%x00%gs%x00%cr']);
|
|
378
431
|
const latestStash = latestResult.exitCode === 0 ? parseLatestGitStash(latestResult.stdout) : null;
|
|
379
432
|
res.json({
|
|
380
433
|
ok: true,
|
|
381
434
|
stashRef: latestStash?.ref ?? null,
|
|
435
|
+
stashName: latestStash?.stashName ?? null,
|
|
382
436
|
message: latestStash
|
|
383
|
-
? `没有本地变更需要保存,已复用最近快照 ${latestStash.
|
|
437
|
+
? `没有本地变更需要保存,已复用最近快照 ${latestStash.stashName}`
|
|
384
438
|
: '没有本地变更需要保存',
|
|
385
439
|
noChanges: true,
|
|
386
440
|
reusedLatestStash: Boolean(latestStash),
|
|
@@ -396,13 +450,13 @@ gitRouter.post('/git/stash/create', validateToken, async (req, res) => {
|
|
|
396
450
|
res.status(400).json({ error: result.stderr || 'git stash push failed' });
|
|
397
451
|
return;
|
|
398
452
|
}
|
|
399
|
-
const listResult = await execGitCommand(normalizedPath, ['stash', 'list', '-n', '1']);
|
|
400
|
-
const
|
|
401
|
-
const
|
|
402
|
-
const stashRef = match ? match[1] : 'stash@{0}';
|
|
453
|
+
const listResult = await execGitCommand(normalizedPath, ['stash', 'list', '-n', '1', '--format=%H%x00%gd%x00%gs%x00%cr']);
|
|
454
|
+
const latestStash = listResult.exitCode === 0 ? parseLatestGitStash(listResult.stdout) : null;
|
|
455
|
+
const stashRef = latestStash?.ref || 'stash@{0}';
|
|
403
456
|
res.json({
|
|
404
457
|
ok: true,
|
|
405
458
|
stashRef,
|
|
459
|
+
stashName: latestStash?.stashName ?? null,
|
|
406
460
|
message: stashMessage,
|
|
407
461
|
workspacePath: normalizedPath
|
|
408
462
|
});
|
|
@@ -425,33 +479,15 @@ gitRouter.post('/git/stash/list', validateToken, async (req, res) => {
|
|
|
425
479
|
try {
|
|
426
480
|
const normalizedPath = path.resolve(String(workspacePath).trim());
|
|
427
481
|
const stashLimit = Math.min(Math.max(1, Number(limit) || 20), 100);
|
|
428
|
-
const result = await execGitCommand(normalizedPath, ['stash', 'list',
|
|
482
|
+
const result = await execGitCommand(normalizedPath, ['stash', 'list', '-n', String(stashLimit), '--format=%H%x00%gd%x00%gs%x00%cr']);
|
|
429
483
|
if (result.exitCode !== 0) {
|
|
430
484
|
res.status(400).json({ error: result.stderr || 'git stash list failed' });
|
|
431
485
|
return;
|
|
432
486
|
}
|
|
433
|
-
const stashes =
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (!refMatch)
|
|
438
|
-
continue;
|
|
439
|
-
const ref = refMatch[1];
|
|
440
|
-
const restOfLine = line.slice(ref.length + 2);
|
|
441
|
-
let branch = '';
|
|
442
|
-
let message = restOfLine;
|
|
443
|
-
const branchMatch = restOfLine.match(/^(?:On|WIP on)\s+([^:]+):\s*(.*)$/);
|
|
444
|
-
if (branchMatch) {
|
|
445
|
-
branch = branchMatch[1].trim();
|
|
446
|
-
message = branchMatch[2].trim();
|
|
447
|
-
}
|
|
448
|
-
stashes.push({
|
|
449
|
-
ref,
|
|
450
|
-
message,
|
|
451
|
-
branch,
|
|
452
|
-
createdAt: null,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
487
|
+
const stashes = result.stdout
|
|
488
|
+
.split('\n')
|
|
489
|
+
.map(parseGitStashListLine)
|
|
490
|
+
.filter((stash) => stash !== null);
|
|
455
491
|
res.json({
|
|
456
492
|
ok: true,
|
|
457
493
|
stashes,
|
package/dist/routes/git.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createScopedFilePathspecArgs, createScopedGitPathspecArgs, normalizeGitCommitMessage, parseGitStatusStagedPaths, } from './git.js';
|
|
2
|
+
import { createScopedFilePathspecArgs, createScopedGitPathspecArgs, normalizeGitCommitMessage, parseGitStashListLine, parseLatestGitStash, parseGitStatusStagedPaths, } 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');
|
|
@@ -21,3 +21,23 @@ describe('git commit helpers', () => {
|
|
|
21
21
|
expect([...stagedPaths].sort()).toEqual(['added.ts', 'renamed.ts', 'staged.ts']);
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
|
+
describe('git stash reference helpers', () => {
|
|
25
|
+
it('parses stable stash object hashes while preserving the display selector', () => {
|
|
26
|
+
const stash = parseGitStashListLine('0123456789abcdef0123456789abcdef01234567\u0000stash@{3}\u0000On main: 快照: 任务线-2026-06-02 10:00:00\u00002 hours ago');
|
|
27
|
+
expect(stash).toEqual({
|
|
28
|
+
ref: '0123456789abcdef0123456789abcdef01234567',
|
|
29
|
+
stashName: 'stash@{3}',
|
|
30
|
+
message: '快照: 任务线-2026-06-02 10:00:00',
|
|
31
|
+
branch: 'main',
|
|
32
|
+
createdAt: '2 hours ago',
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
it('uses the stable stash object hash for the latest no-change snapshot reuse', () => {
|
|
36
|
+
const latest = parseLatestGitStash('fedcba9876543210fedcba9876543210fedcba98\u0000stash@{0}\u0000On master: 快照: latest\u0000just now\n');
|
|
37
|
+
expect(latest).toEqual({
|
|
38
|
+
ref: 'fedcba9876543210fedcba9876543210fedcba98',
|
|
39
|
+
stashName: 'stash@{0}',
|
|
40
|
+
message: '快照: latest',
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|