94 lines
3.0 KiB
JavaScript
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 };
|