/** * Git Handler * Handles git file watching and client notifications for the sync server. */ import { watch, type FSWatcher } from 'fs' import { join } from 'path' // Git watcher state let gitWatcher: FSWatcher | null = null let debounceTimer: ReturnType | null = null const DEBOUNCE_MS = 300 // Files to ignore const IGNORE_PATTERNS = [ 'FETCH_HEAD', 'gc.log', '.lock', 'COMMIT_EDITMSG', 'ORIG_HEAD', 'gitk.cache', 'sourcetreeconfig', '.DS_Store', 'fsmonitor', 'packed-refs' ] /** * Set up git directory watcher */ export function setupGitWatcher(workingDir: string, broadcast: (message: string, filter?: (ws: any) => boolean) => void) { const gitDir = join(workingDir, '.git') try { gitWatcher = watch(gitDir, { recursive: true }, (_, filename) => { if (!filename) return // Ignore noisy files if (IGNORE_PATTERNS.some(p => filename.includes(p))) return // Only meaningful changes const isRelevant = filename.includes('refs/') || filename === 'HEAD' || filename === 'index' || filename.includes('objects/') if (!isRelevant) return // Debounce if (debounceTimer) clearTimeout(debounceTimer) debounceTimer = setTimeout(() => { console.log(`[Git] Change: ${filename}`) broadcast(JSON.stringify({ type: 'git-change', timestamp: Date.now() })) }, DEBOUNCE_MS) }) console.log(`[Git] Watching ${gitDir}`) } catch (e: any) { console.error(`[Git] Watch failed: ${e.message}`) } } /** * Handle new git client connection */ export function handleGitClient(ws: any) { ws.send(JSON.stringify({ type: 'connected' })) } /** * Cleanup git watcher */ export function cleanupGitWatcher() { if (gitWatcher) { gitWatcher.close() gitWatcher = null } if (debounceTimer) { clearTimeout(debounceTimer) debounceTimer = null } }