zen-gitsync 2.1.3 → 2.1.8
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/LICENSE +21 -21
- package/README.md +96 -96
- package/index.js +2 -2
- package/package.json +69 -66
- package/src/config.js +51 -51
- package/src/gitCommit.js +261 -261
- package/src/ui/public/assets/index-B7Bmz9T9.js +22 -0
- package/src/ui/public/assets/index-DNVXEFTm.css +1 -0
- package/src/ui/public/assets/{vendor-Dy1zosHw.js → vendor-Bm8yNvvz.js} +1 -1
- package/src/ui/public/favicon.svg +26 -26
- package/src/ui/public/index.html +3 -3
- package/src/ui/public/logo.svg +26 -26
- package/src/ui/server/index.js +231 -60
- package/src/ui/client/README.md +0 -5
- package/src/ui/client/auto-imports.d.ts +0 -10
- package/src/ui/client/components.d.ts +0 -33
- package/src/ui/client/index.html +0 -13
- package/src/ui/client/package.json +0 -28
- package/src/ui/client/public/favicon.svg +0 -27
- package/src/ui/client/public/logo.svg +0 -27
- package/src/ui/client/public/vite.svg +0 -1
- package/src/ui/client/src/App.vue +0 -984
- package/src/ui/client/src/assets/logo.svg +0 -27
- package/src/ui/client/src/components/CommitForm.vue +0 -2158
- package/src/ui/client/src/components/GitStatus.vue +0 -1621
- package/src/ui/client/src/components/LogList.vue +0 -1937
- package/src/ui/client/src/main.ts +0 -7
- package/src/ui/client/src/stores/configStore.ts +0 -212
- package/src/ui/client/src/stores/gitLogStore.ts +0 -790
- package/src/ui/client/src/stores/gitStore.ts +0 -443
- package/src/ui/client/src/vite-env.d.ts +0 -1
- package/src/ui/client/stats.html +0 -4949
- package/src/ui/client/tsconfig.app.json +0 -14
- package/src/ui/client/tsconfig.json +0 -7
- package/src/ui/client/tsconfig.node.json +0 -24
- package/src/ui/client/vite.config.ts +0 -50
- package/src/ui/public/assets/index-CekdGwXY.js +0 -20
- package/src/ui/public/assets/index-SwSkrgGk.css +0 -1
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<!-- 主分支 -->
|
|
3
|
-
<line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
|
|
4
|
-
|
|
5
|
-
<!-- 特性分支 -->
|
|
6
|
-
<path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
7
|
-
|
|
8
|
-
<!-- 开发分支 -->
|
|
9
|
-
<path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
10
|
-
|
|
11
|
-
<!-- 修复分支 -->
|
|
12
|
-
<path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
13
|
-
|
|
14
|
-
<!-- 合并点 -->
|
|
15
|
-
<path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
16
|
-
|
|
17
|
-
<!-- 节点 -->
|
|
18
|
-
<circle cx="40" cy="40" r="10" fill="#34495e" />
|
|
19
|
-
<circle cx="40" cy="70" r="10" fill="#34495e" />
|
|
20
|
-
<circle cx="40" cy="100" r="10" fill="#34495e" />
|
|
21
|
-
<circle cx="40" cy="130" r="10" fill="#34495e" />
|
|
22
|
-
<circle cx="40" cy="145" r="10" fill="#34495e" />
|
|
23
|
-
<circle cx="40" cy="160" r="10" fill="#34495e" />
|
|
24
|
-
<circle cx="80" cy="130" r="10" fill="#3498db" />
|
|
25
|
-
<circle cx="100" cy="160" r="10" fill="#2ecc71" />
|
|
26
|
-
<circle cx="120" cy="160" r="10" fill="#e74c3c" />
|
|
1
|
+
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- 主分支 -->
|
|
3
|
+
<line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
|
|
4
|
+
|
|
5
|
+
<!-- 特性分支 -->
|
|
6
|
+
<path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
7
|
+
|
|
8
|
+
<!-- 开发分支 -->
|
|
9
|
+
<path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
10
|
+
|
|
11
|
+
<!-- 修复分支 -->
|
|
12
|
+
<path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
13
|
+
|
|
14
|
+
<!-- 合并点 -->
|
|
15
|
+
<path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
16
|
+
|
|
17
|
+
<!-- 节点 -->
|
|
18
|
+
<circle cx="40" cy="40" r="10" fill="#34495e" />
|
|
19
|
+
<circle cx="40" cy="70" r="10" fill="#34495e" />
|
|
20
|
+
<circle cx="40" cy="100" r="10" fill="#34495e" />
|
|
21
|
+
<circle cx="40" cy="130" r="10" fill="#34495e" />
|
|
22
|
+
<circle cx="40" cy="145" r="10" fill="#34495e" />
|
|
23
|
+
<circle cx="40" cy="160" r="10" fill="#34495e" />
|
|
24
|
+
<circle cx="80" cy="130" r="10" fill="#3498db" />
|
|
25
|
+
<circle cx="100" cy="160" r="10" fill="#2ecc71" />
|
|
26
|
+
<circle cx="120" cy="160" r="10" fill="#e74c3c" />
|
|
27
27
|
</svg>
|
package/src/ui/public/index.html
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Zen-GitSync - Git同步工具</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-B7Bmz9T9.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-Bm8yNvvz.js">
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/vendor-Dp0FkvMe.css">
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DNVXEFTm.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="app"></div>
|
package/src/ui/public/logo.svg
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<!-- 主分支 -->
|
|
3
|
-
<line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
|
|
4
|
-
|
|
5
|
-
<!-- 特性分支 -->
|
|
6
|
-
<path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
7
|
-
|
|
8
|
-
<!-- 开发分支 -->
|
|
9
|
-
<path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
10
|
-
|
|
11
|
-
<!-- 修复分支 -->
|
|
12
|
-
<path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
13
|
-
|
|
14
|
-
<!-- 合并点 -->
|
|
15
|
-
<path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
16
|
-
|
|
17
|
-
<!-- 节点 -->
|
|
18
|
-
<circle cx="40" cy="40" r="10" fill="#34495e" />
|
|
19
|
-
<circle cx="40" cy="70" r="10" fill="#34495e" />
|
|
20
|
-
<circle cx="40" cy="100" r="10" fill="#34495e" />
|
|
21
|
-
<circle cx="40" cy="130" r="10" fill="#34495e" />
|
|
22
|
-
<circle cx="40" cy="145" r="10" fill="#34495e" />
|
|
23
|
-
<circle cx="40" cy="160" r="10" fill="#34495e" />
|
|
24
|
-
<circle cx="80" cy="130" r="10" fill="#3498db" />
|
|
25
|
-
<circle cx="100" cy="160" r="10" fill="#2ecc71" />
|
|
26
|
-
<circle cx="120" cy="160" r="10" fill="#e74c3c" />
|
|
1
|
+
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<!-- 主分支 -->
|
|
3
|
+
<line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
|
|
4
|
+
|
|
5
|
+
<!-- 特性分支 -->
|
|
6
|
+
<path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
7
|
+
|
|
8
|
+
<!-- 开发分支 -->
|
|
9
|
+
<path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
10
|
+
|
|
11
|
+
<!-- 修复分支 -->
|
|
12
|
+
<path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
13
|
+
|
|
14
|
+
<!-- 合并点 -->
|
|
15
|
+
<path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
16
|
+
|
|
17
|
+
<!-- 节点 -->
|
|
18
|
+
<circle cx="40" cy="40" r="10" fill="#34495e" />
|
|
19
|
+
<circle cx="40" cy="70" r="10" fill="#34495e" />
|
|
20
|
+
<circle cx="40" cy="100" r="10" fill="#34495e" />
|
|
21
|
+
<circle cx="40" cy="130" r="10" fill="#34495e" />
|
|
22
|
+
<circle cx="40" cy="145" r="10" fill="#34495e" />
|
|
23
|
+
<circle cx="40" cy="160" r="10" fill="#34495e" />
|
|
24
|
+
<circle cx="80" cy="130" r="10" fill="#3498db" />
|
|
25
|
+
<circle cx="100" cy="160" r="10" fill="#2ecc71" />
|
|
26
|
+
<circle cx="120" cy="160" r="10" fill="#e74c3c" />
|
|
27
27
|
</svg>
|
package/src/ui/server/index.js
CHANGED
|
@@ -52,7 +52,7 @@ async function startUIServer() {
|
|
|
52
52
|
});
|
|
53
53
|
app.get('/api/status_porcelain', async (req, res) => {
|
|
54
54
|
try {
|
|
55
|
-
const { stdout } = await execGitCommand('git status --porcelain');
|
|
55
|
+
const { stdout } = await execGitCommand('git status --porcelain --untracked-files=all');
|
|
56
56
|
res.json({ status: stdout });
|
|
57
57
|
} catch (error) {
|
|
58
58
|
res.status(500).json({ error: error.message });
|
|
@@ -72,67 +72,37 @@ async function startUIServer() {
|
|
|
72
72
|
// 获取分支与远程的差异状态(领先/落后提交数)
|
|
73
73
|
app.get('/api/branch-status', async (req, res) => {
|
|
74
74
|
try {
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
// 检查当前目录是否是Git仓库
|
|
76
|
+
if (!isGitRepo) {
|
|
77
|
+
return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
|
|
78
|
+
}
|
|
78
79
|
|
|
79
|
-
//
|
|
80
|
-
const { stdout:
|
|
81
|
-
const
|
|
80
|
+
// 获取当前分支
|
|
81
|
+
const { stdout: branchOutput } = await execGitCommand('git symbolic-ref --short HEAD');
|
|
82
|
+
const currentBranch = branchOutput.trim();
|
|
82
83
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
let hasUpstream = false;
|
|
84
|
+
// 获取上游分支
|
|
85
|
+
const { stdout: upstreamOutput } = await execGitCommand('git rev-parse --abbrev-ref --symbolic-full-name @{u}', { ignoreError: true });
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
hasUpstream = true;
|
|
91
|
-
} catch (error) {
|
|
92
|
-
// 没有上游分支设置,这是正常的
|
|
93
|
-
console.log(`分支 ${currentBranch} 没有设置上游分支`);
|
|
94
|
-
hasUpstream = false;
|
|
87
|
+
if (!upstreamOutput.trim()) {
|
|
88
|
+
// 没有上游分支
|
|
89
|
+
return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
|
|
95
90
|
}
|
|
96
91
|
|
|
97
|
-
|
|
98
|
-
if (!hasUpstream) {
|
|
99
|
-
return res.json({
|
|
100
|
-
branch: currentBranch,
|
|
101
|
-
remote,
|
|
102
|
-
hasUpstream: false,
|
|
103
|
-
ahead: 0,
|
|
104
|
-
behind: 0
|
|
105
|
-
});
|
|
106
|
-
}
|
|
92
|
+
const upstreamBranch = upstreamOutput.trim();
|
|
107
93
|
|
|
108
|
-
//
|
|
109
|
-
const { stdout:
|
|
110
|
-
|
|
111
|
-
// 解析结果,格式通常是 "1 2" 表示领先1个提交,落后2个提交
|
|
112
|
-
let ahead = 0;
|
|
113
|
-
let behind = 0;
|
|
114
|
-
|
|
115
|
-
if (statusCompare && statusCompare.trim()) {
|
|
116
|
-
const parts = statusCompare.trim().split(/\s+/);
|
|
117
|
-
if (parts.length >= 2) {
|
|
118
|
-
ahead = parseInt(parts[0]) || 0;
|
|
119
|
-
behind = parseInt(parts[1]) || 0;
|
|
120
|
-
} else if (parts.length === 1) {
|
|
121
|
-
// 只有一个数字的情况,通常是领先的提交数
|
|
122
|
-
ahead = parseInt(parts[0]) || 0;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
94
|
+
// 获取领先/落后提交数
|
|
95
|
+
const { stdout: aheadBehindOutput } = await execGitCommand(`git rev-list --left-right --count ${currentBranch}...${upstreamBranch}`);
|
|
96
|
+
const [ahead, behind] = aheadBehindOutput.trim().split('\t').map(Number);
|
|
125
97
|
|
|
126
98
|
res.json({
|
|
127
|
-
branch: currentBranch,
|
|
128
|
-
remote,
|
|
129
|
-
upstreamBranch,
|
|
130
99
|
hasUpstream: true,
|
|
100
|
+
upstreamBranch,
|
|
131
101
|
ahead,
|
|
132
102
|
behind
|
|
133
103
|
});
|
|
134
104
|
} catch (error) {
|
|
135
|
-
console.error(
|
|
105
|
+
console.error('获取分支状态失败:', error);
|
|
136
106
|
res.status(500).json({ error: error.message });
|
|
137
107
|
}
|
|
138
108
|
});
|
|
@@ -359,6 +329,132 @@ async function startUIServer() {
|
|
|
359
329
|
}
|
|
360
330
|
});
|
|
361
331
|
|
|
332
|
+
// POST接口版本的浏览目录功能
|
|
333
|
+
app.post('/api/browse_directory', async (req, res) => {
|
|
334
|
+
try {
|
|
335
|
+
// 获取要浏览的目录路径,如果没有提供,则使用当前目录
|
|
336
|
+
const directoryPath = req.body.currentPath || process.cwd();
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
// 读取目录内容
|
|
340
|
+
const items = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
341
|
+
|
|
342
|
+
// 分离文件夹和文件
|
|
343
|
+
const directories = [];
|
|
344
|
+
const files = [];
|
|
345
|
+
|
|
346
|
+
for (const item of items) {
|
|
347
|
+
const fullPath = path.join(directoryPath, item.name);
|
|
348
|
+
if (item.isDirectory()) {
|
|
349
|
+
directories.push({
|
|
350
|
+
name: item.name,
|
|
351
|
+
path: fullPath,
|
|
352
|
+
type: 'directory'
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// 只返回目录,不返回文件
|
|
358
|
+
directories.sort((a, b) => a.name.localeCompare(b.name));
|
|
359
|
+
|
|
360
|
+
// 获取父目录路径
|
|
361
|
+
const parentPath = path.dirname(directoryPath);
|
|
362
|
+
|
|
363
|
+
// 返回选择的目录路径
|
|
364
|
+
res.json({
|
|
365
|
+
success: true,
|
|
366
|
+
path: directoryPath,
|
|
367
|
+
parentPath: parentPath !== directoryPath ? parentPath : null,
|
|
368
|
+
items: directories
|
|
369
|
+
});
|
|
370
|
+
} catch (error) {
|
|
371
|
+
res.status(400).json({
|
|
372
|
+
success: false,
|
|
373
|
+
error: `无法读取目录 "${directoryPath}": ${error.message}`
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
} catch (error) {
|
|
377
|
+
res.status(500).json({
|
|
378
|
+
success: false,
|
|
379
|
+
error: error.message
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// 获取最近访问的目录列表
|
|
385
|
+
app.get('/api/recent_directories', async (req, res) => {
|
|
386
|
+
try {
|
|
387
|
+
// 尝试从配置中获取最近的目录
|
|
388
|
+
const recentDirs = await configManager.getRecentDirectories();
|
|
389
|
+
res.json({
|
|
390
|
+
success: true,
|
|
391
|
+
directories: recentDirs || []
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
res.status(500).json({
|
|
395
|
+
success: false,
|
|
396
|
+
error: error.message
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// 在资源管理器/访达中打开当前目录
|
|
402
|
+
app.post('/api/open_directory', async (req, res) => {
|
|
403
|
+
try {
|
|
404
|
+
// 获取要打开的目录路径,如果没有提供,则使用当前目录
|
|
405
|
+
const directoryPath = req.body.path || process.cwd();
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
// 检查目录是否存在
|
|
409
|
+
await fs.access(directoryPath);
|
|
410
|
+
|
|
411
|
+
// 使用open模块打开目录,自动处理不同操作系统
|
|
412
|
+
await open(directoryPath, { wait: false });
|
|
413
|
+
|
|
414
|
+
res.json({
|
|
415
|
+
success: true,
|
|
416
|
+
message: '已在文件管理器中打开目录'
|
|
417
|
+
});
|
|
418
|
+
} catch (error) {
|
|
419
|
+
res.status(400).json({
|
|
420
|
+
success: false,
|
|
421
|
+
error: `无法打开目录 "${directoryPath}": ${error.message}`
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
} catch (error) {
|
|
425
|
+
res.status(500).json({
|
|
426
|
+
success: false,
|
|
427
|
+
error: error.message
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// 保存最近访问的目录
|
|
433
|
+
app.post('/api/save_recent_directory', async (req, res) => {
|
|
434
|
+
try {
|
|
435
|
+
const { path } = req.body;
|
|
436
|
+
|
|
437
|
+
if (!path) {
|
|
438
|
+
return res.status(400).json({
|
|
439
|
+
success: false,
|
|
440
|
+
error: '目录路径不能为空'
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// 保存到配置
|
|
445
|
+
await configManager.saveRecentDirectory(path);
|
|
446
|
+
|
|
447
|
+
res.json({
|
|
448
|
+
success: true
|
|
449
|
+
});
|
|
450
|
+
} catch (error) {
|
|
451
|
+
res.status(500).json({
|
|
452
|
+
success: false,
|
|
453
|
+
error: error.message
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
362
458
|
// 获取配置
|
|
363
459
|
app.get('/api/config/getConfig', async (req, res) => {
|
|
364
460
|
try {
|
|
@@ -702,6 +798,7 @@ async function startUIServer() {
|
|
|
702
798
|
const dateFrom = req.query.dateFrom || '';
|
|
703
799
|
const dateTo = req.query.dateTo || '';
|
|
704
800
|
const branch = req.query.branch ? req.query.branch.split(',') : [];
|
|
801
|
+
const withParents = req.query.with_parents === 'true';
|
|
705
802
|
|
|
706
803
|
// 构建Git命令选项
|
|
707
804
|
let commandOptions = [];
|
|
@@ -716,7 +813,7 @@ async function startUIServer() {
|
|
|
716
813
|
const branchRefs = branch.map(b => b.trim()).join(' ');
|
|
717
814
|
|
|
718
815
|
// 直接将分支名作为命令参数,并确保后面添加 -- 分隔符防止歧义
|
|
719
|
-
return executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, req.query.all === 'true');
|
|
816
|
+
return executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, req.query.all === 'true', withParents);
|
|
720
817
|
}
|
|
721
818
|
|
|
722
819
|
// 如果没有指定分支,则使用--all参数
|
|
@@ -760,11 +857,17 @@ async function startUIServer() {
|
|
|
760
857
|
// 合并所有命令选项
|
|
761
858
|
const options = [...commandOptions, limitOption].filter(Boolean).join(' ');
|
|
762
859
|
|
|
763
|
-
|
|
860
|
+
// 添加父提交信息的格式
|
|
861
|
+
let formatString = '%H|%an|%ae|%ad|%B|%D';
|
|
862
|
+
if (withParents) {
|
|
863
|
+
formatString = '%H|%an|%ae|%ad|%B|%D|%P';
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
console.log(`执行Git命令: git log --all --pretty=format:"${formatString}" --date=short ${options}`);
|
|
764
867
|
|
|
765
868
|
// 使用 git log 命令获取提交历史
|
|
766
869
|
let { stdout: logOutput } = await execGitCommand(
|
|
767
|
-
`git log --all --pretty=format:"
|
|
870
|
+
`git log --all --pretty=format:"${formatString}" --date=short ${options}`
|
|
768
871
|
);
|
|
769
872
|
|
|
770
873
|
// 获取总提交数量(考虑筛选条件)
|
|
@@ -793,7 +896,7 @@ async function startUIServer() {
|
|
|
793
896
|
totalCommits = 0;
|
|
794
897
|
}
|
|
795
898
|
|
|
796
|
-
processAndSendLogOutput(res, logOutput, totalCommits, page, limit);
|
|
899
|
+
processAndSendLogOutput(res, logOutput, totalCommits, page, limit, withParents);
|
|
797
900
|
} catch (error) {
|
|
798
901
|
console.error('获取Git日志失败:', error);
|
|
799
902
|
res.status(500).json({ error: '获取日志失败: ' + error.message });
|
|
@@ -801,7 +904,7 @@ async function startUIServer() {
|
|
|
801
904
|
});
|
|
802
905
|
|
|
803
906
|
// 抽取执行Git日志命令的函数
|
|
804
|
-
async function executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, isAll) {
|
|
907
|
+
async function executeGitLogCommand(res, branchRefs, author, message, dateFrom, dateTo, limit, skip, isAll, withParents = false) {
|
|
805
908
|
try {
|
|
806
909
|
// 构建命令选项
|
|
807
910
|
const commandOptions = [];
|
|
@@ -851,8 +954,14 @@ async function startUIServer() {
|
|
|
851
954
|
})
|
|
852
955
|
.join(' ');
|
|
853
956
|
|
|
957
|
+
// 添加父提交信息的格式
|
|
958
|
+
let formatString = '%H|%an|%ae|%ad|%B|%D';
|
|
959
|
+
if (withParents) {
|
|
960
|
+
formatString = '%H|%an|%ae|%ad|%B|%D|%P';
|
|
961
|
+
}
|
|
962
|
+
|
|
854
963
|
// 构建执行的命令
|
|
855
|
-
const command = `git log ${formattedBranchRefs} --pretty=format:"
|
|
964
|
+
const command = `git log ${formattedBranchRefs} --pretty=format:"${formatString}" --date=short ${options}`;
|
|
856
965
|
console.log(`执行Git命令(带分支引用): ${command}`);
|
|
857
966
|
|
|
858
967
|
// 执行命令
|
|
@@ -872,7 +981,7 @@ async function startUIServer() {
|
|
|
872
981
|
totalCommits = 0;
|
|
873
982
|
}
|
|
874
983
|
|
|
875
|
-
processAndSendLogOutput(res, logOutput, totalCommits, skip / limit + 1, limit);
|
|
984
|
+
processAndSendLogOutput(res, logOutput, totalCommits, skip / limit + 1, limit, withParents);
|
|
876
985
|
} catch (error) {
|
|
877
986
|
console.error('执行Git日志命令失败:', error);
|
|
878
987
|
res.status(500).json({ error: '获取日志失败: ' + error.message });
|
|
@@ -880,7 +989,7 @@ async function startUIServer() {
|
|
|
880
989
|
}
|
|
881
990
|
|
|
882
991
|
// 抽取处理输出并发送响应的函数
|
|
883
|
-
function processAndSendLogOutput(res, logOutput, totalCommits, page, limit) {
|
|
992
|
+
function processAndSendLogOutput(res, logOutput, totalCommits, page, limit, withParents = false) {
|
|
884
993
|
// 替换提交记录之间的换行符
|
|
885
994
|
logOutput = logOutput.replace(/\n(?=[a-f0-9]{40}\|)/g, "<<<RECORD_SEPARATOR>>>");
|
|
886
995
|
|
|
@@ -891,7 +1000,7 @@ async function startUIServer() {
|
|
|
891
1000
|
const data = logEntries.map(entry => {
|
|
892
1001
|
const parts = entry.split('|');
|
|
893
1002
|
if (parts.length >= 5) {
|
|
894
|
-
|
|
1003
|
+
const result = {
|
|
895
1004
|
hash: parts[0],
|
|
896
1005
|
author: parts[1],
|
|
897
1006
|
email: parts[2],
|
|
@@ -899,6 +1008,13 @@ async function startUIServer() {
|
|
|
899
1008
|
message: parts[4],
|
|
900
1009
|
branch: parts[5] || ''
|
|
901
1010
|
};
|
|
1011
|
+
|
|
1012
|
+
// 如果请求了父提交信息,添加到结果中
|
|
1013
|
+
if (withParents && parts[6]) {
|
|
1014
|
+
result.parents = parts[6].split(' ');
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return result;
|
|
902
1018
|
}
|
|
903
1019
|
return null;
|
|
904
1020
|
}).filter(item => item !== null);
|
|
@@ -1179,6 +1295,36 @@ async function startUIServer() {
|
|
|
1179
1295
|
}
|
|
1180
1296
|
});
|
|
1181
1297
|
|
|
1298
|
+
// 重置到指定提交(hard)
|
|
1299
|
+
app.post('/api/reset-to-commit', async (req, res) => {
|
|
1300
|
+
try {
|
|
1301
|
+
const { hash } = req.body;
|
|
1302
|
+
|
|
1303
|
+
if (!hash) {
|
|
1304
|
+
return res.status(400).json({
|
|
1305
|
+
success: false,
|
|
1306
|
+
error: '缺少提交哈希参数'
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
console.log(`执行重置到指定提交操作: hash=${hash}`);
|
|
1311
|
+
|
|
1312
|
+
// 执行git reset --hard命令
|
|
1313
|
+
await execGitCommand(`git reset --hard ${hash}`);
|
|
1314
|
+
|
|
1315
|
+
res.json({
|
|
1316
|
+
success: true,
|
|
1317
|
+
message: `已成功重置到提交 ${hash}`
|
|
1318
|
+
});
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
console.error('重置到指定提交失败:', error);
|
|
1321
|
+
res.status(500).json({
|
|
1322
|
+
success: false,
|
|
1323
|
+
error: `重置到指定提交失败: ${error.message}`
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1182
1328
|
// 添加清理Git锁定文件的接口
|
|
1183
1329
|
app.post('/api/remove-lock', async (req, res) => {
|
|
1184
1330
|
try {
|
|
@@ -1263,7 +1409,32 @@ async function startUIServer() {
|
|
|
1263
1409
|
}
|
|
1264
1410
|
});
|
|
1265
1411
|
|
|
1266
|
-
|
|
1412
|
+
// 获取远程仓库URL的API
|
|
1413
|
+
app.get('/api/remote-url', async (req, res) => {
|
|
1414
|
+
try {
|
|
1415
|
+
// 检查当前目录是否是Git仓库
|
|
1416
|
+
if (!isGitRepo) {
|
|
1417
|
+
return res.json({ success: false, error: '当前目录不是Git仓库' });
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
// 执行git命令获取远程仓库URL
|
|
1421
|
+
const { stdout } = await execGitCommand('git config --get remote.origin.url');
|
|
1422
|
+
|
|
1423
|
+
// 返回远程仓库URL
|
|
1424
|
+
res.json({
|
|
1425
|
+
success: true,
|
|
1426
|
+
url: stdout.trim()
|
|
1427
|
+
});
|
|
1428
|
+
} catch (error) {
|
|
1429
|
+
console.error('获取远程仓库URL失败:', error);
|
|
1430
|
+
res.json({
|
|
1431
|
+
success: false,
|
|
1432
|
+
error: error.message || '获取远程仓库URL失败'
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
|
|
1437
|
+
// 获取所有作者列表
|
|
1267
1438
|
app.get('/api/authors', async (req, res) => {
|
|
1268
1439
|
try {
|
|
1269
1440
|
// 使用git命令获取所有提交者,不依赖Unix命令
|
|
@@ -1394,7 +1565,7 @@ async function startUIServer() {
|
|
|
1394
1565
|
const { stdout: statusOutput } = await execGitCommand('git status');
|
|
1395
1566
|
|
|
1396
1567
|
// 获取porcelain格式状态
|
|
1397
|
-
const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain');
|
|
1568
|
+
const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain --untracked-files=all');
|
|
1398
1569
|
|
|
1399
1570
|
// 广播到所有连接的客户端
|
|
1400
1571
|
io.emit('git_status_update', {
|
package/src/ui/client/README.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Vue 3 + TypeScript + Vite
|
|
2
|
-
|
|
3
|
-
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
|
4
|
-
|
|
5
|
-
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// Generated by unplugin-vue-components
|
|
4
|
-
// Read more: https://github.com/vuejs/core/pull/3399
|
|
5
|
-
// biome-ignore lint: disable
|
|
6
|
-
export {}
|
|
7
|
-
|
|
8
|
-
/* prettier-ignore */
|
|
9
|
-
declare module 'vue' {
|
|
10
|
-
export interface GlobalComponents {
|
|
11
|
-
CommitForm: typeof import('./src/components/CommitForm.vue')['default']
|
|
12
|
-
ElAlert: typeof import('element-plus/es')['ElAlert']
|
|
13
|
-
ElButton: typeof import('element-plus/es')['ElButton']
|
|
14
|
-
ElCard: typeof import('element-plus/es')['ElCard']
|
|
15
|
-
ElDialog: typeof import('element-plus/es')['ElDialog']
|
|
16
|
-
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
|
17
|
-
ElForm: typeof import('element-plus/es')['ElForm']
|
|
18
|
-
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
|
19
|
-
ElIcon: typeof import('element-plus/es')['ElIcon']
|
|
20
|
-
ElInput: typeof import('element-plus/es')['ElInput']
|
|
21
|
-
ElOption: typeof import('element-plus/es')['ElOption']
|
|
22
|
-
ElRow: typeof import('element-plus/es')['ElRow']
|
|
23
|
-
ElSelect: typeof import('element-plus/es')['ElSelect']
|
|
24
|
-
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
|
25
|
-
ElTag: typeof import('element-plus/es')['ElTag']
|
|
26
|
-
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
|
27
|
-
GitStatus: typeof import('./src/components/GitStatus.vue')['default']
|
|
28
|
-
LogList: typeof import('./src/components/LogList.vue')['default']
|
|
29
|
-
}
|
|
30
|
-
export interface ComponentCustomProperties {
|
|
31
|
-
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
|
32
|
-
}
|
|
33
|
-
}
|
package/src/ui/client/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>Zen-GitSync - Git同步工具</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="app"></div>
|
|
11
|
-
<script type="module" src="/src/main.ts"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "client",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.0.0",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "vite",
|
|
8
|
-
"build": "vue-tsc -b && vite build",
|
|
9
|
-
"preview": "vite preview"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"@gitgraph/js": "^1.4.0",
|
|
13
|
-
"element-plus": "^2.9.9",
|
|
14
|
-
"pinia": "^3.0.2",
|
|
15
|
-
"socket.io-client": "^4.8.1",
|
|
16
|
-
"vue": "^3.5.13"
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@vitejs/plugin-vue": "^5.2.2",
|
|
20
|
-
"@vue/tsconfig": "^0.7.0",
|
|
21
|
-
"rollup-plugin-visualizer": "^5.14.0",
|
|
22
|
-
"typescript": "~5.7.2",
|
|
23
|
-
"unplugin-auto-import": "^19.2.0",
|
|
24
|
-
"unplugin-vue-components": "^28.5.0",
|
|
25
|
-
"vite": "^6.3.1",
|
|
26
|
-
"vue-tsc": "^2.2.8"
|
|
27
|
-
}
|
|
28
|
-
}
|