'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 };