Files
ForgeFlow/src/shared/git-status.cjs
T
2026-07-24 20:29:23 +02:00

94 lines
3.0 KiB
JavaScript

'use strict';
function parseBranchHeader(line, branch) {
if (line.startsWith('# branch.oid ')) branch.oid = line.slice(13).trim();
if (line.startsWith('# branch.head ')) branch.head = line.slice(14).trim();
if (line.startsWith('# branch.upstream ')) branch.upstream = line.slice(18).trim();
if (line.startsWith('# branch.ab ')) {
const match = line.match(/\+(\d+)\s+-(\d+)/);
if (match) {
branch.ahead = Number(match[1]);
branch.behind = Number(match[2]);
}
}
}
function statusLabel(code) {
const map = {
M: 'modified', A: 'added', D: 'deleted', R: 'renamed', C: 'copied',
U: 'conflict', T: 'type-changed', '?': 'untracked', '!': 'ignored', '.': 'clean', ' ': 'clean'
};
return map[code] || 'changed';
}
function buildFile(path, originalPath, xy, kind) {
const indexCode = xy?.[0] || '.';
const worktreeCode = xy?.[1] || '.';
const conflict = kind === 'u' || indexCode === 'U' || worktreeCode === 'U';
const untracked = kind === '?';
return {
path,
originalPath: originalPath || null,
indexCode,
worktreeCode,
staged: !untracked && indexCode !== '.' && indexCode !== ' ',
unstaged: untracked || (worktreeCode !== '.' && worktreeCode !== ' '),
untracked,
conflict,
status: conflict ? 'conflict' : untracked ? 'untracked' : statusLabel(worktreeCode !== '.' ? worktreeCode : indexCode)
};
}
function parsePorcelainV2(output) {
const branch = { oid: null, head: null, upstream: null, ahead: 0, behind: 0 };
const files = [];
const entries = String(output || '').split('\0');
for (let index = 0; index < entries.length; index += 1) {
const entry = entries[index];
if (!entry) continue;
if (entry.startsWith('# ')) {
parseBranchHeader(entry, branch);
continue;
}
const kind = entry[0];
if (kind === '1') {
const parts = entry.split(' ');
const xy = parts[1];
const path = parts.slice(8).join(' ');
files.push(buildFile(path, null, xy, kind));
} else if (kind === '2') {
const parts = entry.split(' ');
const xy = parts[1];
const path = parts.slice(9).join(' ');
const originalPath = entries[index + 1] || null;
index += 1;
files.push(buildFile(path, originalPath, xy, kind));
} else if (kind === 'u') {
const parts = entry.split(' ');
const xy = parts[1];
const path = parts.slice(10).join(' ');
files.push(buildFile(path, null, xy, kind));
} else if (kind === '?' || kind === '!') {
const path = entry.slice(2);
if (kind === '?') files.push(buildFile(path, null, '??', kind));
}
}
return {
branch,
files,
counts: {
changed: files.length,
staged: files.filter((file) => file.staged).length,
unstaged: files.filter((file) => file.unstaged).length,
conflicts: files.filter((file) => file.conflict).length,
untracked: files.filter((file) => file.untracked).length
},
clean: files.length === 0
};
}
module.exports = { parsePorcelainV2, statusLabel };