zen-gitsync 2.16.13 → 2.16.14
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/src/cli/ai/sessionStore.js +97 -97
- package/src/config.js +52 -19
- package/src/ui/public/assets/AgentView-tIASiLaj.js +4 -0
- package/src/ui/public/assets/AppVersionBadge-BaS_yj3n.js +2 -0
- package/src/ui/public/assets/BranchSelector-B7RamkH9.js +1 -0
- package/src/ui/public/assets/CommitForm-DUjKTZ_Z.js +6 -0
- package/src/ui/public/assets/CommonDialog-ChSKsEz2.css +1 -0
- package/src/ui/public/assets/CommonDialog-FNHz2ADP.js +1 -0
- package/src/ui/public/assets/EditorView-BsLgJ2gr.js +0 -0
- package/src/ui/public/assets/FlowExecutionViewer-B6YdupsX.js +1 -0
- package/src/ui/public/assets/FlowOrchestrationWorkspace-DBeNhoNV.js +21 -0
- package/src/ui/public/assets/LogList-UvFfOD6q.js +15 -0
- package/src/ui/public/assets/MindmapView-DaUBWuao.js +2 -0
- package/src/ui/public/assets/MonitorView-DCYcyM3i.js +1 -0
- package/src/ui/public/assets/ProjectStartupButton-BLXDeEfO.js +1 -0
- package/src/ui/public/assets/RemoteRepoCard-RHaqsUVJ.js +5 -0
- package/src/ui/public/assets/SourceMapView-DRrnpkUE.js +3 -0
- package/src/ui/public/assets/SvgIcon-BDMUZ0Pv.js +1 -0
- package/src/ui/public/assets/SvgIcon-CDNsk4-Q.css +1 -0
- package/src/ui/public/assets/UserInputNode-BsMEfkJ_.js +1 -0
- package/src/ui/public/assets/WorkbenchView-BXBpVF4k.js +28 -0
- package/src/ui/public/assets/_plugin-vue_export-helper-DvYp0OXm.js +15 -0
- package/src/ui/public/assets/configStore-BoBfkqsG.js +1 -0
- package/src/ui/public/assets/element-plus-BId3Dnij.js +25 -0
- package/src/ui/public/assets/element-plus-D3Dn7nX0.css +1 -0
- package/src/ui/public/assets/{flow-mindmap-CGn6wqsF.js → flow-mindmap-DnyDhwJv.js} +1 -1
- package/src/ui/public/assets/index-CiQmfjtm.css +1 -0
- package/src/ui/public/assets/index-DHzXn8Zw.js +68 -0
- package/src/ui/public/assets/{monaco-DPQ3OaiL.js → monaco-Dje8Rg13.js} +2 -2
- package/src/ui/public/assets/socket-io-CGezBd--.js +1 -0
- package/src/ui/public/assets/{ts.worker-METxwbDZ.js → ts.worker-Dth06zuC.js} +16 -1
- package/src/ui/public/assets/{vendor-C2ZpZPB4.css → vendor-DVvq7qtS.css} +1 -1
- package/src/ui/public/assets/{vendor-pbWglt5U.js → vendor-exjCs1jx.js} +67 -26
- package/src/ui/public/assets/vue-flow-Dj_X_Vr1.js +39 -0
- package/src/ui/public/index.html +15 -9
- package/src/ui/server/routes/git.js +36 -5
- package/src/ui/server/routes/git.test.js +147 -0
- package/src/ui/server/routes/gitOps.js +107 -3
- package/src/ui/server/routes/workbench/agentRoutes.js +218 -218
- package/src/ui/server/routes/workbench/agentSessionStore.js +173 -173
- package/src/ui/public/assets/AgentView-DpHVftKB.js +0 -4
- package/src/ui/public/assets/AppVersionBadge-DoBF_YRQ.js +0 -2
- package/src/ui/public/assets/BranchSelector-BG5-5Rji.js +0 -1
- package/src/ui/public/assets/CommitForm-CRvh9bMD.js +0 -6
- package/src/ui/public/assets/EditorView-D8Rj7g4x.js +0 -0
- package/src/ui/public/assets/FlowExecutionViewer-BblOVK2I.js +0 -1
- package/src/ui/public/assets/FlowOrchestrationWorkspace-DYJtdPCz.js +0 -21
- package/src/ui/public/assets/LogList-BJDC-tvx.js +0 -15
- package/src/ui/public/assets/MindmapView-DVKkHt-H.js +0 -2
- package/src/ui/public/assets/MonitorView-ZHUyTSjI.js +0 -1
- package/src/ui/public/assets/ProjectStartupButton-CRpmOPMY.js +0 -1
- package/src/ui/public/assets/RemoteRepoCard-oN1X-RTu.js +0 -5
- package/src/ui/public/assets/SourceMapView-DWjOj5HG.js +0 -3
- package/src/ui/public/assets/UserInputNode-DUS9U0qM.js +0 -1
- package/src/ui/public/assets/WorkbenchView-qCRoJV-3.js +0 -28
- package/src/ui/public/assets/element-plus-C0loJL0J.js +0 -32
- package/src/ui/public/assets/element-plus-CXfbWXaJ.css +0 -1
- package/src/ui/public/assets/index-DLfjXhwQ.js +0 -82
- package/src/ui/public/assets/index-Fbjp0DKn.css +0 -1
- package/src/ui/public/assets/socket-io-B9UuzwTN.js +0 -1
- package/src/ui/public/assets/vue-flow-DRwHh4gM.js +0 -39
- /package/src/ui/public/assets/{dagre-CePNlbK4.js → dagre-BCXBo6I9.js} +0 -0
|
@@ -95,14 +95,45 @@ export function registerGitRoutes({
|
|
|
95
95
|
if (!branch) {
|
|
96
96
|
throw new HttpError(400, '分支名称不能为空');
|
|
97
97
|
}
|
|
98
|
-
|
|
98
|
+
|
|
99
|
+
let finalBranch = branch;
|
|
100
|
+
|
|
101
|
+
// 远程分支(如 origin/xxx):直接 checkout 远程引用会进入 detached HEAD,
|
|
102
|
+
// 需要先解析出同名本地分支——已存在则直接切换,不存在则基于远程分支创建并跟踪。
|
|
103
|
+
if (branch.includes('/')) {
|
|
104
|
+
const { stdout: remoteRef } = await execGitCommand(
|
|
105
|
+
['rev-parse', '--verify', '--quiet', `refs/remotes/${branch}`],
|
|
106
|
+
{ ignoreError: true, log: false }
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (remoteRef.trim()) {
|
|
110
|
+
// 去掉第一个路径段(远程名),得到本地分支名,支持 origin/feature/x 这类多级名称
|
|
111
|
+
const localName = branch.substring(branch.indexOf('/') + 1);
|
|
112
|
+
const { stdout: localRef } = await execGitCommand(
|
|
113
|
+
['rev-parse', '--verify', '--quiet', `refs/heads/${localName}`],
|
|
114
|
+
{ ignoreError: true, log: false }
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (localRef.trim()) {
|
|
118
|
+
// 本地已有同名分支,直接切换
|
|
119
|
+
finalBranch = localName;
|
|
120
|
+
} else {
|
|
121
|
+
// 本地没有:创建同名本地分支并跟踪该远程分支
|
|
122
|
+
await execGitCommand(['checkout', '--track', branch]);
|
|
123
|
+
clearBranchCache();
|
|
124
|
+
res.json({ success: true, branch: localName, created: true });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
99
130
|
// 执行分支切换
|
|
100
|
-
await execGitCommand(['checkout',
|
|
101
|
-
|
|
131
|
+
await execGitCommand(['checkout', finalBranch]);
|
|
132
|
+
|
|
102
133
|
// 清除分支缓存,因为分支已切换
|
|
103
134
|
clearBranchCache();
|
|
104
|
-
|
|
105
|
-
res.json({ success: true });
|
|
135
|
+
|
|
136
|
+
res.json({ success: true, branch: finalBranch });
|
|
106
137
|
} catch (error) {
|
|
107
138
|
logger.error('切换分支失败:', error);
|
|
108
139
|
res.status(500).json({ success: false, error: error.message });
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Copyright 2026 xz333221
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
//
|
|
15
|
+
// src/ui/server/routes/git.js 单元测试。
|
|
16
|
+
// 验证 POST /api/checkout 对远程分支的处理:
|
|
17
|
+
// 选中 origin/xxx 时应切到同名本地分支,不存在则 --track 自动创建,
|
|
18
|
+
// 而不是直接 checkout 远程引用进入 detached HEAD。
|
|
19
|
+
//
|
|
20
|
+
// 测试不真起 Express,直接调 registerGitRoutes 注入 mock app。
|
|
21
|
+
import { test } from 'node:test'
|
|
22
|
+
import assert from 'node:assert/strict'
|
|
23
|
+
import { registerGitRoutes } from './git.js'
|
|
24
|
+
|
|
25
|
+
/** 最小 express app mock:只实现 get/post + handler 调用 */
|
|
26
|
+
function makeApp() {
|
|
27
|
+
const handlers = new Map() // key = "METHOD path" → handler
|
|
28
|
+
return {
|
|
29
|
+
get(path, handler) { handlers.set(`GET ${path}`, handler) },
|
|
30
|
+
post(...args) {
|
|
31
|
+
// create-branch 等路由带 express.json() 中间件,handler 永远是最后一个参数
|
|
32
|
+
const path = args[0]
|
|
33
|
+
const handler = args[args.length - 1]
|
|
34
|
+
handlers.set(`POST ${path}`, handler)
|
|
35
|
+
},
|
|
36
|
+
invoke(method, path, req = { query: {}, body: {} }, res = makeRes()) {
|
|
37
|
+
const key = `${method} ${path}`
|
|
38
|
+
const handler = handlers.get(key)
|
|
39
|
+
assert.ok(handler, `no handler for ${key}`)
|
|
40
|
+
return handler(req, res, () => {})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function makeRes() {
|
|
45
|
+
return {
|
|
46
|
+
statusCode: 200,
|
|
47
|
+
payload: null,
|
|
48
|
+
status(c) { this.statusCode = c; return this },
|
|
49
|
+
json(p) { this.payload = p; return this }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 构造注入依赖,calls 记录所有 git 子命令 */
|
|
54
|
+
function makeDeps(app, execGitCommand) {
|
|
55
|
+
const calls = []
|
|
56
|
+
registerGitRoutes({
|
|
57
|
+
app,
|
|
58
|
+
execGitCommand: async (args, opts) => { calls.push(args); return execGitCommand(args, opts) },
|
|
59
|
+
clearBranchCache: () => {}
|
|
60
|
+
})
|
|
61
|
+
return calls
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ========== /api/checkout 远程分支处理 ==========
|
|
65
|
+
|
|
66
|
+
test('checkout: 本地分支 → 直接 checkout,不做远程探测', async () => {
|
|
67
|
+
const app = makeApp()
|
|
68
|
+
const calls = makeDeps(app, async () => ({ stdout: '' }))
|
|
69
|
+
const res = makeRes()
|
|
70
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'develop' } }, res)
|
|
71
|
+
assert.equal(res.statusCode, 200)
|
|
72
|
+
assert.deepEqual(res.payload, { success: true, branch: 'develop' })
|
|
73
|
+
assert.deepEqual(calls, [['checkout', 'develop']])
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('checkout: 远程分支 + 本地已存在同名分支 → 切到本地分支', async () => {
|
|
77
|
+
const app = makeApp()
|
|
78
|
+
const calls = makeDeps(app, async (args) => {
|
|
79
|
+
if (args[0] === 'rev-parse' && args.includes('refs/remotes/origin/release-v1')) return { stdout: 'abc123\n' }
|
|
80
|
+
if (args[0] === 'rev-parse' && args.includes('refs/heads/release-v1')) return { stdout: 'def456\n' }
|
|
81
|
+
return { stdout: '' }
|
|
82
|
+
})
|
|
83
|
+
const res = makeRes()
|
|
84
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/release-v1' } }, res)
|
|
85
|
+
assert.equal(res.statusCode, 200)
|
|
86
|
+
assert.deepEqual(res.payload, { success: true, branch: 'release-v1' })
|
|
87
|
+
// 最后一步必须是 checkout 本地分支名,而不是 checkout origin/release-v1(detached HEAD)
|
|
88
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', 'release-v1'])
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('checkout: 远程分支 + 本地无同名分支 → checkout --track 自动创建', async () => {
|
|
92
|
+
const app = makeApp()
|
|
93
|
+
const calls = makeDeps(app, async (args) => {
|
|
94
|
+
if (args[0] === 'rev-parse' && args.includes('refs/remotes/origin/release-v1')) return { stdout: 'abc123\n' }
|
|
95
|
+
if (args[0] === 'rev-parse' && args.includes('refs/heads/release-v1')) return { stdout: '' } // 本地不存在
|
|
96
|
+
return { stdout: '' }
|
|
97
|
+
})
|
|
98
|
+
const res = makeRes()
|
|
99
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/release-v1' } }, res)
|
|
100
|
+
assert.equal(res.statusCode, 200)
|
|
101
|
+
assert.deepEqual(res.payload, { success: true, branch: 'release-v1', created: true })
|
|
102
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', '--track', 'origin/release-v1'])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('checkout: 多级远程分支(origin/feature/x)→ 本地名取第一个斜杠之后部分', async () => {
|
|
106
|
+
const app = makeApp()
|
|
107
|
+
const calls = makeDeps(app, async (args) => {
|
|
108
|
+
if (args[0] === 'rev-parse' && args.includes('refs/remotes/origin/feature/x')) return { stdout: 'abc123\n' }
|
|
109
|
+
if (args[0] === 'rev-parse' && args.includes('refs/heads/feature/x')) return { stdout: '' }
|
|
110
|
+
return { stdout: '' }
|
|
111
|
+
})
|
|
112
|
+
const res = makeRes()
|
|
113
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/feature/x' } }, res)
|
|
114
|
+
assert.equal(res.statusCode, 200)
|
|
115
|
+
assert.deepEqual(res.payload, { success: true, branch: 'feature/x', created: true })
|
|
116
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', '--track', 'origin/feature/x'])
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('checkout: 带斜杠但远程引用不存在(如本地 feature/x)→ 按原名直接 checkout', async () => {
|
|
120
|
+
const app = makeApp()
|
|
121
|
+
const calls = makeDeps(app, async (args) => {
|
|
122
|
+
if (args[0] === 'rev-parse') return { stdout: '' } // refs/remotes/feature/x 不存在
|
|
123
|
+
return { stdout: '' }
|
|
124
|
+
})
|
|
125
|
+
const res = makeRes()
|
|
126
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'feature/x' } }, res)
|
|
127
|
+
assert.equal(res.statusCode, 200)
|
|
128
|
+
assert.deepEqual(res.payload, { success: true, branch: 'feature/x' })
|
|
129
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', 'feature/x'])
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('checkout: rev-parse 探测抛错 → ignoreError 吞下,按原名 checkout(非 500)', async () => {
|
|
133
|
+
const app = makeApp()
|
|
134
|
+
const calls = makeDeps(app, async (args, opts) => {
|
|
135
|
+
if (args[0] === 'rev-parse') {
|
|
136
|
+
const err = new Error('Command failed: git rev-parse --verify --quiet refs/remotes/origin/gone')
|
|
137
|
+
err.code = 128
|
|
138
|
+
if (opts && opts.ignoreError) return { stdout: '' }
|
|
139
|
+
throw err
|
|
140
|
+
}
|
|
141
|
+
return { stdout: '' }
|
|
142
|
+
})
|
|
143
|
+
const res = makeRes()
|
|
144
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/gone' } }, res)
|
|
145
|
+
assert.equal(res.statusCode, 200, '探测失败应降级为普通 checkout,不应 500')
|
|
146
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', 'origin/gone'])
|
|
147
|
+
})
|
|
@@ -36,7 +36,15 @@ export function registerGitOpsRoutes({
|
|
|
36
36
|
setRecentPushStatus
|
|
37
37
|
}) {
|
|
38
38
|
// 提交更改
|
|
39
|
+
// 锁定文件排除逻辑:提交前检测暂存区中是否有锁定文件,
|
|
40
|
+
// 如果有则临时 git reset HEAD 取消暂存 → 提交 → 恢复暂存状态。
|
|
41
|
+
// 这样用户可以从左侧文件列表区域自由操作锁定文件(暂存/撤回),
|
|
42
|
+
// 但右侧提交时锁定文件不会被提交。
|
|
39
43
|
app.post('/api/commit', express.json(), async (req, res) => {
|
|
44
|
+
let tempFile = null;
|
|
45
|
+
// 临时取消暂存的锁定文件列表(提交后需恢复)
|
|
46
|
+
let unstagedLockedFiles = [];
|
|
47
|
+
|
|
40
48
|
try {
|
|
41
49
|
const { message, hasNewlines, noVerify } = req.body;
|
|
42
50
|
|
|
@@ -44,7 +52,6 @@ export function registerGitOpsRoutes({
|
|
|
44
52
|
const commitArgs = ['commit'];
|
|
45
53
|
|
|
46
54
|
// 如果消息包含换行符,使用文件方式提交
|
|
47
|
-
let tempFile
|
|
48
55
|
if (hasNewlines) {
|
|
49
56
|
// 创建临时文件存储提交信息
|
|
50
57
|
tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
|
|
@@ -60,17 +67,114 @@ export function registerGitOpsRoutes({
|
|
|
60
67
|
commitArgs.push('--no-verify');
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
// ====== 锁定文件排除逻辑 ======
|
|
71
|
+
const lockedFiles = await configManager.getLockedFiles();
|
|
72
|
+
let skippedLockedFiles = [];
|
|
73
|
+
|
|
74
|
+
if (lockedFiles.length > 0) {
|
|
75
|
+
// 获取当前暂存区文件列表
|
|
76
|
+
const { stdout: stagedOutput } = await execGitCommand(
|
|
77
|
+
['diff', '--cached', '--name-only'], { log: false }
|
|
78
|
+
);
|
|
79
|
+
const stagedFiles = stagedOutput.split('\n').filter(Boolean);
|
|
80
|
+
|
|
81
|
+
if (stagedFiles.length > 0) {
|
|
82
|
+
// 获取 Git 根目录,用于将锁定文件的绝对路径转为 git 相对路径
|
|
83
|
+
let gitRoot;
|
|
84
|
+
try {
|
|
85
|
+
const { stdout: rootOut } = await execGitCommand(
|
|
86
|
+
['rev-parse', '--show-toplevel'], { log: false }
|
|
87
|
+
);
|
|
88
|
+
gitRoot = path.normalize(rootOut.trim());
|
|
89
|
+
} catch (_) {
|
|
90
|
+
gitRoot = path.normalize(process.cwd());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 将锁定文件路径统一为 git 相对路径(正斜杠)以便与
|
|
94
|
+
// git diff --cached --name-only 的输出进行比较
|
|
95
|
+
function toGitRelative(p) {
|
|
96
|
+
const normalized = path.normalize(p);
|
|
97
|
+
const relative = (path.isAbsolute(normalized) && normalized.startsWith(gitRoot))
|
|
98
|
+
? path.relative(gitRoot, normalized)
|
|
99
|
+
: normalized;
|
|
100
|
+
return relative.replace(/\\/g, '/');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const lockedRelativeSet = new Set(lockedFiles.map(toGitRelative));
|
|
104
|
+
|
|
105
|
+
// 找出暂存区中的锁定文件
|
|
106
|
+
unstagedLockedFiles = stagedFiles.filter(
|
|
107
|
+
f => lockedRelativeSet.has(f.replace(/\\/g, '/'))
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (unstagedLockedFiles.length > 0) {
|
|
111
|
+
// 临时取消暂存锁定文件
|
|
112
|
+
await execGitCommand(
|
|
113
|
+
['reset', 'HEAD', '--', ...unstagedLockedFiles], { log: false }
|
|
114
|
+
);
|
|
115
|
+
skippedLockedFiles = unstagedLockedFiles;
|
|
116
|
+
|
|
117
|
+
// 检查取消暂存后是否还有可提交的内容
|
|
118
|
+
const { stdout: remainingStaged } = await execGitCommand(
|
|
119
|
+
['diff', '--cached', '--name-only'], { log: false }
|
|
120
|
+
);
|
|
121
|
+
if (!remainingStaged.trim()) {
|
|
122
|
+
// 所有暂存文件都是锁定的 — 恢复暂存状态并返回提示
|
|
123
|
+
await execGitCommand(
|
|
124
|
+
['add', '--', ...unstagedLockedFiles], { log: false }
|
|
125
|
+
);
|
|
126
|
+
if (tempFile) {
|
|
127
|
+
await fs.unlink(tempFile).catch(() => {});
|
|
128
|
+
}
|
|
129
|
+
res.json({
|
|
130
|
+
success: false,
|
|
131
|
+
error: '所有暂存文件都被锁定,没有可提交的内容',
|
|
132
|
+
skippedLockedFiles,
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
63
140
|
logger.info(`commitArgs ==>`, commitArgs);
|
|
64
141
|
// 执行提交命令
|
|
65
142
|
await execGitCommand(commitArgs);
|
|
66
143
|
|
|
67
144
|
// 如果使用了临时文件,删除它
|
|
68
|
-
if (
|
|
145
|
+
if (tempFile) {
|
|
69
146
|
await fs.unlink(tempFile).catch(() => {});
|
|
70
147
|
}
|
|
71
148
|
|
|
72
|
-
|
|
149
|
+
// 恢复锁定文件的暂存状态(保留用户在左侧文件列表中的暂存操作)
|
|
150
|
+
if (unstagedLockedFiles.length > 0) {
|
|
151
|
+
try {
|
|
152
|
+
await execGitCommand(
|
|
153
|
+
['add', '--', ...unstagedLockedFiles], { log: false }
|
|
154
|
+
);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
logger.warn('恢复锁定文件暂存状态失败:', e.message);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
res.json({
|
|
161
|
+
success: true,
|
|
162
|
+
skippedLockedFiles: skippedLockedFiles.length > 0
|
|
163
|
+
? skippedLockedFiles
|
|
164
|
+
: undefined,
|
|
165
|
+
});
|
|
73
166
|
} catch (error) {
|
|
167
|
+
// 出错时也要尝试恢复锁定文件的暂存状态
|
|
168
|
+
if (unstagedLockedFiles.length > 0) {
|
|
169
|
+
try {
|
|
170
|
+
await execGitCommand(
|
|
171
|
+
['add', '--', ...unstagedLockedFiles], { log: false }
|
|
172
|
+
);
|
|
173
|
+
} catch (_) { /* 忽略恢复失败,不掩盖原始错误 */ }
|
|
174
|
+
}
|
|
175
|
+
if (tempFile) {
|
|
176
|
+
await fs.unlink(tempFile).catch(() => {});
|
|
177
|
+
}
|
|
74
178
|
res.status(500).json({ success: false, error: error.message });
|
|
75
179
|
}
|
|
76
180
|
});
|