zen-gitsync 2.1.26 → 2.1.29
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 +3 -3
- package/scripts/release.js +12 -0
- package/src/ui/public/assets/index-BKjt1yVw.js +44 -0
- package/src/ui/public/assets/index-th8vfo_E.css +1 -0
- package/src/ui/public/assets/vendor-Ym9LLftt.js +45 -0
- package/src/ui/public/index.html +3 -3
- package/src/ui/server/index.js +395 -150
- package/src/utils/index.js +85 -8
- package/src/ui/public/assets/index-Cj-n4CZW.css +0 -1
- package/src/ui/public/assets/index-LmXVzmKV.js +0 -22
- package/src/ui/public/assets/vendor-LDRpPx4a.js +0 -45
package/src/ui/server/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import express from 'express';
|
|
|
2
2
|
import { createServer } from 'http';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { execGitCommand } from '../../utils/index.js';
|
|
5
|
+
import { execGitCommand, getCommandHistory, clearCommandHistory, registerSocketIO } from '../../utils/index.js';
|
|
6
6
|
import open from 'open';
|
|
7
7
|
import config from '../../config.js';
|
|
8
8
|
import chalk from 'chalk';
|
|
@@ -23,11 +23,45 @@ let debounceTimer = null;
|
|
|
23
23
|
// 防抖延迟时间 (毫秒)
|
|
24
24
|
const DEBOUNCE_DELAY = 1000;
|
|
25
25
|
|
|
26
|
+
// 分支状态缓存
|
|
27
|
+
let branchStatusCache = {
|
|
28
|
+
currentBranch: null,
|
|
29
|
+
upstreamBranch: null,
|
|
30
|
+
lastUpdate: 0,
|
|
31
|
+
cacheTimeout: 5000 // 5秒缓存
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// 当前分支缓存 - 只在特定情况下更新
|
|
35
|
+
let currentBranchCache = {
|
|
36
|
+
branchName: null,
|
|
37
|
+
lastUpdate: 0,
|
|
38
|
+
// 分支名缓存时间更长,因为分支切换不频繁
|
|
39
|
+
cacheTimeout: 300000 // 5分钟缓存,或者直到主动清除
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// 上游分支缓存 - 只在特定情况下更新
|
|
43
|
+
let upstreamBranchCache = {
|
|
44
|
+
upstreamBranch: null,
|
|
45
|
+
lastUpdate: 0,
|
|
46
|
+
// 上游分支缓存时间也较长,因为上游分支设置不频繁
|
|
47
|
+
cacheTimeout: 300000 // 5分钟缓存,或者直到主动清除
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// 推送状态标记 - 用于优化推送后的分支状态查询
|
|
51
|
+
let recentPushStatus = {
|
|
52
|
+
justPushed: false,
|
|
53
|
+
pushTime: 0,
|
|
54
|
+
validDuration: 10000 // 推送后10秒内认为分支状态是同步的
|
|
55
|
+
};
|
|
56
|
+
|
|
26
57
|
async function startUIServer(noOpen = false, savePort = false) {
|
|
27
58
|
const app = express();
|
|
28
59
|
const httpServer = createServer(app);
|
|
29
60
|
const io = new Server(httpServer);
|
|
30
61
|
|
|
62
|
+
// 注册Socket.io实例,用于命令历史通知
|
|
63
|
+
registerSocketIO(io);
|
|
64
|
+
|
|
31
65
|
// 添加全局中间件来解析JSON请求体
|
|
32
66
|
app.use(express.json());
|
|
33
67
|
|
|
@@ -42,14 +76,18 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
42
76
|
app.use(express.static(path.join(__dirname, '../public')));
|
|
43
77
|
|
|
44
78
|
// API路由
|
|
45
|
-
|
|
79
|
+
// 移除了 /api/status 端点,因为前端只使用 porcelain 格式
|
|
80
|
+
|
|
81
|
+
// Add new endpoint for command history
|
|
82
|
+
app.get('/api/command-history', async (req, res) => {
|
|
46
83
|
try {
|
|
47
|
-
const
|
|
48
|
-
res.json({
|
|
84
|
+
const history = getCommandHistory();
|
|
85
|
+
res.json({ success: true, history });
|
|
49
86
|
} catch (error) {
|
|
50
|
-
res.status(500).json({ error: error.message });
|
|
87
|
+
res.status(500).json({ success: false, error: error.message });
|
|
51
88
|
}
|
|
52
89
|
});
|
|
90
|
+
|
|
53
91
|
app.get('/api/status_porcelain', async (req, res) => {
|
|
54
92
|
try {
|
|
55
93
|
const { stdout } = await execGitCommand('git status --porcelain --untracked-files=all');
|
|
@@ -59,42 +97,190 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
59
97
|
}
|
|
60
98
|
});
|
|
61
99
|
|
|
62
|
-
//
|
|
100
|
+
// 获取当前分支的优化函数
|
|
101
|
+
async function getCurrentBranchOptimized(forceRefresh = false) {
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
|
|
104
|
+
// 如果不是强制刷新且缓存有效,使用缓存
|
|
105
|
+
if (!forceRefresh &&
|
|
106
|
+
currentBranchCache.branchName &&
|
|
107
|
+
(now - currentBranchCache.lastUpdate) < currentBranchCache.cacheTimeout) {
|
|
108
|
+
console.log(`使用缓存的分支名: ${currentBranchCache.branchName}`);
|
|
109
|
+
return currentBranchCache.branchName;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 缓存失效或强制刷新,重新获取
|
|
113
|
+
console.log('重新获取当前分支名...');
|
|
114
|
+
const { stdout } = await execGitCommand('git symbolic-ref --short HEAD');
|
|
115
|
+
const branchName = stdout.trim();
|
|
116
|
+
|
|
117
|
+
// 更新缓存
|
|
118
|
+
currentBranchCache = {
|
|
119
|
+
branchName,
|
|
120
|
+
lastUpdate: now,
|
|
121
|
+
cacheTimeout: 300000 // 5分钟缓存
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return branchName;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 获取上游分支的优化函数
|
|
128
|
+
async function getUpstreamBranchOptimized(forceRefresh = false) {
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
|
|
131
|
+
// 如果不是强制刷新且缓存有效,使用缓存
|
|
132
|
+
if (!forceRefresh &&
|
|
133
|
+
upstreamBranchCache.upstreamBranch !== null &&
|
|
134
|
+
(now - upstreamBranchCache.lastUpdate) < upstreamBranchCache.cacheTimeout) {
|
|
135
|
+
console.log(`使用缓存的上游分支: ${upstreamBranchCache.upstreamBranch}`);
|
|
136
|
+
return upstreamBranchCache.upstreamBranch;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 缓存失效或强制刷新,重新获取
|
|
140
|
+
console.log('重新获取上游分支...');
|
|
141
|
+
const { stdout: upstreamOutput } = await execGitCommand('git rev-parse --abbrev-ref --symbolic-full-name @{u}', { ignoreError: true });
|
|
142
|
+
const upstreamBranch = upstreamOutput.trim() || null;
|
|
143
|
+
|
|
144
|
+
// 更新缓存
|
|
145
|
+
upstreamBranchCache = {
|
|
146
|
+
upstreamBranch,
|
|
147
|
+
lastUpdate: now,
|
|
148
|
+
cacheTimeout: 300000 // 5分钟缓存
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return upstreamBranch;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 清除分支缓存的函数(在分支切换时调用)
|
|
155
|
+
function clearBranchCache() {
|
|
156
|
+
console.log('清除分支缓存');
|
|
157
|
+
currentBranchCache = {
|
|
158
|
+
branchName: null,
|
|
159
|
+
lastUpdate: 0,
|
|
160
|
+
cacheTimeout: 300000
|
|
161
|
+
};
|
|
162
|
+
// 清除上游分支缓存
|
|
163
|
+
upstreamBranchCache = {
|
|
164
|
+
upstreamBranch: null,
|
|
165
|
+
lastUpdate: 0,
|
|
166
|
+
cacheTimeout: 300000
|
|
167
|
+
};
|
|
168
|
+
// 同时清除分支状态缓存
|
|
169
|
+
branchStatusCache = {
|
|
170
|
+
currentBranch: null,
|
|
171
|
+
upstreamBranch: null,
|
|
172
|
+
lastUpdate: 0,
|
|
173
|
+
cacheTimeout: 5000
|
|
174
|
+
};
|
|
175
|
+
// 清除推送状态标记
|
|
176
|
+
recentPushStatus = {
|
|
177
|
+
justPushed: false,
|
|
178
|
+
pushTime: 0,
|
|
179
|
+
validDuration: 10000
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 获取当前分支 - 使用缓存优化
|
|
63
184
|
app.get('/api/branch', async (req, res) => {
|
|
64
185
|
try {
|
|
65
|
-
const
|
|
66
|
-
|
|
186
|
+
const forceRefresh = req.query.force === 'true';
|
|
187
|
+
const branch = await getCurrentBranchOptimized(forceRefresh);
|
|
188
|
+
res.json({ branch });
|
|
67
189
|
} catch (error) {
|
|
68
190
|
res.status(500).json({ error: error.message });
|
|
69
191
|
}
|
|
70
192
|
});
|
|
71
193
|
|
|
72
|
-
//
|
|
194
|
+
// 获取分支与远程的差异状态(领先/落后提交数)- 优化版本
|
|
73
195
|
app.get('/api/branch-status', async (req, res) => {
|
|
74
196
|
try {
|
|
75
197
|
// 检查当前目录是否是Git仓库
|
|
76
198
|
if (!isGitRepo) {
|
|
77
199
|
return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
|
|
78
200
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
201
|
+
|
|
202
|
+
const now = Date.now();
|
|
203
|
+
const forceRefresh = req.query.force === 'true';
|
|
204
|
+
const refreshCountOnly = req.query.countOnly === 'true'; // 新增:只刷新计数
|
|
205
|
+
|
|
206
|
+
// 检查是否刚刚推送过,如果是则直接返回同步状态
|
|
207
|
+
if (recentPushStatus.justPushed &&
|
|
208
|
+
(now - recentPushStatus.pushTime) < recentPushStatus.validDuration) {
|
|
209
|
+
console.log('检测到最近推送过,直接返回同步状态');
|
|
210
|
+
return res.json({
|
|
211
|
+
hasUpstream: true,
|
|
212
|
+
upstreamBranch: branchStatusCache.upstreamBranch || 'origin/main',
|
|
213
|
+
ahead: 0,
|
|
214
|
+
behind: 0
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 检查分支信息缓存是否有效
|
|
219
|
+
const branchInfoCacheValid = branchStatusCache.currentBranch &&
|
|
220
|
+
branchStatusCache.upstreamBranch &&
|
|
221
|
+
(now - branchStatusCache.lastUpdate) < branchStatusCache.cacheTimeout;
|
|
222
|
+
|
|
223
|
+
// 如果只需要刷新计数,或者缓存有效且不是强制刷新
|
|
224
|
+
if ((refreshCountOnly && branchInfoCacheValid) || (!forceRefresh && branchInfoCacheValid)) {
|
|
225
|
+
|
|
226
|
+
// 使用缓存的分支信息,只重新计算领先/落后状态
|
|
227
|
+
const { stdout: aheadBehindOutput } = await execGitCommand(
|
|
228
|
+
`git rev-list --left-right --count ${branchStatusCache.currentBranch}...${branchStatusCache.upstreamBranch}`
|
|
229
|
+
);
|
|
230
|
+
const [ahead, behind] = aheadBehindOutput.trim().split('\t').map(Number);
|
|
231
|
+
|
|
232
|
+
console.log(`使用缓存的分支信息: ${branchStatusCache.currentBranch} -> ${branchStatusCache.upstreamBranch} (${refreshCountOnly ? '只刷新计数' : '缓存有效'})`);
|
|
233
|
+
|
|
234
|
+
return res.json({
|
|
235
|
+
hasUpstream: true,
|
|
236
|
+
upstreamBranch: branchStatusCache.upstreamBranch,
|
|
237
|
+
ahead,
|
|
238
|
+
behind
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 缓存失效或强制刷新,重新获取分支信息
|
|
243
|
+
console.log('重新获取分支信息...');
|
|
244
|
+
|
|
245
|
+
// 分支名缓存和分支状态缓存独立工作
|
|
246
|
+
// 只有在分支状态强制刷新且分支名缓存也失效时,才强制刷新分支名
|
|
247
|
+
const shouldForceRefreshBranch = forceRefresh &&
|
|
248
|
+
(!currentBranchCache.branchName ||
|
|
249
|
+
(Date.now() - currentBranchCache.lastUpdate) >= currentBranchCache.cacheTimeout);
|
|
250
|
+
|
|
251
|
+
const currentBranch = await getCurrentBranchOptimized(shouldForceRefreshBranch);
|
|
252
|
+
|
|
253
|
+
// 使用优化后的上游分支获取函数
|
|
254
|
+
// 只有在分支状态强制刷新且上游分支缓存也失效时,才强制刷新上游分支
|
|
255
|
+
const shouldForceRefreshUpstream = forceRefresh &&
|
|
256
|
+
(upstreamBranchCache.upstreamBranch === null ||
|
|
257
|
+
(Date.now() - upstreamBranchCache.lastUpdate) >= upstreamBranchCache.cacheTimeout);
|
|
258
|
+
|
|
259
|
+
const upstreamBranch = await getUpstreamBranchOptimized(shouldForceRefreshUpstream);
|
|
260
|
+
|
|
261
|
+
if (!upstreamBranch) {
|
|
262
|
+
// 没有上游分支,清空缓存
|
|
263
|
+
branchStatusCache = {
|
|
264
|
+
currentBranch: null,
|
|
265
|
+
upstreamBranch: null,
|
|
266
|
+
lastUpdate: 0,
|
|
267
|
+
cacheTimeout: 5000
|
|
268
|
+
};
|
|
89
269
|
return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
|
|
90
270
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
271
|
+
|
|
272
|
+
// 更新缓存
|
|
273
|
+
branchStatusCache = {
|
|
274
|
+
currentBranch,
|
|
275
|
+
upstreamBranch,
|
|
276
|
+
lastUpdate: now,
|
|
277
|
+
cacheTimeout: 5000
|
|
278
|
+
};
|
|
279
|
+
|
|
94
280
|
// 获取领先/落后提交数
|
|
95
281
|
const { stdout: aheadBehindOutput } = await execGitCommand(`git rev-list --left-right --count ${currentBranch}...${upstreamBranch}`);
|
|
96
282
|
const [ahead, behind] = aheadBehindOutput.trim().split('\t').map(Number);
|
|
97
|
-
|
|
283
|
+
|
|
98
284
|
res.json({
|
|
99
285
|
hasUpstream: true,
|
|
100
286
|
upstreamBranch,
|
|
@@ -167,7 +353,10 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
167
353
|
|
|
168
354
|
// 切换到新创建的分支
|
|
169
355
|
await execGitCommand(`git checkout ${newBranchName}`);
|
|
170
|
-
|
|
356
|
+
|
|
357
|
+
// 清除分支缓存,因为分支已切换
|
|
358
|
+
clearBranchCache();
|
|
359
|
+
|
|
171
360
|
res.json({ success: true, branch: newBranchName });
|
|
172
361
|
} catch (error) {
|
|
173
362
|
console.error('创建分支失败:', error);
|
|
@@ -185,7 +374,10 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
185
374
|
|
|
186
375
|
// 执行分支切换
|
|
187
376
|
await execGitCommand(`git checkout ${branch}`);
|
|
188
|
-
|
|
377
|
+
|
|
378
|
+
// 清除分支缓存,因为分支已切换
|
|
379
|
+
clearBranchCache();
|
|
380
|
+
|
|
189
381
|
res.json({ success: true });
|
|
190
382
|
} catch (error) {
|
|
191
383
|
console.error('切换分支失败:', error);
|
|
@@ -819,6 +1011,15 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
819
1011
|
app.post('/api/push', async (req, res) => {
|
|
820
1012
|
try {
|
|
821
1013
|
const { stdout } = await execGitCommand('git push');
|
|
1014
|
+
|
|
1015
|
+
// 推送成功后,设置推送状态标记
|
|
1016
|
+
recentPushStatus = {
|
|
1017
|
+
justPushed: true,
|
|
1018
|
+
pushTime: Date.now(),
|
|
1019
|
+
validDuration: 10000 // 10秒内认为分支状态是同步的
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
console.log('推送成功,已设置推送状态标记');
|
|
822
1023
|
res.json({ success: true, message: stdout });
|
|
823
1024
|
} catch (error) {
|
|
824
1025
|
res.status(500).json({ success: false, error: error.message });
|
|
@@ -865,7 +1066,7 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
865
1066
|
try {
|
|
866
1067
|
// 获取分页参数
|
|
867
1068
|
const page = parseInt(req.query.page) || 1;
|
|
868
|
-
const limit = parseInt(req.query.limit) ||
|
|
1069
|
+
const limit = parseInt(req.query.limit) || 20;
|
|
869
1070
|
const skip = (page - 1) * limit;
|
|
870
1071
|
|
|
871
1072
|
// 获取筛选参数
|
|
@@ -939,40 +1140,16 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
939
1140
|
formatString = '%H%x1E%an%x1E%ae%x1E%ad%x1E%B%x1E%D%x1E%P';
|
|
940
1141
|
}
|
|
941
1142
|
|
|
942
|
-
console.log(`执行Git命令: git log --all --pretty=format:"${formatString}" --date=
|
|
943
|
-
|
|
1143
|
+
console.log(`执行Git命令: git log --all --pretty=format:"${formatString}" --date=format:"%Y-%m-%d %H:%M" ${options}`);
|
|
1144
|
+
|
|
944
1145
|
// 使用 git log 命令获取提交历史
|
|
945
1146
|
let { stdout: logOutput } = await execGitCommand(
|
|
946
|
-
`git log --all --pretty=format:"${formatString}" --date=
|
|
1147
|
+
`git log --all --pretty=format:"${formatString}" --date=format:"%Y-%m-%d %H:%M" ${options}`
|
|
947
1148
|
);
|
|
948
1149
|
|
|
949
|
-
//
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
// 构建计数命令,包含相同的筛选条件
|
|
953
|
-
// 对于作者筛选,使用 rev-list 并手动计数,避免 --count 与复杂作者筛选结合可能的问题
|
|
954
|
-
if (author.length > 1) {
|
|
955
|
-
// 多作者情况,使用 wc -l 手动计数
|
|
956
|
-
const countCommand = `git rev-list --all ${commandOptions.join(' ')}`;
|
|
957
|
-
console.log(`执行计数命令(多作者): ${countCommand}`);
|
|
958
|
-
|
|
959
|
-
const { stdout: countOutput } = await execGitCommand(countCommand);
|
|
960
|
-
// 计算行数作为提交数
|
|
961
|
-
totalCommits = countOutput.trim().split('\n').filter(line => line.trim() !== '').length;
|
|
962
|
-
} else {
|
|
963
|
-
// 单作者或无作者筛选,可以直接使用 --count
|
|
964
|
-
const countCommand = `git rev-list --all --count ${commandOptions.join(' ')}`;
|
|
965
|
-
console.log(`执行计数命令(单作者): ${countCommand}`);
|
|
966
|
-
|
|
967
|
-
const { stdout: countOutput } = await execGitCommand(countCommand);
|
|
968
|
-
totalCommits = parseInt(countOutput.trim());
|
|
969
|
-
}
|
|
970
|
-
} catch (error) {
|
|
971
|
-
console.error('获取提交总数失败:', error);
|
|
972
|
-
totalCommits = 0;
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
processAndSendLogOutput(res, logOutput, totalCommits, page, limit, withParents);
|
|
1150
|
+
// 分页加载优化:不需要获取总数,通过实际返回的数据量判断是否还有更多
|
|
1151
|
+
// 这里直接处理已获取的数据,通过返回数据量判断是否还有更多
|
|
1152
|
+
processAndSendLogOutput(res, logOutput, page, limit, withParents);
|
|
976
1153
|
} catch (error) {
|
|
977
1154
|
console.error('获取Git日志失败:', error);
|
|
978
1155
|
res.status(500).json({ error: '获取日志失败: ' + error.message });
|
|
@@ -1038,35 +1215,22 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1038
1215
|
}
|
|
1039
1216
|
|
|
1040
1217
|
// 构建执行的命令
|
|
1041
|
-
const command = `git log ${formattedBranchRefs} --pretty=format:"${formatString}" --date=
|
|
1218
|
+
const command = `git log ${formattedBranchRefs} --pretty=format:"${formatString}" --date=format:"%Y-%m-%d %H:%M" ${options}`;
|
|
1042
1219
|
console.log(`执行Git命令(带分支引用): ${command}`);
|
|
1043
1220
|
|
|
1044
1221
|
// 执行命令
|
|
1045
1222
|
const { stdout: logOutput } = await execGitCommand(command);
|
|
1046
1223
|
|
|
1047
|
-
//
|
|
1048
|
-
|
|
1049
|
-
try {
|
|
1050
|
-
// 构建计数命令
|
|
1051
|
-
const countCommand = `git rev-list ${formattedBranchRefs} --count ${commandOptions.join(' ')}`;
|
|
1052
|
-
console.log(`执行计数命令(分支): ${countCommand}`);
|
|
1053
|
-
|
|
1054
|
-
const { stdout: countOutput } = await execGitCommand(countCommand);
|
|
1055
|
-
totalCommits = parseInt(countOutput.trim());
|
|
1056
|
-
} catch (error) {
|
|
1057
|
-
console.error('获取提交总数失败:', error);
|
|
1058
|
-
totalCommits = 0;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
processAndSendLogOutput(res, logOutput, totalCommits, skip / limit + 1, limit, withParents);
|
|
1224
|
+
// 分页加载优化:不需要获取总数,通过实际返回的数据量判断是否还有更多
|
|
1225
|
+
processAndSendLogOutput(res, logOutput, skip / limit + 1, limit, withParents);
|
|
1062
1226
|
} catch (error) {
|
|
1063
1227
|
console.error('执行Git日志命令失败:', error);
|
|
1064
1228
|
res.status(500).json({ error: '获取日志失败: ' + error.message });
|
|
1065
1229
|
}
|
|
1066
1230
|
}
|
|
1067
1231
|
|
|
1068
|
-
//
|
|
1069
|
-
function processAndSendLogOutput(res, logOutput,
|
|
1232
|
+
// 抽取处理输出并发送响应的函数(优化版本:不再依赖总数计算)
|
|
1233
|
+
function processAndSendLogOutput(res, logOutput, page, limit, withParents = false) {
|
|
1070
1234
|
// 替换提交记录之间的换行符 - 使用 ASCII 控制字符 \x1E 匹配
|
|
1071
1235
|
logOutput = logOutput.replace(/\n(?=[a-f0-9]{40}\x1E)/g, "<<<RECORD_SEPARATOR>>>");
|
|
1072
1236
|
|
|
@@ -1097,15 +1261,17 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1097
1261
|
return null;
|
|
1098
1262
|
}).filter(item => item !== null);
|
|
1099
1263
|
|
|
1100
|
-
//
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1264
|
+
// 优化:通过返回的数据量判断是否有更多数据,而不依赖总数计算
|
|
1265
|
+
// 如果返回的数据量等于请求的limit,说明可能还有更多数据
|
|
1266
|
+
// 如果返回的数据量小于limit,说明已经到底了
|
|
1267
|
+
const hasMore = data.length === limit;
|
|
1268
|
+
|
|
1269
|
+
console.log(`分页查询 - 页码: ${page}, 每页数量: ${limit}, 返回数量: ${data.length}, 是否有更多: ${hasMore} (优化版本,不计算总数)`);
|
|
1270
|
+
|
|
1105
1271
|
// 返回提交历史数据,包括是否有更多数据的标志
|
|
1106
1272
|
res.json({
|
|
1107
1273
|
data: data,
|
|
1108
|
-
total:
|
|
1274
|
+
total: -1, // 不再提供总数,前端不应依赖此字段
|
|
1109
1275
|
page: page,
|
|
1110
1276
|
limit: limit,
|
|
1111
1277
|
hasMore: hasMore
|
|
@@ -1715,6 +1881,10 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1715
1881
|
// 当客户端连接时,立即发送一次Git状态
|
|
1716
1882
|
getAndBroadcastStatus();
|
|
1717
1883
|
|
|
1884
|
+
// 发送当前命令历史
|
|
1885
|
+
const history = getCommandHistory();
|
|
1886
|
+
socket.emit('initial_command_history', { history });
|
|
1887
|
+
|
|
1718
1888
|
// 客户端可以请求开始/停止监控
|
|
1719
1889
|
socket.on('start_monitoring', () => {
|
|
1720
1890
|
if (!watcher) {
|
|
@@ -1731,6 +1901,18 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1731
1901
|
}
|
|
1732
1902
|
});
|
|
1733
1903
|
|
|
1904
|
+
// 请求完整命令历史
|
|
1905
|
+
socket.on('request_full_history', () => {
|
|
1906
|
+
const fullHistory = getCommandHistory();
|
|
1907
|
+
socket.emit('full_command_history', { history: fullHistory });
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
// 清空命令历史
|
|
1911
|
+
socket.on('clear_command_history', () => {
|
|
1912
|
+
const result = clearCommandHistory();
|
|
1913
|
+
socket.emit('command_history_cleared', { success: result });
|
|
1914
|
+
});
|
|
1915
|
+
|
|
1734
1916
|
// 客户端断开连接
|
|
1735
1917
|
socket.on('disconnect', () => {
|
|
1736
1918
|
console.log('客户端已断开连接:', socket.id);
|
|
@@ -1790,34 +1972,29 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1790
1972
|
}
|
|
1791
1973
|
}
|
|
1792
1974
|
|
|
1793
|
-
// 获取并广播Git状态
|
|
1975
|
+
// 获取并广播Git状态 (优化版本 - 只获取porcelain格式)
|
|
1794
1976
|
async function getAndBroadcastStatus() {
|
|
1795
1977
|
try {
|
|
1796
1978
|
// 如果不是Git仓库,发送特殊状态
|
|
1797
1979
|
if (!isGitRepo) {
|
|
1798
|
-
io.emit('git_status_update', {
|
|
1980
|
+
io.emit('git_status_update', {
|
|
1799
1981
|
isGitRepo: false,
|
|
1800
|
-
status: '当前目录不是Git仓库',
|
|
1801
1982
|
porcelain: '',
|
|
1802
1983
|
timestamp: new Date().toISOString()
|
|
1803
1984
|
});
|
|
1804
1985
|
return;
|
|
1805
1986
|
}
|
|
1806
|
-
|
|
1807
|
-
//
|
|
1808
|
-
const { stdout: statusOutput } = await execGitCommand('git status');
|
|
1809
|
-
|
|
1810
|
-
// 获取porcelain格式状态
|
|
1987
|
+
|
|
1988
|
+
// 只获取porcelain格式状态,不再获取完整的git status
|
|
1811
1989
|
const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain --untracked-files=all');
|
|
1812
|
-
|
|
1990
|
+
|
|
1813
1991
|
// 广播到所有连接的客户端
|
|
1814
|
-
io.emit('git_status_update', {
|
|
1992
|
+
io.emit('git_status_update', {
|
|
1815
1993
|
isGitRepo: true,
|
|
1816
|
-
status: statusOutput,
|
|
1817
1994
|
porcelain: porcelainOutput,
|
|
1818
1995
|
timestamp: new Date().toISOString()
|
|
1819
1996
|
});
|
|
1820
|
-
|
|
1997
|
+
|
|
1821
1998
|
console.log('已广播Git状态更新');
|
|
1822
1999
|
} catch (error) {
|
|
1823
2000
|
console.error('获取或广播Git状态失败:', error);
|
|
@@ -1851,70 +2028,138 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
1851
2028
|
// 启动服务器
|
|
1852
2029
|
const PORT = 3000;
|
|
1853
2030
|
|
|
1854
|
-
//
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
2031
|
+
// 创建一个函数来保存端口号到文件和环境变量
|
|
2032
|
+
// 使用闭包保存端口状态,防止多次写入相同端口
|
|
2033
|
+
const savePortToFile = (function() {
|
|
2034
|
+
let savedPort = null;
|
|
2035
|
+
|
|
2036
|
+
return async function(port) {
|
|
2037
|
+
try {
|
|
2038
|
+
// 只有当savePort为true且端口没有保存过时才保存端口号
|
|
2039
|
+
if (savePort && savedPort !== port) {
|
|
2040
|
+
savedPort = port;
|
|
2041
|
+
|
|
2042
|
+
// 保存到项目根目录的.port文件
|
|
2043
|
+
const portFilePath = path.join(process.cwd(), '.port');
|
|
2044
|
+
await fs.writeFile(portFilePath, port.toString(), 'utf8');
|
|
2045
|
+
console.log(`端口号 ${port} 已保存到 ${portFilePath}`);
|
|
2046
|
+
|
|
2047
|
+
// 同时保存到前端Vite项目的.env.local文件
|
|
2048
|
+
try {
|
|
2049
|
+
const clientPath = path.join(process.cwd(), 'src', 'ui', 'client');
|
|
2050
|
+
const envPath = path.join(clientPath, '.env.local');
|
|
2051
|
+
|
|
2052
|
+
// 检查目录是否存在
|
|
2053
|
+
await fs.access(clientPath).catch(() => {
|
|
2054
|
+
console.log(`客户端目录 ${clientPath} 不存在,跳过环境变量设置`);
|
|
2055
|
+
return Promise.reject(new Error('Client directory not found'));
|
|
2056
|
+
});
|
|
2057
|
+
|
|
2058
|
+
// 写入环境变量
|
|
2059
|
+
await fs.writeFile(envPath, `VITE_BACKEND_PORT=${port}\n`, 'utf8');
|
|
2060
|
+
console.log(`端口号环境变量已保存到 ${envPath}`);
|
|
2061
|
+
} catch (envError) {
|
|
2062
|
+
console.error('保存端口号到环境变量失败,但不影响主要功能:', envError);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
} catch (error) {
|
|
2066
|
+
console.error('保存端口号到文件失败:', error);
|
|
1863
2067
|
}
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
2068
|
+
};
|
|
2069
|
+
})();
|
|
1868
2070
|
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
// 启动文件监控
|
|
1877
|
-
initFileSystemWatcher();
|
|
1878
|
-
} else {
|
|
1879
|
-
console.log(chalk.yellow(` 当前目录不是Git仓库,文件监控未启动`));
|
|
1880
|
-
}
|
|
1881
|
-
console.log(chalk.green('======================================'));
|
|
1882
|
-
|
|
1883
|
-
// 保存端口号到文件
|
|
1884
|
-
savePortToFile(PORT);
|
|
2071
|
+
// 尝试在可用端口上启动服务器
|
|
2072
|
+
await startServerOnAvailablePort(PORT);
|
|
2073
|
+
|
|
2074
|
+
// 启动服务器函数
|
|
2075
|
+
async function startServerOnAvailablePort(startPort) {
|
|
2076
|
+
let currentPort = startPort;
|
|
2077
|
+
const maxPort = startPort + 100; // 最多尝试100个端口
|
|
1885
2078
|
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
2079
|
+
while (currentPort < maxPort) {
|
|
2080
|
+
try {
|
|
2081
|
+
// 等待1秒,避免快速尝试多个端口
|
|
2082
|
+
if (currentPort > startPort) {
|
|
2083
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
2084
|
+
console.log(`尝试端口 ${currentPort}...`);
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// 尝试在当前端口启动服务器
|
|
2088
|
+
await new Promise((resolve, reject) => {
|
|
2089
|
+
// 仅监听一次error事件
|
|
2090
|
+
const errorHandler = (err) => {
|
|
2091
|
+
httpServer.removeListener('error', errorHandler);
|
|
2092
|
+
reject(err);
|
|
2093
|
+
};
|
|
2094
|
+
|
|
2095
|
+
// 使用变量标记回调是否已执行,防止多次触发
|
|
2096
|
+
let callbackExecuted = false;
|
|
2097
|
+
|
|
2098
|
+
httpServer.once('error', errorHandler);
|
|
2099
|
+
|
|
2100
|
+
httpServer.listen(currentPort, () => {
|
|
2101
|
+
// 确保回调只执行一次
|
|
2102
|
+
if (callbackExecuted) return;
|
|
2103
|
+
callbackExecuted = true;
|
|
2104
|
+
|
|
2105
|
+
// 成功监听,移除错误处理器
|
|
2106
|
+
httpServer.removeListener('error', errorHandler);
|
|
2107
|
+
|
|
2108
|
+
// 输出服务器信息
|
|
2109
|
+
console.log(chalk.green('======================================'));
|
|
2110
|
+
console.log(chalk.green(` Zen GitSync 服务器已启动`));
|
|
2111
|
+
console.log(chalk.green(` 访问地址: http://localhost:${currentPort}`));
|
|
2112
|
+
console.log(chalk.green(` 启动时间: ${new Date().toLocaleString()}`));
|
|
2113
|
+
|
|
2114
|
+
if (isGitRepo) {
|
|
2115
|
+
console.log(chalk.green(` 当前目录是Git仓库,文件监控已启动`));
|
|
2116
|
+
// 启动文件监控
|
|
2117
|
+
initFileSystemWatcher();
|
|
2118
|
+
} else {
|
|
2119
|
+
console.log(chalk.yellow(` 当前目录不是Git仓库,文件监控未启动`));
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
console.log(chalk.green('======================================'));
|
|
2123
|
+
|
|
2124
|
+
// 保存端口号到文件
|
|
2125
|
+
savePortToFile(currentPort);
|
|
2126
|
+
|
|
2127
|
+
// 只有在noOpen为false时才打开浏览器
|
|
2128
|
+
if (!noOpen) {
|
|
2129
|
+
open(`http://localhost:${currentPort}`);
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
resolve();
|
|
1909
2133
|
});
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2134
|
+
});
|
|
2135
|
+
|
|
2136
|
+
// 如果成功启动,退出循环
|
|
2137
|
+
return;
|
|
2138
|
+
} catch (err) {
|
|
2139
|
+
// 处理端口被占用的情况
|
|
2140
|
+
if (err.code === 'EADDRINUSE') {
|
|
2141
|
+
console.log(`端口 ${currentPort} 被占用,尝试下一个端口...`);
|
|
2142
|
+
currentPort++;
|
|
2143
|
+
} else {
|
|
2144
|
+
// 其他错误,直接抛出
|
|
2145
|
+
console.error('启动服务器失败:', err);
|
|
2146
|
+
process.exit(1);
|
|
1913
2147
|
}
|
|
1914
2148
|
}
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
|
-
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
// 如果尝试了所有端口都失败
|
|
2152
|
+
console.error(`无法找到可用端口 (尝试范围: ${startPort}-${maxPort-1})`);
|
|
2153
|
+
process.exit(1);
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
// 添加命令历史的清空API
|
|
2157
|
+
app.post('/api/clear-command-history', async (req, res) => {
|
|
2158
|
+
try {
|
|
2159
|
+
const result = clearCommandHistory();
|
|
2160
|
+
res.json({ success: result });
|
|
2161
|
+
} catch (error) {
|
|
2162
|
+
res.status(500).json({ success: false, error: error.message });
|
|
1918
2163
|
}
|
|
1919
2164
|
});
|
|
1920
2165
|
}
|