zen-gitsync 1.5.4 → 2.0.1
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/README.md +9 -0
- package/package.json +12 -3
- package/src/config.js +2 -1
- package/src/gitCommit.js +14 -1
- package/src/ui/client/README.md +5 -0
- package/src/ui/client/auto-imports.d.ts +10 -0
- package/src/ui/client/components.d.ts +31 -0
- package/src/ui/client/index.html +13 -0
- package/src/ui/client/package.json +27 -0
- package/src/ui/client/public/favicon.svg +27 -0
- package/src/ui/client/public/logo.svg +27 -0
- package/src/ui/client/public/vite.svg +1 -0
- package/src/ui/client/src/App.vue +539 -0
- package/src/ui/client/src/assets/logo.svg +27 -0
- package/src/ui/client/src/components/CommitForm.vue +904 -0
- package/src/ui/client/src/components/GitStatus.vue +403 -0
- package/src/ui/client/src/components/LogList.vue +270 -0
- package/src/ui/client/src/main.ts +4 -0
- package/src/ui/client/src/vite-env.d.ts +1 -0
- package/src/ui/client/stats.html +4949 -0
- package/src/ui/client/tsconfig.app.json +14 -0
- package/src/ui/client/tsconfig.json +7 -0
- package/src/ui/client/tsconfig.node.json +24 -0
- package/src/ui/client/vite.config.ts +48 -0
- package/src/ui/public/assets/index-BcTk2R6G.js +9 -0
- package/src/ui/public/assets/index-ChUZ1vPG.css +1 -0
- package/src/ui/public/assets/vendor-BAXrrwNU.js +41 -0
- package/src/ui/public/assets/vendor-Dp0FkvMe.css +1 -0
- package/src/ui/public/favicon.svg +27 -0
- package/src/ui/public/index.html +16 -0
- package/src/ui/public/logo.svg +27 -0
- package/src/ui/public/vite.svg +1 -0
- package/src/ui/server/index.js +447 -0
- package/src/utils/index.js +59 -19
|
@@ -0,0 +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" />
|
|
27
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
<script type="module" crossorigin src="/assets/index-BcTk2R6G.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-BAXrrwNU.js">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/vendor-Dp0FkvMe.css">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-ChUZ1vPG.css">
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="app"></div>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +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" />
|
|
27
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { createServer } from 'http';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { execGitCommand } from '../../utils/index.js';
|
|
6
|
+
import open from 'open';
|
|
7
|
+
import config from '../../config.js';
|
|
8
|
+
// import { Server } from 'socket.io';
|
|
9
|
+
// import { exec } from 'child_process';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
const configManager = config; // 确保 configManager 可用
|
|
14
|
+
|
|
15
|
+
async function startUIServer() {
|
|
16
|
+
const app = express();
|
|
17
|
+
const httpServer = createServer(app);
|
|
18
|
+
// const io = new Server(httpServer);
|
|
19
|
+
|
|
20
|
+
// 添加全局中间件来解析JSON请求体
|
|
21
|
+
app.use(express.json());
|
|
22
|
+
|
|
23
|
+
// // 启动前端Vue应用
|
|
24
|
+
// const clientPath = path.join(__dirname, '../client');
|
|
25
|
+
// console.log(`正在启动前端应用,路径: ${clientPath}`);
|
|
26
|
+
|
|
27
|
+
// const vueProcess = exec('npm run dev', { cwd: clientPath }, (error) => {
|
|
28
|
+
// if (error) {
|
|
29
|
+
// console.error('启动前端应用失败:', error);
|
|
30
|
+
// }
|
|
31
|
+
// });
|
|
32
|
+
|
|
33
|
+
// vueProcess.stdout.on('data', (data) => {
|
|
34
|
+
// console.log(`前端输出: ${data}`);
|
|
35
|
+
// });
|
|
36
|
+
|
|
37
|
+
// vueProcess.stderr.on('data', (data) => {
|
|
38
|
+
// console.error(`前端错误: ${data}`);
|
|
39
|
+
// });
|
|
40
|
+
|
|
41
|
+
// 静态文件服务
|
|
42
|
+
app.use(express.static(path.join(__dirname, '../public')));
|
|
43
|
+
|
|
44
|
+
// API路由
|
|
45
|
+
app.get('/api/status', async (req, res) => {
|
|
46
|
+
try {
|
|
47
|
+
const { stdout } = await execGitCommand('git status');
|
|
48
|
+
res.json({ status: stdout });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
res.status(500).json({ error: error.message });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
app.get('/api/status_porcelain', async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const { stdout } = await execGitCommand('git status --porcelain');
|
|
56
|
+
res.json({ status: stdout });
|
|
57
|
+
} catch (error) {
|
|
58
|
+
res.status(500).json({ error: error.message });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 获取当前分支
|
|
63
|
+
app.get('/api/branch', async (req, res) => {
|
|
64
|
+
try {
|
|
65
|
+
const { stdout } = await execGitCommand('git rev-parse --abbrev-ref HEAD');
|
|
66
|
+
res.json({ branch: stdout.trim() });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
res.status(500).json({ error: error.message });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// 获取所有分支
|
|
73
|
+
app.get('/api/branches', async (req, res) => {
|
|
74
|
+
try {
|
|
75
|
+
// 获取本地分支
|
|
76
|
+
const { stdout: localBranches } = await execGitCommand('git branch --format="%(refname:short)"');
|
|
77
|
+
// 获取远程分支(过滤掉 origin/HEAD 和 origin)
|
|
78
|
+
const { stdout: remoteBranches } = await execGitCommand('git branch -r --format="%(refname:short)"');
|
|
79
|
+
|
|
80
|
+
// 合并并去重
|
|
81
|
+
const allBranches = [...new Set([
|
|
82
|
+
...localBranches.split('\n')
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.filter(b => !b.startsWith('* ')), // 过滤掉HEAD指针
|
|
85
|
+
...remoteBranches.split('\n')
|
|
86
|
+
.filter(Boolean)
|
|
87
|
+
.filter(b => b.includes('/')) // 过滤掉单纯的 origin
|
|
88
|
+
.map(b => b.split('/')[1]) // 提取真正的分支名称
|
|
89
|
+
])];
|
|
90
|
+
|
|
91
|
+
res.json({ branches: allBranches });
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('获取分支列表失败:', error);
|
|
94
|
+
res.status(500).json({ error: error.message });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// 创建新分支
|
|
99
|
+
app.post('/api/create-branch', express.json(), async (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
const { newBranchName, baseBranch } = req.body;
|
|
102
|
+
|
|
103
|
+
if (!newBranchName) {
|
|
104
|
+
return res.status(400).json({ success: false, error: '分支名称不能为空' });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 构建创建分支的命令
|
|
108
|
+
let command = `git branch ${newBranchName}`;
|
|
109
|
+
|
|
110
|
+
// 如果指定了基础分支,则基于该分支创建
|
|
111
|
+
if (baseBranch) {
|
|
112
|
+
command = `git branch ${newBranchName} ${baseBranch}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 执行创建分支命令
|
|
116
|
+
await execGitCommand(command);
|
|
117
|
+
|
|
118
|
+
// 切换到新创建的分支
|
|
119
|
+
await execGitCommand(`git checkout ${newBranchName}`);
|
|
120
|
+
|
|
121
|
+
res.json({ success: true, branch: newBranchName });
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error('创建分支失败:', error);
|
|
124
|
+
res.status(500).json({ success: false, error: error.message });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// 切换分支
|
|
129
|
+
app.post('/api/checkout', async (req, res) => {
|
|
130
|
+
try {
|
|
131
|
+
const { branch } = req.body;
|
|
132
|
+
if (!branch) {
|
|
133
|
+
return res.status(400).json({ success: false, error: '分支名称不能为空' });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 执行分支切换
|
|
137
|
+
await execGitCommand(`git checkout ${branch}`);
|
|
138
|
+
|
|
139
|
+
res.json({ success: true });
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('切换分支失败:', error);
|
|
142
|
+
res.status(500).json({ success: false, error: error.message });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// 获取Git用户配置信息
|
|
147
|
+
app.get('/api/user-info', async (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
// 获取用户名
|
|
150
|
+
const { stdout: userName } = await execGitCommand('git config user.name');
|
|
151
|
+
// 获取用户邮箱
|
|
152
|
+
const { stdout: userEmail } = await execGitCommand('git config user.email');
|
|
153
|
+
|
|
154
|
+
res.json({
|
|
155
|
+
name: userName.trim(),
|
|
156
|
+
email: userEmail.trim()
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
res.status(500).json({ error: error.message });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// 新增获取当前工作目录接口
|
|
164
|
+
app.get('/api/current_directory', async (req, res) => {
|
|
165
|
+
try {
|
|
166
|
+
const directory = process.cwd();
|
|
167
|
+
|
|
168
|
+
// 检查当前目录是否是Git仓库
|
|
169
|
+
try {
|
|
170
|
+
await execGitCommand('git rev-parse --is-inside-work-tree');
|
|
171
|
+
} catch (error) {
|
|
172
|
+
return res.status(400).json({
|
|
173
|
+
error: '当前目录不是一个Git仓库',
|
|
174
|
+
directory,
|
|
175
|
+
isGitRepo: false
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
res.json({
|
|
180
|
+
directory,
|
|
181
|
+
isGitRepo: true
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
res.status(500).json({ error: error.message });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// 获取配置
|
|
189
|
+
app.get('/api/config/getConfig', async (req, res) => {
|
|
190
|
+
try {
|
|
191
|
+
const config = await configManager.loadConfig()
|
|
192
|
+
res.json(config)
|
|
193
|
+
} catch (error) {
|
|
194
|
+
res.status(500).json({ error: error.message })
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// 保存描述模板
|
|
199
|
+
app.post('/api/config/save-template', express.json(), async (req, res) => {
|
|
200
|
+
try {
|
|
201
|
+
const { template, type } = req.body
|
|
202
|
+
|
|
203
|
+
if (!template || !type) {
|
|
204
|
+
return res.status(400).json({ success: false, error: '缺少必要参数' })
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const config = await configManager.loadConfig()
|
|
208
|
+
|
|
209
|
+
if (type === 'description') {
|
|
210
|
+
// 确保描述模板数组存在
|
|
211
|
+
if (!config.descriptionTemplates) {
|
|
212
|
+
config.descriptionTemplates = []
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 检查是否已存在相同模板
|
|
216
|
+
if (!config.descriptionTemplates.includes(template)) {
|
|
217
|
+
config.descriptionTemplates.push(template)
|
|
218
|
+
await configManager.saveConfig(config)
|
|
219
|
+
}
|
|
220
|
+
} else if (type === 'scope') {
|
|
221
|
+
// 确保作用域模板数组存在
|
|
222
|
+
if (!config.scopeTemplates) {
|
|
223
|
+
config.scopeTemplates = []
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 检查是否已存在相同模板
|
|
227
|
+
if (!config.scopeTemplates.includes(template)) {
|
|
228
|
+
config.scopeTemplates.push(template)
|
|
229
|
+
await configManager.saveConfig(config)
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
return res.status(400).json({ success: false, error: '不支持的模板类型' })
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
res.json({ success: true })
|
|
236
|
+
} catch (error) {
|
|
237
|
+
res.status(500).json({ success: false, error: error.message })
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// 删除描述模板
|
|
242
|
+
app.post('/api/config/delete-template', express.json(), async (req, res) => {
|
|
243
|
+
try {
|
|
244
|
+
const { template, type } = req.body
|
|
245
|
+
|
|
246
|
+
if (!template || !type) {
|
|
247
|
+
return res.status(400).json({ success: false, error: '缺少必要参数' })
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const config = await configManager.loadConfig()
|
|
251
|
+
|
|
252
|
+
if (type === 'description') {
|
|
253
|
+
// 确保描述模板数组存在
|
|
254
|
+
if (config.descriptionTemplates) {
|
|
255
|
+
const index = config.descriptionTemplates.indexOf(template)
|
|
256
|
+
if (index !== -1) {
|
|
257
|
+
config.descriptionTemplates.splice(index, 1)
|
|
258
|
+
await configManager.saveConfig(config)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} else if (type === 'scope') {
|
|
262
|
+
// 确保作用域模板数组存在
|
|
263
|
+
if (config.scopeTemplates) {
|
|
264
|
+
const index = config.scopeTemplates.indexOf(template)
|
|
265
|
+
if (index !== -1) {
|
|
266
|
+
config.scopeTemplates.splice(index, 1)
|
|
267
|
+
await configManager.saveConfig(config)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
return res.status(400).json({ success: false, error: '不支持的模板类型' })
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
res.json({ success: true })
|
|
275
|
+
} catch (error) {
|
|
276
|
+
res.status(500).json({ success: false, error: error.message })
|
|
277
|
+
}
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// 提交更改
|
|
281
|
+
app.post('/api/commit', express.json(), async (req, res) => {
|
|
282
|
+
try {
|
|
283
|
+
const { message, hasNewlines, noVerify } = req.body;
|
|
284
|
+
|
|
285
|
+
// 构建 git commit 命令
|
|
286
|
+
let commitCommand = 'git commit';
|
|
287
|
+
|
|
288
|
+
// 如果消息包含换行符,使用文件方式提交
|
|
289
|
+
if (hasNewlines) {
|
|
290
|
+
// 创建临时文件存储提交信息
|
|
291
|
+
const tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
|
|
292
|
+
await fs.writeFile(tempFile, message);
|
|
293
|
+
commitCommand += ` -F "${tempFile}"`;
|
|
294
|
+
} else {
|
|
295
|
+
// 否则直接在命令行中提供消息
|
|
296
|
+
commitCommand += ` -m "${message}"`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 添加 --no-verify 参数
|
|
300
|
+
if (noVerify) {
|
|
301
|
+
commitCommand += ' --no-verify';
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
console.log(`commitCommand ==>`, commitCommand);
|
|
305
|
+
// 执行提交命令
|
|
306
|
+
await execGitCommand(commitCommand);
|
|
307
|
+
|
|
308
|
+
// 如果使用了临时文件,删除它
|
|
309
|
+
if (hasNewlines) {
|
|
310
|
+
const tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
|
|
311
|
+
await fs.unlink(tempFile).catch(() => {});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
res.json({ success: true });
|
|
315
|
+
} catch (error) {
|
|
316
|
+
res.status(500).json({ success: false, error: error.message });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// 添加 add 接口
|
|
321
|
+
app.post('/api/add', async (req, res) => {
|
|
322
|
+
try {
|
|
323
|
+
// 执行 git add . 命令添加所有更改
|
|
324
|
+
await execGitCommand('git add .');
|
|
325
|
+
res.json({ success: true });
|
|
326
|
+
} catch (error) {
|
|
327
|
+
res.status(500).json({ success: false, error: error.message });
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// 推送更改
|
|
332
|
+
app.post('/api/push', async (req, res) => {
|
|
333
|
+
try {
|
|
334
|
+
await execGitCommand('git push');
|
|
335
|
+
res.json({ success: true });
|
|
336
|
+
} catch (error) {
|
|
337
|
+
res.status(500).json({ error: error.message });
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// 获取日志
|
|
342
|
+
app.get('/api/log', async (req, res) => {
|
|
343
|
+
try {
|
|
344
|
+
// 获取请求参数中的数量限制,默认为100
|
|
345
|
+
const limit = req.query.all === 'true' ? '' : '-n 100';
|
|
346
|
+
|
|
347
|
+
// 修改 git log 命令,添加 %ae 参数来获取作者邮箱
|
|
348
|
+
const { stdout } = await execGitCommand(`git log --all --pretty=format:"%h|%an|%ae|%ad|%s|%D" --date=short ${limit}`);
|
|
349
|
+
const logs = stdout.split('\n').map(line => {
|
|
350
|
+
const [hash, author, email, date, message, refs] = line.split('|');
|
|
351
|
+
|
|
352
|
+
// 从引用信息中提取分支名称
|
|
353
|
+
let branch = null;
|
|
354
|
+
if (refs) {
|
|
355
|
+
// 提取所有引用信息,而不仅仅是第一个匹配
|
|
356
|
+
branch = refs.trim();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return { hash, author, email, date, message, branch };
|
|
360
|
+
});
|
|
361
|
+
res.json(logs);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
res.status(500).json({ error: error.message });
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// 获取文件差异
|
|
368
|
+
app.get('/api/diff', async (req, res) => {
|
|
369
|
+
try {
|
|
370
|
+
const filePath = req.query.file;
|
|
371
|
+
|
|
372
|
+
if (!filePath) {
|
|
373
|
+
return res.status(400).json({ error: '缺少文件路径参数' });
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// 执行git diff命令获取文件差异
|
|
377
|
+
const { stdout } = await execGitCommand(`git diff -- "${filePath}"`);
|
|
378
|
+
|
|
379
|
+
res.json({ diff: stdout });
|
|
380
|
+
} catch (error) {
|
|
381
|
+
res.status(500).json({ error: error.message });
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Socket.io 实时更新
|
|
386
|
+
// io.on('connection', (socket) => {
|
|
387
|
+
// console.log('客户端已连接');
|
|
388
|
+
|
|
389
|
+
// // 定期发送状态更新
|
|
390
|
+
// const statusInterval = setInterval(async () => {
|
|
391
|
+
// try {
|
|
392
|
+
// const { stdout } = await execGitCommand('git status');
|
|
393
|
+
// socket.emit('status_update', { status: stdout });
|
|
394
|
+
// } catch (error) {
|
|
395
|
+
// console.error('状态更新错误:', error);
|
|
396
|
+
// }
|
|
397
|
+
// }, 5000);
|
|
398
|
+
|
|
399
|
+
// socket.on('disconnect', () => {
|
|
400
|
+
// clearInterval(statusInterval);
|
|
401
|
+
// console.log('客户端已断开连接');
|
|
402
|
+
// });
|
|
403
|
+
// });
|
|
404
|
+
|
|
405
|
+
// 启动服务器
|
|
406
|
+
const PORT = 3000;
|
|
407
|
+
httpServer.listen(PORT, () => {
|
|
408
|
+
console.log(`后端API服务器已启动: http://localhost:${PORT}`);
|
|
409
|
+
open(`http://localhost:${PORT}`);
|
|
410
|
+
}).on('error', async (err) => {
|
|
411
|
+
if (err.code === 'EADDRINUSE') {
|
|
412
|
+
console.log(`端口 ${PORT} 被占用,尝试其他端口...`);
|
|
413
|
+
let newPort = PORT + 1;
|
|
414
|
+
while (newPort < PORT + 100) {
|
|
415
|
+
try {
|
|
416
|
+
await new Promise((resolve, reject) => {
|
|
417
|
+
httpServer.listen(newPort, () => {
|
|
418
|
+
console.log(`后端API服务器已启动: http://localhost:${newPort}`);
|
|
419
|
+
open(`http://localhost:${newPort}`);
|
|
420
|
+
resolve();
|
|
421
|
+
}).on('error', (e) => {
|
|
422
|
+
if (e.code === 'EADDRINUSE') {
|
|
423
|
+
console.log(`端口 ${newPort} 也被占用,继续尝试...`);
|
|
424
|
+
newPort++;
|
|
425
|
+
reject(e);
|
|
426
|
+
} else {
|
|
427
|
+
reject(e);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
break;
|
|
432
|
+
} catch (e) {
|
|
433
|
+
if (newPort >= PORT + 100) {
|
|
434
|
+
console.error('无法找到可用端口,请手动指定端口');
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
console.error('服务器启动失败:', err);
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export default startUIServer;
|
|
447
|
+
|
package/src/utils/index.js
CHANGED
|
@@ -290,30 +290,34 @@ Options:
|
|
|
290
290
|
-h, --help Show this help message
|
|
291
291
|
--set-default-message=<msg> Set default commit message
|
|
292
292
|
get-config Show current configuration
|
|
293
|
-
-y
|
|
294
|
-
-m <message>
|
|
295
|
-
-m=<message>
|
|
296
|
-
--path=<path>
|
|
297
|
-
--cwd=<path>
|
|
298
|
-
--interval=<seconds>
|
|
299
|
-
log
|
|
300
|
-
--n=<number>
|
|
301
|
-
--no-diff
|
|
302
|
-
addScript
|
|
293
|
+
-y Auto commit with default message
|
|
294
|
+
-m <message> Commit message (use quotes if message contains spaces)
|
|
295
|
+
-m=<message> Commit message (use this form without spaces around '=')
|
|
296
|
+
--path=<path> Set custom working directory
|
|
297
|
+
--cwd=<path> Set custom working directory
|
|
298
|
+
--interval=<seconds> Set interval time for automatic commits (in seconds)
|
|
299
|
+
log Show git commit logs
|
|
300
|
+
--n=<number> Number of commits to show with --log
|
|
301
|
+
--no-diff Skip displaying git diff
|
|
302
|
+
addScript Add "g:y": "g -y" to package.json scripts
|
|
303
|
+
addResetScript Add "g:reset": "git reset --hard origin/<current-branch>" to package.json scripts
|
|
304
|
+
ui Launch graphical user interface (v2.0.0)
|
|
303
305
|
|
|
304
306
|
Example:
|
|
305
307
|
g -m "Initial commit" Commit with a custom message
|
|
306
|
-
g -m=Fix-bug
|
|
307
|
-
g -y
|
|
308
|
-
g -y --interval=600
|
|
309
|
-
g --path=/path/to/repo
|
|
310
|
-
g log
|
|
311
|
-
g log --n=5
|
|
312
|
-
g addScript
|
|
308
|
+
g -m=Fix-bug Commit with a custom message (no spaces around '=')
|
|
309
|
+
g -y Auto commit with the default message
|
|
310
|
+
g -y --interval=600 Commit every 10 minutes (600 seconds)
|
|
311
|
+
g --path=/path/to/repo Specify a custom working directory
|
|
312
|
+
g log Show recent commit logs
|
|
313
|
+
g log --n=5 Show the last 5 commits with --log
|
|
314
|
+
g addScript Add auto commit script to package.json
|
|
315
|
+
g addResetScript Add reset script to package.json
|
|
313
316
|
|
|
314
317
|
Add auto submit in package.json:
|
|
315
318
|
"scripts": {
|
|
316
|
-
"g:y": "g -y"
|
|
319
|
+
"g:y": "g -y",
|
|
320
|
+
"g:reset": "git reset --hard origin/<current-branch>"
|
|
317
321
|
}
|
|
318
322
|
|
|
319
323
|
Run in the background across platforms:
|
|
@@ -323,6 +327,9 @@ Run in the background across platforms:
|
|
|
323
327
|
Linux/macOS:
|
|
324
328
|
nohup g -y --path=your-folder --interval=600 > git-autocommit.log 2>&1 &
|
|
325
329
|
|
|
330
|
+
Start GUI interface:
|
|
331
|
+
g ui
|
|
332
|
+
|
|
326
333
|
Stop all monitoring processes:
|
|
327
334
|
Windows: Terminate the Node.js process in the Task Manager.
|
|
328
335
|
Linux/macOS:
|
|
@@ -626,10 +633,43 @@ async function addScriptToPackageJson() {
|
|
|
626
633
|
}
|
|
627
634
|
}
|
|
628
635
|
|
|
636
|
+
async function addResetScriptToPackageJson() {
|
|
637
|
+
try {
|
|
638
|
+
// 读取当前目录的 package.json
|
|
639
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
640
|
+
const packageJson = JSON.parse(await fs.readFile(packagePath, 'utf8'));
|
|
641
|
+
|
|
642
|
+
// 确保有 scripts 部分
|
|
643
|
+
if (!packageJson.scripts) {
|
|
644
|
+
packageJson.scripts = {};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// 获取当前分支名
|
|
648
|
+
const branch = execSyncGitCommand('git branch --show-current', {log: false}).trim();
|
|
649
|
+
|
|
650
|
+
// 添加 g:reset 命令
|
|
651
|
+
if (!packageJson.scripts['g:reset']) {
|
|
652
|
+
packageJson.scripts['g:reset'] = `git reset --hard origin/${branch}`;
|
|
653
|
+
// 写回文件
|
|
654
|
+
await fs.writeFile(packagePath, JSON.stringify(packageJson, null, 2));
|
|
655
|
+
console.log(chalk.green(`✓ 成功添加 g:reset 脚本到 package.json (重置到 origin/${branch})`));
|
|
656
|
+
} else {
|
|
657
|
+
console.log(chalk.yellow('⚠️ g:reset 脚本已存在'));
|
|
658
|
+
}
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (error.code === 'ENOENT') {
|
|
661
|
+
console.error(chalk.red('❌ 当前目录下未找到 package.json 文件'));
|
|
662
|
+
} else {
|
|
663
|
+
console.error(chalk.red('❌ 添加脚本失败:'), error.message);
|
|
664
|
+
}
|
|
665
|
+
process.exit(1);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
629
669
|
export {
|
|
630
670
|
coloredLog, errorLog, execSyncGitCommand,
|
|
631
671
|
execGitCommand, getCwd, judgePlatform, showHelp, judgeLog, printGitLog,
|
|
632
672
|
judgeHelp, exec_exit, judgeUnmerged, delay, formatDuration,
|
|
633
673
|
exec_push, execPull, judgeRemote, execDiff, execAddAndCommit,
|
|
634
|
-
addScriptToPackageJson
|
|
674
|
+
addScriptToPackageJson, addResetScriptToPackageJson
|
|
635
675
|
};
|