zen-gitsync 2.0.5 → 2.0.7

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.
@@ -8,17 +8,25 @@ import config from '../../config.js';
8
8
  import chalk from 'chalk';
9
9
  import fs from 'fs/promises';
10
10
  import os from 'os';
11
- // import { Server } from 'socket.io';
11
+ import { Server } from 'socket.io';
12
+ import chokidar from 'chokidar';
12
13
  // import { exec } from 'child_process';
13
14
 
14
15
  const __filename = fileURLToPath(import.meta.url);
15
16
  const __dirname = path.dirname(__filename);
16
17
  const configManager = config; // 确保 configManager 可用
17
18
 
19
+ // 文件系统变动监控器
20
+ let watcher = null;
21
+ // 防抖计时器
22
+ let debounceTimer = null;
23
+ // 防抖延迟时间 (毫秒)
24
+ const DEBOUNCE_DELAY = 1000;
25
+
18
26
  async function startUIServer() {
19
27
  const app = express();
20
28
  const httpServer = createServer(app);
21
- // const io = new Server(httpServer);
29
+ const io = new Server(httpServer);
22
30
 
23
31
  // 添加全局中间件来解析JSON请求体
24
32
  app.use(express.json());
@@ -30,24 +38,6 @@ async function startUIServer() {
30
38
  next();
31
39
  });
32
40
 
33
- // // 启动前端Vue应用
34
- // const clientPath = path.join(__dirname, '../client');
35
- // console.log(`正在启动前端应用,路径: ${clientPath}`);
36
-
37
- // const vueProcess = exec('npm run dev', { cwd: clientPath }, (error) => {
38
- // if (error) {
39
- // console.error('启动前端应用失败:', error);
40
- // }
41
- // });
42
-
43
- // vueProcess.stdout.on('data', (data) => {
44
- // console.log(`前端输出: ${data}`);
45
- // });
46
-
47
- // vueProcess.stderr.on('data', (data) => {
48
- // console.error(`前端错误: ${data}`);
49
- // });
50
-
51
41
  // 静态文件服务
52
42
  app.use(express.static(path.join(__dirname, '../public')));
53
43
 
@@ -79,6 +69,74 @@ async function startUIServer() {
79
69
  }
80
70
  });
81
71
 
72
+ // 获取分支与远程的差异状态(领先/落后提交数)
73
+ app.get('/api/branch-status', async (req, res) => {
74
+ try {
75
+ // 先获取当前分支名
76
+ const { stdout: branchName } = await execGitCommand('git rev-parse --abbrev-ref HEAD');
77
+ const currentBranch = branchName.trim();
78
+
79
+ // 获取远程仓库名称
80
+ const { stdout: remoteName } = await execGitCommand('git remote');
81
+ const remote = remoteName.trim() || 'origin';
82
+
83
+ // 尝试获取当前分支的上游分支
84
+ let upstreamBranch = '';
85
+ let hasUpstream = false;
86
+
87
+ try {
88
+ const { stdout: upstream } = await execGitCommand(`git rev-parse --abbrev-ref ${currentBranch}@{upstream}`);
89
+ upstreamBranch = upstream.trim();
90
+ hasUpstream = true;
91
+ } catch (error) {
92
+ // 没有上游分支设置,这是正常的
93
+ console.log(`分支 ${currentBranch} 没有设置上游分支`);
94
+ hasUpstream = false;
95
+ }
96
+
97
+ // 如果分支没有上游设置,直接返回结果
98
+ if (!hasUpstream) {
99
+ return res.json({
100
+ branch: currentBranch,
101
+ remote,
102
+ hasUpstream: false,
103
+ ahead: 0,
104
+ behind: 0
105
+ });
106
+ }
107
+
108
+ // 获取领先和落后的提交数
109
+ const { stdout: statusCompare } = await execGitCommand(`git rev-list --count --left-right ${currentBranch}...${upstreamBranch}`);
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
+ }
125
+
126
+ res.json({
127
+ branch: currentBranch,
128
+ remote,
129
+ upstreamBranch,
130
+ hasUpstream: true,
131
+ ahead,
132
+ behind
133
+ });
134
+ } catch (error) {
135
+ console.error("获取分支状态失败:", error);
136
+ res.status(500).json({ error: error.message });
137
+ }
138
+ });
139
+
82
140
  // 获取所有分支
83
141
  app.get('/api/branches', async (req, res) => {
84
142
  try {
@@ -198,7 +256,6 @@ async function startUIServer() {
198
256
  // 新增切换工作目录接口
199
257
  app.post('/api/change_directory', async (req, res) => {
200
258
  try {
201
-
202
259
  const { path } = req.body;
203
260
 
204
261
  if (!path) {
@@ -212,12 +269,22 @@ async function startUIServer() {
212
269
  // 检查新目录是否是Git仓库
213
270
  try {
214
271
  await execGitCommand('git rev-parse --is-inside-work-tree');
272
+
273
+ // 初始化文件监控
274
+ initFileSystemWatcher();
275
+
215
276
  res.json({
216
277
  success: true,
217
278
  directory: newDirectory,
218
279
  isGitRepo: true
219
280
  });
220
281
  } catch (error) {
282
+ // 不是Git仓库,停止监控
283
+ if (watcher) {
284
+ watcher.close().catch(err => console.error('关闭监控器失败:', err));
285
+ watcher = null;
286
+ }
287
+
221
288
  res.json({
222
289
  success: true,
223
290
  directory: newDirectory,
@@ -482,6 +549,46 @@ async function startUIServer() {
482
549
  }
483
550
  });
484
551
 
552
+ // 添加单个文件到暂存区
553
+ app.post('/api/add-file', async (req, res) => {
554
+ try {
555
+ const { filePath } = req.body;
556
+
557
+ if (!filePath) {
558
+ return res.status(400).json({
559
+ success: false,
560
+ error: '缺少文件路径参数'
561
+ });
562
+ }
563
+
564
+ // 执行 git add 命令添加特定文件
565
+ await execGitCommand(`git add "${filePath}"`);
566
+ res.json({ success: true });
567
+ } catch (error) {
568
+ res.status(500).json({ success: false, error: error.message });
569
+ }
570
+ });
571
+
572
+ // 从暂存区移除单个文件
573
+ app.post('/api/unstage-file', async (req, res) => {
574
+ try {
575
+ const { filePath } = req.body;
576
+
577
+ if (!filePath) {
578
+ return res.status(400).json({
579
+ success: false,
580
+ error: '缺少文件路径参数'
581
+ });
582
+ }
583
+
584
+ // 执行 git reset HEAD 命令移除特定文件的暂存
585
+ await execGitCommand(`git reset HEAD -- "${filePath}"`);
586
+ res.json({ success: true });
587
+ } catch (error) {
588
+ res.status(500).json({ success: false, error: error.message });
589
+ }
590
+ });
591
+
485
592
  // 推送更改
486
593
  app.post('/api/push', async (req, res) => {
487
594
  try {
@@ -495,29 +602,132 @@ async function startUIServer() {
495
602
  // 获取日志
496
603
  app.get('/api/log', async (req, res) => {
497
604
  try {
498
- // 获取请求参数中的数量限制,默认为30
499
- const limit = req.query.all === 'true' ? '' : '-n 30';
605
+ // 获取分页参数
606
+ const page = parseInt(req.query.page) || 1;
607
+ const limit = parseInt(req.query.limit) || 100;
608
+ const skip = (page - 1) * limit;
609
+
610
+ // 获取筛选参数
611
+ const author = req.query.author ? req.query.author.split(',') : [];
612
+ const message = req.query.message || '';
613
+ const dateFrom = req.query.dateFrom || '';
614
+ const dateTo = req.query.dateTo || '';
500
615
 
501
- // graph参数保留但不做特殊处理,避免前端代码重复调用API
502
- // 由前端统一使用该接口
616
+ // 构建Git命令选项
617
+ let commandOptions = [];
503
618
 
504
- // 修改 git log 命令,添加 %ae 参数来获取作者邮箱
505
- const { stdout } = await execGitCommand(`git log --all --pretty=format:"%h|%an|%ae|%ad|%s|%D" --date=short ${limit}`);
506
- const logs = stdout.split('\n').map(line => {
507
- const [hash, author, email, date, message, refs] = line.split('|');
619
+ // 作者筛选(支持多作者,使用正则表达式OR操作)
620
+ if (author.length > 0) {
621
+ // 过滤掉空作者
622
+ const validAuthors = author.filter(a => a.trim() !== '');
508
623
 
509
- // 从引用信息中提取分支名称
510
- let branch = null;
511
- if (refs) {
512
- // 提取所有引用信息,而不仅仅是第一个匹配
513
- branch = refs.trim();
624
+ if (validAuthors.length === 1) {
625
+ // 单个作者,直接使用--author
626
+ commandOptions.push(`--author="${validAuthors[0].trim()}"`);
627
+ } else if (validAuthors.length > 1) {
628
+ // 多个作者,使用正则表达式OR条件
629
+ // 只转义OR运算符,保持其他内容不变
630
+ const authorPattern = validAuthors
631
+ .map(a => a.trim())
632
+ .join('\\|'); // 在JavaScript字符串中\\|会变成\|,在shell中会成为|
633
+
634
+ commandOptions.push(`--author="${authorPattern}"`);
514
635
  }
515
-
516
- return { hash, author, email, date, message, branch };
636
+ }
637
+
638
+ // 日期范围筛选
639
+ if (dateFrom && dateTo) {
640
+ commandOptions.push(`--after="${dateFrom}" --before="${dateTo} 23:59:59"`);
641
+ } else if (dateFrom) {
642
+ commandOptions.push(`--after="${dateFrom}"`);
643
+ } else if (dateTo) {
644
+ commandOptions.push(`--before="${dateTo} 23:59:59"`);
645
+ }
646
+
647
+ // 提交信息筛选
648
+ if (message) {
649
+ commandOptions.push(`--grep="${message}"`);
650
+ }
651
+
652
+ // 如果all=true,则不使用限制,否则按页码和limit精确获取
653
+ // 修复:只获取当前页的数据,而不是累计所有之前页的数据
654
+ const limitOption = req.query.all === 'true' ? '' : `-n ${limit} --skip=${skip}`;
655
+
656
+ // 合并所有命令选项
657
+ const options = [...commandOptions, limitOption].filter(Boolean).join(' ');
658
+
659
+ console.log(`执行Git命令: git log --all --pretty=format:"%H|%an|%ae|%ad|%B|%D" --date=short ${options}`);
660
+
661
+ // 使用 git log 命令获取提交历史
662
+ let { stdout: logOutput } = await execGitCommand(
663
+ `git log --all --pretty=format:"%H|%an|%ae|%ad|%B|%D" --date=short ${options}`
664
+ );
665
+
666
+ // 获取总提交数量(考虑筛选条件)
667
+ let totalCommits = 0;
668
+ try {
669
+ // 构建计数命令,包含相同的筛选条件
670
+ // 对于作者筛选,使用 rev-list 并手动计数,避免 --count 与复杂作者筛选结合可能的问题
671
+ if (author.length > 1) {
672
+ // 多作者情况,使用 wc -l 手动计数
673
+ const countCommand = `git rev-list --all ${commandOptions.join(' ')}`;
674
+ console.log(`执行计数命令(多作者): ${countCommand}`);
675
+
676
+ const { stdout: countOutput } = await execGitCommand(countCommand);
677
+ // 计算行数作为提交数
678
+ totalCommits = countOutput.trim().split('\n').filter(line => line.trim() !== '').length;
679
+ } else {
680
+ // 单作者或无作者筛选,可以直接使用 --count
681
+ const countCommand = `git rev-list --all --count ${commandOptions.join(' ')}`;
682
+ console.log(`执行计数命令(单作者): ${countCommand}`);
683
+
684
+ const { stdout: countOutput } = await execGitCommand(countCommand);
685
+ totalCommits = parseInt(countOutput.trim());
686
+ }
687
+ } catch (error) {
688
+ console.error('获取提交总数失败:', error);
689
+ totalCommits = 0;
690
+ }
691
+
692
+ // 替换提交记录之间的换行符
693
+ logOutput = logOutput.replace(/\n(?=[a-f0-9]{40}\|)/g, "<<<RECORD_SEPARATOR>>>");
694
+
695
+ // 按分隔符拆分日志条目
696
+ const logEntries = logOutput.split("<<<RECORD_SEPARATOR>>>");
697
+
698
+ // 处理每个日志条目
699
+ const data = logEntries.map(entry => {
700
+ const parts = entry.split('|');
701
+ if (parts.length >= 5) {
702
+ return {
703
+ hash: parts[0],
704
+ author: parts[1],
705
+ email: parts[2],
706
+ date: parts[3],
707
+ message: parts[4],
708
+ branch: parts[5] || ''
709
+ };
710
+ }
711
+ return null;
712
+ }).filter(item => item !== null);
713
+
714
+ // 计算是否有更多数据
715
+ // 修复:使用总记录数和已获取记录总数来判断
716
+ const hasMore = req.query.all === 'true' ? false : (skip + data.length < totalCommits);
717
+
718
+ console.log(`分页查询 - 页码: ${page}, 每页数量: ${limit}, 跳过: ${skip}, 总数: ${totalCommits}, 返回数量: ${data.length}, 是否有更多: ${hasMore}`);
719
+
720
+ // 返回提交历史数据,包括是否有更多数据的标志
721
+ res.json({
722
+ data: data,
723
+ total: totalCommits,
724
+ page: page,
725
+ limit: limit,
726
+ hasMore: hasMore
517
727
  });
518
- res.json(logs);
519
728
  } catch (error) {
520
- res.status(500).json({ error: error.message });
729
+ console.error('获取Git日志失败:', error);
730
+ res.status(500).json({ error: '获取日志失败: ' + error.message });
521
731
  }
522
732
  });
523
733
 
@@ -539,6 +749,27 @@ async function startUIServer() {
539
749
  }
540
750
  });
541
751
 
752
+ // 获取文件内容 (用于未跟踪文件)
753
+ app.get('/api/file-content', async (req, res) => {
754
+ try {
755
+ const filePath = req.query.file;
756
+
757
+ if (!filePath) {
758
+ return res.status(400).json({ error: '缺少文件路径参数' });
759
+ }
760
+
761
+ try {
762
+ // 读取文件内容
763
+ const content = await fs.readFile(filePath, 'utf8');
764
+ res.json({ success: true, content });
765
+ } catch (readError) {
766
+ res.status(500).json({ success: false, error: `无法读取文件: ${readError.message}` });
767
+ }
768
+ } catch (error) {
769
+ res.status(500).json({ error: error.message });
770
+ }
771
+ });
772
+
542
773
  // 撤回文件修改
543
774
  app.post('/api/revert_file', async (req, res) => {
544
775
  try {
@@ -630,6 +861,137 @@ async function startUIServer() {
630
861
  }
631
862
  });
632
863
 
864
+ // 获取提交的文件列表
865
+ app.get('/api/commit-files', async (req, res) => {
866
+ try {
867
+ const hash = req.query.hash;
868
+
869
+ if (!hash) {
870
+ return res.status(400).json({
871
+ success: false,
872
+ error: '缺少提交哈希参数'
873
+ });
874
+ }
875
+
876
+ console.log(`获取提交文件列表: hash=${hash}`);
877
+
878
+ // 执行命令获取提交中修改的文件列表
879
+ const { stdout } = await execGitCommand(`git show --name-only --format="" ${hash}`);
880
+
881
+ // 将输出按行分割,并过滤掉空行
882
+ const files = stdout.split('\n').filter(line => line.trim());
883
+ console.log(`找到${files.length}个文件:`, files);
884
+
885
+ res.json({
886
+ success: true,
887
+ files
888
+ });
889
+ } catch (error) {
890
+ console.error('获取提交文件列表失败:', error);
891
+ res.status(500).json({
892
+ success: false,
893
+ error: `获取提交文件列表失败: ${error.message}`
894
+ });
895
+ }
896
+ });
897
+
898
+ // 获取提交中特定文件的差异
899
+ app.get('/api/commit-file-diff', async (req, res) => {
900
+ try {
901
+ const hash = req.query.hash;
902
+ const filePath = req.query.file;
903
+
904
+ if (!hash || !filePath) {
905
+ return res.status(400).json({
906
+ success: false,
907
+ error: '缺少必要参数'
908
+ });
909
+ }
910
+
911
+ console.log(`获取提交文件差异: hash=${hash}, file=${filePath}`);
912
+
913
+ // 执行命令获取文件差异,-p显示补丁,限定文件路径
914
+ const { stdout } = await execGitCommand(`git show ${hash} -- "${filePath}"`);
915
+
916
+ console.log(`获取到差异内容,长度: ${stdout.length}`);
917
+ // 如果差异内容太长,只打印前100个字符
918
+ if (stdout.length > 100) {
919
+ console.log(`差异内容预览: ${stdout.substring(0, 100)}...`);
920
+ }
921
+
922
+ res.json({
923
+ success: true,
924
+ diff: stdout
925
+ });
926
+ } catch (error) {
927
+ console.error('获取提交文件差异失败:', error);
928
+ res.status(500).json({
929
+ success: false,
930
+ error: `获取提交文件差异失败: ${error.message}`
931
+ });
932
+ }
933
+ });
934
+
935
+ // 撤销某个提交 (revert)
936
+ app.post('/api/revert-commit', async (req, res) => {
937
+ try {
938
+ const { hash } = req.body;
939
+
940
+ if (!hash) {
941
+ return res.status(400).json({
942
+ success: false,
943
+ error: '缺少提交哈希参数'
944
+ });
945
+ }
946
+
947
+ console.log(`执行撤销提交操作: hash=${hash}`);
948
+
949
+ // 执行git revert命令
950
+ await execGitCommand(`git revert --no-edit ${hash}`);
951
+
952
+ res.json({
953
+ success: true,
954
+ message: `已成功撤销提交 ${hash}`
955
+ });
956
+ } catch (error) {
957
+ console.error('撤销提交失败:', error);
958
+ res.status(500).json({
959
+ success: false,
960
+ error: `撤销提交失败: ${error.message}`
961
+ });
962
+ }
963
+ });
964
+
965
+ // Cherry-pick某个提交
966
+ app.post('/api/cherry-pick-commit', async (req, res) => {
967
+ try {
968
+ const { hash } = req.body;
969
+
970
+ if (!hash) {
971
+ return res.status(400).json({
972
+ success: false,
973
+ error: '缺少提交哈希参数'
974
+ });
975
+ }
976
+
977
+ console.log(`执行Cherry-pick操作: hash=${hash}`);
978
+
979
+ // 执行git cherry-pick命令
980
+ await execGitCommand(`git cherry-pick ${hash}`);
981
+
982
+ res.json({
983
+ success: true,
984
+ message: `已成功Cherry-pick提交 ${hash}`
985
+ });
986
+ } catch (error) {
987
+ console.error('Cherry-pick提交失败:', error);
988
+ res.status(500).json({
989
+ success: false,
990
+ error: `Cherry-pick提交失败: ${error.message}`
991
+ });
992
+ }
993
+ });
994
+
633
995
  // 添加清理Git锁定文件的接口
634
996
  app.post('/api/remove-lock', async (req, res) => {
635
997
  try {
@@ -714,25 +1076,153 @@ async function startUIServer() {
714
1076
  }
715
1077
  });
716
1078
 
1079
+ // 获取所有作者列表
1080
+ app.get('/api/authors', async (req, res) => {
1081
+ try {
1082
+ // 使用git命令获取所有提交者,不依赖Unix命令
1083
+ const { stdout } = await execGitCommand('git log --format="%an"');
1084
+
1085
+ // 将结果按行分割并过滤空行
1086
+ const lines = stdout.split('\n').filter(author => author.trim() !== '');
1087
+
1088
+ // 手动去重,不依赖Unix的uniq命令
1089
+ const uniqueAuthors = Array.from(new Set(lines)).sort();
1090
+
1091
+ // 控制台输出一下搜索示例,方便调试
1092
+ if (uniqueAuthors.length > 1) {
1093
+ const searchExample = uniqueAuthors.slice(0, 2).join('|');
1094
+ console.log(`多作者搜索示例: git log --author="${searchExample}"`);
1095
+ }
1096
+
1097
+ res.json({
1098
+ success: true,
1099
+ authors: uniqueAuthors
1100
+ });
1101
+ } catch (error) {
1102
+ console.error('获取作者列表失败:', error);
1103
+ res.status(500).json({
1104
+ success: false,
1105
+ error: '获取作者列表失败: ' + error.message
1106
+ });
1107
+ }
1108
+ });
1109
+
717
1110
  // Socket.io 实时更新
718
- // io.on('connection', (socket) => {
719
- // console.log('客户端已连接');
1111
+ io.on('connection', (socket) => {
1112
+ console.log('客户端已连接:', socket.id);
720
1113
 
721
- // // 定期发送状态更新
722
- // const statusInterval = setInterval(async () => {
723
- // try {
724
- // const { stdout } = await execGitCommand('git status');
725
- // socket.emit('status_update', { status: stdout });
726
- // } catch (error) {
727
- // console.error('状态更新错误:', error);
728
- // }
729
- // }, 5000);
1114
+ // 当客户端连接时,立即发送一次Git状态
1115
+ getAndBroadcastStatus();
1116
+
1117
+ // 客户端可以请求开始/停止监控
1118
+ socket.on('start_monitoring', () => {
1119
+ if (!watcher) {
1120
+ initFileSystemWatcher();
1121
+ socket.emit('monitoring_status', { active: true });
1122
+ }
1123
+ });
730
1124
 
731
- // socket.on('disconnect', () => {
732
- // clearInterval(statusInterval);
733
- // console.log('客户端已断开连接');
734
- // });
735
- // });
1125
+ socket.on('stop_monitoring', () => {
1126
+ if (watcher) {
1127
+ watcher.close().catch(err => console.error('关闭监控器失败:', err));
1128
+ watcher = null;
1129
+ socket.emit('monitoring_status', { active: false });
1130
+ }
1131
+ });
1132
+
1133
+ // 客户端断开连接
1134
+ socket.on('disconnect', () => {
1135
+ console.log('客户端已断开连接:', socket.id);
1136
+ });
1137
+ });
1138
+
1139
+ // 初始化文件系统监控
1140
+ function initFileSystemWatcher() {
1141
+ // 停止已有的监控器
1142
+ if (watcher) {
1143
+ watcher.close().catch(err => console.error('关闭旧监控器失败:', err));
1144
+ }
1145
+
1146
+ try {
1147
+ // 获取当前工作目录
1148
+ const currentDir = process.cwd();
1149
+
1150
+ console.log(`初始化文件系统监控器,路径: ${currentDir}`);
1151
+
1152
+ // 检查是否是Git仓库
1153
+ try {
1154
+ execGitCommand('git rev-parse --is-inside-work-tree');
1155
+ } catch (error) {
1156
+ console.log('当前目录不是Git仓库,不启动监控');
1157
+ return;
1158
+ }
1159
+
1160
+ // 使用chokidar监控文件变动
1161
+ watcher = chokidar.watch(currentDir, {
1162
+ ignored: [
1163
+ /(^|[\/\\])\../, // 忽略.开头的文件和目录
1164
+ '**/node_modules/**', // 忽略node_modules
1165
+ '**/.git/**', // 忽略.git目录
1166
+ ],
1167
+ persistent: true,
1168
+ ignoreInitial: true, // 忽略初始扫描时的文件
1169
+ awaitWriteFinish: {
1170
+ stabilityThreshold: 300, // 等待文件写入完成的时间
1171
+ pollInterval: 100 // 轮询间隔
1172
+ }
1173
+ });
1174
+
1175
+ // 合并所有变动事件到一个处理程序
1176
+ const events = ['add', 'change', 'unlink'];
1177
+ events.forEach(event => {
1178
+ watcher.on(event, path => {
1179
+ console.log(`检测到文件变动 [${event}]: ${path}`);
1180
+ debouncedNotifyChanges();
1181
+ });
1182
+ });
1183
+
1184
+ watcher.on('error', error => {
1185
+ console.error('文件监控错误:', error);
1186
+ });
1187
+
1188
+ console.log('文件系统监控器已启动');
1189
+ } catch (error) {
1190
+ console.error('启动文件监控失败:', error);
1191
+ }
1192
+ }
1193
+
1194
+ // 获取并广播Git状态
1195
+ async function getAndBroadcastStatus() {
1196
+ try {
1197
+ // 获取常规状态
1198
+ const { stdout: statusOutput } = await execGitCommand('git status');
1199
+
1200
+ // 获取porcelain格式状态
1201
+ const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain');
1202
+
1203
+ // 广播到所有连接的客户端
1204
+ io.emit('git_status_update', {
1205
+ status: statusOutput,
1206
+ porcelain: porcelainOutput,
1207
+ timestamp: new Date().toISOString()
1208
+ });
1209
+
1210
+ console.log('已广播Git状态更新');
1211
+ } catch (error) {
1212
+ console.error('获取或广播Git状态失败:', error);
1213
+ }
1214
+ }
1215
+
1216
+ // 防抖处理函数
1217
+ function debouncedNotifyChanges() {
1218
+ if (debounceTimer) {
1219
+ clearTimeout(debounceTimer);
1220
+ }
1221
+
1222
+ debounceTimer = setTimeout(() => {
1223
+ getAndBroadcastStatus();
1224
+ }, DEBOUNCE_DELAY);
1225
+ }
736
1226
 
737
1227
  // 启动服务器
738
1228
  const PORT = 3000;
@@ -742,6 +1232,10 @@ async function startUIServer() {
742
1232
  console.log(chalk.green(` 访问地址: http://localhost:${PORT}`));
743
1233
  console.log(chalk.green(` 启动时间: ${new Date().toLocaleString()}`));
744
1234
  console.log(chalk.green('======================================'));
1235
+
1236
+ // 启动文件监控
1237
+ initFileSystemWatcher();
1238
+
745
1239
  open(`http://localhost:${PORT}`);
746
1240
  }).on('error', async (err) => {
747
1241
  if (err.code === 'EADDRINUSE') {
@@ -756,6 +1250,10 @@ async function startUIServer() {
756
1250
  console.log(chalk.green(` 访问地址: http://localhost:${newPort}`));
757
1251
  console.log(chalk.green(` 启动时间: ${new Date().toLocaleString()}`));
758
1252
  console.log(chalk.green('======================================'));
1253
+
1254
+ // 启动文件监控
1255
+ initFileSystemWatcher();
1256
+
759
1257
  open(`http://localhost:${newPort}`);
760
1258
  resolve();
761
1259
  }).on('error', (e) => {