vibetunnel/web/src/server/utils/git-status.ts
Peter Steinberger acdc4f22a8 Consolidate duplicate git status implementations
- Create shared git-status utility to avoid code duplication
- Update git-watcher.ts to use the shared utility
- Update sessions.ts to use the shared utility
- Remove duplicate getDetailedGitStatus implementations
- Fix import and linting issues

The shared utility provides a single source of truth for parsing git status output,
making the codebase more maintainable.
2025-07-28 02:48:01 +02:00

108 lines
2.7 KiB
TypeScript

/**
* Shared Git Status Utilities
*
* Provides a single implementation for parsing git status output
* to avoid duplication across the codebase.
*/
import { execFile } from 'child_process';
import { promisify } from 'util';
const execFileAsync = promisify(execFile);
export interface GitStatusCounts {
modified: number;
untracked: number;
staged: number;
deleted: number;
ahead: number;
behind: number;
}
/**
* Get detailed git status including file counts and ahead/behind info
* @param workingDir The directory to check git status in
* @returns Git status counts or null if not a git repository
*/
export async function getDetailedGitStatus(workingDir: string): Promise<GitStatusCounts> {
try {
const { stdout: statusOutput } = await execFileAsync(
'git',
['status', '--porcelain=v1', '--branch'],
{
cwd: workingDir,
timeout: 5000,
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
}
);
const lines = statusOutput.trim().split('\n');
const branchLine = lines[0];
let aheadCount = 0;
let behindCount = 0;
let modifiedCount = 0;
let untrackedCount = 0;
let stagedCount = 0;
let deletedCount = 0;
// Parse branch line for ahead/behind info
if (branchLine?.startsWith('##')) {
const aheadMatch = branchLine.match(/\[ahead (\d+)/);
const behindMatch = branchLine.match(/behind (\d+)/);
if (aheadMatch) {
aheadCount = Number.parseInt(aheadMatch[1], 10);
}
if (behindMatch) {
behindCount = Number.parseInt(behindMatch[1], 10);
}
}
// Parse file statuses
for (let i = 1; i < lines.length; i++) {
const line = lines[i];
if (!line || line.length < 2) continue;
const indexStatus = line[0];
const workingStatus = line[1];
// Staged files (changes in index)
if (indexStatus !== ' ' && indexStatus !== '?') {
stagedCount++;
}
// Working directory changes
if (workingStatus === 'M') {
modifiedCount++;
} else if (workingStatus === 'D' && indexStatus === ' ') {
// Deleted in working tree but not staged
deletedCount++;
}
// Untracked files
if (indexStatus === '?' && workingStatus === '?') {
untrackedCount++;
}
}
return {
modified: modifiedCount,
untracked: untrackedCount,
staged: stagedCount,
deleted: deletedCount,
ahead: aheadCount,
behind: behindCount,
};
} catch (_error) {
// Not a git repository or git command failed
return {
modified: 0,
untracked: 0,
staged: 0,
deleted: 0,
ahead: 0,
behind: 0,
};
}
}