zen-gitsync 2.0.3 → 2.0.5
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/ui/client/components.d.ts +1 -0
- package/src/ui/client/package.json +1 -0
- package/src/ui/client/src/App.vue +368 -219
- package/src/ui/client/src/components/CommitForm.vue +777 -442
- package/src/ui/client/src/components/GitStatus.vue +89 -86
- package/src/ui/client/src/components/LogList.vue +393 -85
- package/src/ui/client/src/main.ts +3 -0
- package/src/ui/client/src/stores/gitLogStore.ts +464 -0
- package/src/ui/client/src/stores/gitStore.ts +301 -0
- package/src/ui/client/stats.html +1 -1
- package/src/ui/public/assets/index-CALk9kKc.js +9 -0
- package/src/ui/public/assets/index-D3zIiSNw.css +1 -0
- package/src/ui/public/assets/vendor-BfXVsoKv.js +45 -0
- package/src/ui/public/index.html +3 -3
- package/src/ui/server/index.js +197 -8
- package/src/ui/public/assets/index-BHmYZROy.css +0 -1
- package/src/ui/public/assets/index-kfMX1bxz.js +0 -9
- package/src/ui/public/assets/vendor-DxvF30ca.js +0 -41
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { ElMessage } from 'element-plus'
|
|
4
|
+
import { useGitStore } from './gitStore'
|
|
5
|
+
|
|
6
|
+
// 定义Git操作间隔时间(毫秒)
|
|
7
|
+
const GIT_OPERATION_DELAY = 300
|
|
8
|
+
|
|
9
|
+
export const useGitLogStore = defineStore('gitLog', () => {
|
|
10
|
+
// 引用gitStore获取仓库状态
|
|
11
|
+
const gitStore = useGitStore()
|
|
12
|
+
|
|
13
|
+
// 状态
|
|
14
|
+
const log = ref<any[]>([])
|
|
15
|
+
const status = ref<{ staged: string[], unstaged: string[], untracked: string[] }>({
|
|
16
|
+
staged: [],
|
|
17
|
+
unstaged: [],
|
|
18
|
+
untracked: []
|
|
19
|
+
})
|
|
20
|
+
// 添加Git状态文本
|
|
21
|
+
const statusText = ref('')
|
|
22
|
+
// 添加fileList状态用于保存porcelain格式的状态
|
|
23
|
+
const fileList = ref<{path: string, type: string}[]>([])
|
|
24
|
+
const isLoadingLog = ref(false)
|
|
25
|
+
const isLoadingStatus = ref(false)
|
|
26
|
+
const isAddingFiles = ref(false)
|
|
27
|
+
const isCommiting = ref(false)
|
|
28
|
+
const isPushing = ref(false)
|
|
29
|
+
const isResetting = ref(false)
|
|
30
|
+
|
|
31
|
+
// 解析 git status --porcelain 输出,提取文件及类型
|
|
32
|
+
function parseStatusPorcelain(statusText: string) {
|
|
33
|
+
if (statusText === undefined || statusText === '') {
|
|
34
|
+
// 如果状态为空字符串,清空文件列表
|
|
35
|
+
fileList.value = []
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const lines = statusText.split('\n')
|
|
40
|
+
const files: {path: string, type: string}[] = []
|
|
41
|
+
for (const line of lines) {
|
|
42
|
+
// 匹配常见的 git status --porcelain 格式
|
|
43
|
+
// M: 修改, A: 新增, D: 删除, ??: 未跟踪
|
|
44
|
+
const match = line.match(/^([ MADRCU\?]{2})\s+(.+)$/)
|
|
45
|
+
if (match) {
|
|
46
|
+
let type = ''
|
|
47
|
+
const code = match[1].trim()
|
|
48
|
+
if (code === 'M' || code === 'MM' || code === 'AM' || code === 'RM') type = 'modified'
|
|
49
|
+
else if (code === 'A' || code === 'AA') type = 'added'
|
|
50
|
+
else if (code === 'D' || code === 'AD' || code === 'DA') type = 'deleted'
|
|
51
|
+
else if (code === '??') type = 'untracked'
|
|
52
|
+
else type = 'other'
|
|
53
|
+
files.push({ path: match[2], type })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
fileList.value = files
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 获取提交日志
|
|
60
|
+
async function fetchLog() {
|
|
61
|
+
// 检查是否是Git仓库
|
|
62
|
+
if (!gitStore.isGitRepo) {
|
|
63
|
+
console.log('当前目录不是Git仓库,跳过加载提交历史')
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
isLoadingLog.value = true
|
|
69
|
+
console.log('开始加载提交历史...')
|
|
70
|
+
const response = await fetch('/api/log')
|
|
71
|
+
const data = await response.json()
|
|
72
|
+
if (data && Array.isArray(data)) {
|
|
73
|
+
log.value = data
|
|
74
|
+
}
|
|
75
|
+
console.log(`提交历史加载完成,共 ${log.value.length} 条记录`)
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('获取提交历史失败:', error)
|
|
78
|
+
ElMessage({
|
|
79
|
+
message: `获取提交历史失败: ${(error as Error).message}`,
|
|
80
|
+
type: 'error'
|
|
81
|
+
})
|
|
82
|
+
} finally {
|
|
83
|
+
isLoadingLog.value = false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 获取Git状态
|
|
88
|
+
async function fetchStatus() {
|
|
89
|
+
// 检查是否是Git仓库
|
|
90
|
+
if (!gitStore.isGitRepo) {
|
|
91
|
+
console.log('当前目录不是Git仓库,跳过加载Git状态')
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
isLoadingStatus.value = true
|
|
97
|
+
const response = await fetch('/api/status')
|
|
98
|
+
const data = await response.json()
|
|
99
|
+
if (data.status) {
|
|
100
|
+
// 更新状态文本
|
|
101
|
+
statusText.value = data.status
|
|
102
|
+
status.value = {
|
|
103
|
+
staged: data.status.staged || [],
|
|
104
|
+
unstaged: data.status.unstaged || [],
|
|
105
|
+
untracked: data.status.untracked || []
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 同时获取porcelain格式的状态
|
|
110
|
+
await fetchStatusPorcelain()
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('获取Git状态失败:', error)
|
|
113
|
+
ElMessage({
|
|
114
|
+
message: `获取Git状态失败: ${(error as Error).message}`,
|
|
115
|
+
type: 'error'
|
|
116
|
+
})
|
|
117
|
+
} finally {
|
|
118
|
+
isLoadingStatus.value = false
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 获取Git状态 (porcelain格式)
|
|
123
|
+
async function fetchStatusPorcelain() {
|
|
124
|
+
// 检查是否是Git仓库
|
|
125
|
+
if (!gitStore.isGitRepo) {
|
|
126
|
+
console.log('当前目录不是Git仓库,跳过加载Git状态')
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const response = await fetch('/api/status_porcelain')
|
|
132
|
+
const data = await response.json()
|
|
133
|
+
if (data.status !== undefined) {
|
|
134
|
+
parseStatusPorcelain(data.status)
|
|
135
|
+
} else {
|
|
136
|
+
// 如果没有收到有效的 status 字段,清空文件列表
|
|
137
|
+
fileList.value = []
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('获取Git状态(porcelain)失败:', error)
|
|
141
|
+
ElMessage({
|
|
142
|
+
message: `获取Git状态(porcelain)失败: ${(error as Error).message}`,
|
|
143
|
+
type: 'error'
|
|
144
|
+
})
|
|
145
|
+
// 清空文件列表
|
|
146
|
+
fileList.value = []
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 添加文件到暂存区 (git add .)
|
|
151
|
+
async function addToStage() {
|
|
152
|
+
// 检查是否是Git仓库
|
|
153
|
+
if (!gitStore.isGitRepo) {
|
|
154
|
+
ElMessage.warning('当前目录不是Git仓库')
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
isAddingFiles.value = true
|
|
160
|
+
const response = await fetch('/api/add', {
|
|
161
|
+
method: 'POST'
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const result = await response.json()
|
|
165
|
+
if (result.success) {
|
|
166
|
+
ElMessage({
|
|
167
|
+
message: '文件已添加到暂存区',
|
|
168
|
+
type: 'success'
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// 刷新状态
|
|
172
|
+
fetchStatus()
|
|
173
|
+
|
|
174
|
+
return true
|
|
175
|
+
} else {
|
|
176
|
+
ElMessage({
|
|
177
|
+
message: `添加文件失败: ${result.error}`,
|
|
178
|
+
type: 'error'
|
|
179
|
+
})
|
|
180
|
+
return false
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
ElMessage({
|
|
184
|
+
message: `添加文件失败: ${(error as Error).message}`,
|
|
185
|
+
type: 'error'
|
|
186
|
+
})
|
|
187
|
+
return false
|
|
188
|
+
} finally {
|
|
189
|
+
isAddingFiles.value = false
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 添加延时函数
|
|
194
|
+
function delay(ms: number) {
|
|
195
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 提交更改
|
|
199
|
+
async function commitChanges(message: string, noVerify = false) {
|
|
200
|
+
// 检查是否是Git仓库
|
|
201
|
+
if (!gitStore.isGitRepo) {
|
|
202
|
+
ElMessage.warning('当前目录不是Git仓库')
|
|
203
|
+
return false
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
isCommiting.value = true
|
|
208
|
+
const response = await fetch('/api/commit', {
|
|
209
|
+
method: 'POST',
|
|
210
|
+
headers: {
|
|
211
|
+
'Content-Type': 'application/json'
|
|
212
|
+
},
|
|
213
|
+
body: JSON.stringify({
|
|
214
|
+
message,
|
|
215
|
+
hasNewlines: message.includes('\n'),
|
|
216
|
+
noVerify
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
const result = await response.json()
|
|
221
|
+
if (result.success) {
|
|
222
|
+
ElMessage({
|
|
223
|
+
message: '提交成功',
|
|
224
|
+
type: 'success'
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
// 刷新状态和日志
|
|
228
|
+
fetchStatus()
|
|
229
|
+
fetchLog()
|
|
230
|
+
|
|
231
|
+
return true
|
|
232
|
+
} else {
|
|
233
|
+
ElMessage({
|
|
234
|
+
message: `提交失败: ${result.error}`,
|
|
235
|
+
type: 'error'
|
|
236
|
+
})
|
|
237
|
+
return false
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
ElMessage({
|
|
241
|
+
message: `提交失败: ${(error as Error).message}`,
|
|
242
|
+
type: 'error'
|
|
243
|
+
})
|
|
244
|
+
return false
|
|
245
|
+
} finally {
|
|
246
|
+
isCommiting.value = false
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 推送到远程
|
|
251
|
+
async function pushToRemote() {
|
|
252
|
+
// 检查是否是Git仓库
|
|
253
|
+
if (!gitStore.isGitRepo) {
|
|
254
|
+
ElMessage.warning('当前目录不是Git仓库')
|
|
255
|
+
return false
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
isPushing.value = true
|
|
260
|
+
const response = await fetch('/api/push', {
|
|
261
|
+
method: 'POST'
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
const result = await response.json()
|
|
265
|
+
if (result.success) {
|
|
266
|
+
ElMessage({
|
|
267
|
+
message: '推送成功',
|
|
268
|
+
type: 'success'
|
|
269
|
+
})
|
|
270
|
+
// 刷新状态
|
|
271
|
+
fetchStatus()
|
|
272
|
+
|
|
273
|
+
// 刷新日志
|
|
274
|
+
fetchLog()
|
|
275
|
+
|
|
276
|
+
return true
|
|
277
|
+
} else {
|
|
278
|
+
ElMessage({
|
|
279
|
+
message: `推送失败: ${result.error}`,
|
|
280
|
+
type: 'error'
|
|
281
|
+
})
|
|
282
|
+
return false
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
ElMessage({
|
|
286
|
+
message: `推送失败: ${(error as Error).message}`,
|
|
287
|
+
type: 'error'
|
|
288
|
+
})
|
|
289
|
+
return false
|
|
290
|
+
} finally {
|
|
291
|
+
isPushing.value = false
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 暂存并提交
|
|
296
|
+
async function addAndCommit(message: string, noVerify = false) {
|
|
297
|
+
const addResult = await addToStage()
|
|
298
|
+
if (!addResult) return false
|
|
299
|
+
|
|
300
|
+
// 使用新的延时常量
|
|
301
|
+
await delay(GIT_OPERATION_DELAY)
|
|
302
|
+
|
|
303
|
+
return await commitChanges(message, noVerify)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 暂存、提交并推送
|
|
307
|
+
async function addCommitAndPush(message: string, noVerify = false) {
|
|
308
|
+
try {
|
|
309
|
+
const addResult = await addToStage()
|
|
310
|
+
if (!addResult) return false
|
|
311
|
+
|
|
312
|
+
// 使用新的延时常量
|
|
313
|
+
await delay(GIT_OPERATION_DELAY)
|
|
314
|
+
|
|
315
|
+
const commitResult = await commitChanges(message, noVerify)
|
|
316
|
+
if (!commitResult) return false
|
|
317
|
+
|
|
318
|
+
// 使用新的延时常量
|
|
319
|
+
await delay(GIT_OPERATION_DELAY)
|
|
320
|
+
|
|
321
|
+
return await pushToRemote()
|
|
322
|
+
} catch (error) {
|
|
323
|
+
// 如果发生错误,尝试删除 index.lock 文件
|
|
324
|
+
try {
|
|
325
|
+
const response = await fetch('/api/remove-lock', {
|
|
326
|
+
method: 'POST'
|
|
327
|
+
})
|
|
328
|
+
const result = await response.json()
|
|
329
|
+
if (result.success) {
|
|
330
|
+
ElMessage({
|
|
331
|
+
message: '已清理锁定文件,请重试操作',
|
|
332
|
+
type: 'warning'
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
} catch (e) {
|
|
336
|
+
console.error('清理锁定文件失败:', e)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
ElMessage({
|
|
340
|
+
message: `操作失败: ${(error as Error).message}`,
|
|
341
|
+
type: 'error'
|
|
342
|
+
})
|
|
343
|
+
return false
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 重置暂存区 (git reset HEAD)
|
|
348
|
+
async function resetHead() {
|
|
349
|
+
// 检查是否是Git仓库
|
|
350
|
+
if (!gitStore.isGitRepo) {
|
|
351
|
+
ElMessage.warning('当前目录不是Git仓库')
|
|
352
|
+
return false
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
isResetting.value = true
|
|
357
|
+
const response = await fetch('/api/reset-head', {
|
|
358
|
+
method: 'POST'
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
const result = await response.json()
|
|
362
|
+
if (result.success) {
|
|
363
|
+
ElMessage({
|
|
364
|
+
message: '已重置暂存区',
|
|
365
|
+
type: 'success'
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// 刷新状态
|
|
369
|
+
fetchStatus()
|
|
370
|
+
|
|
371
|
+
return true
|
|
372
|
+
} else {
|
|
373
|
+
ElMessage({
|
|
374
|
+
message: `重置暂存区失败: ${result.error}`,
|
|
375
|
+
type: 'error'
|
|
376
|
+
})
|
|
377
|
+
return false
|
|
378
|
+
}
|
|
379
|
+
} catch (error) {
|
|
380
|
+
ElMessage({
|
|
381
|
+
message: `重置暂存区失败: ${(error as Error).message}`,
|
|
382
|
+
type: 'error'
|
|
383
|
+
})
|
|
384
|
+
return false
|
|
385
|
+
} finally {
|
|
386
|
+
isResetting.value = false
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// 重置当前分支到远程状态
|
|
391
|
+
async function resetToRemote(branch: string) {
|
|
392
|
+
// 检查是否是Git仓库
|
|
393
|
+
if (!gitStore.isGitRepo) {
|
|
394
|
+
ElMessage.warning('当前目录不是Git仓库')
|
|
395
|
+
return false
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
isResetting.value = true
|
|
400
|
+
const response = await fetch('/api/reset-to-remote', {
|
|
401
|
+
method: 'POST',
|
|
402
|
+
headers: {
|
|
403
|
+
'Content-Type': 'application/json'
|
|
404
|
+
},
|
|
405
|
+
body: JSON.stringify({ branch })
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
const result = await response.json()
|
|
409
|
+
if (result.success) {
|
|
410
|
+
ElMessage({
|
|
411
|
+
message: `已重置分支 ${branch} 到远程状态`,
|
|
412
|
+
type: 'success'
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
// 刷新状态和日志
|
|
416
|
+
fetchStatus()
|
|
417
|
+
fetchLog()
|
|
418
|
+
|
|
419
|
+
return true
|
|
420
|
+
} else {
|
|
421
|
+
ElMessage({
|
|
422
|
+
message: `重置分支失败: ${result.error}`,
|
|
423
|
+
type: 'error'
|
|
424
|
+
})
|
|
425
|
+
return false
|
|
426
|
+
}
|
|
427
|
+
} catch (error) {
|
|
428
|
+
ElMessage({
|
|
429
|
+
message: `重置分支失败: ${(error as Error).message}`,
|
|
430
|
+
type: 'error'
|
|
431
|
+
})
|
|
432
|
+
return false
|
|
433
|
+
} finally {
|
|
434
|
+
isResetting.value = false
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
// 状态
|
|
440
|
+
log,
|
|
441
|
+
status,
|
|
442
|
+
statusText,
|
|
443
|
+
fileList,
|
|
444
|
+
isLoadingLog,
|
|
445
|
+
isLoadingStatus,
|
|
446
|
+
isAddingFiles,
|
|
447
|
+
isResetting,
|
|
448
|
+
isCommiting,
|
|
449
|
+
isPushing,
|
|
450
|
+
|
|
451
|
+
// 方法
|
|
452
|
+
fetchLog,
|
|
453
|
+
fetchStatus,
|
|
454
|
+
fetchStatusPorcelain,
|
|
455
|
+
parseStatusPorcelain,
|
|
456
|
+
addToStage,
|
|
457
|
+
commitChanges,
|
|
458
|
+
pushToRemote,
|
|
459
|
+
addAndCommit,
|
|
460
|
+
addCommitAndPush,
|
|
461
|
+
resetHead,
|
|
462
|
+
resetToRemote
|
|
463
|
+
}
|
|
464
|
+
})
|