UNPKG

parse-git-patch

Version:
149 lines (148 loc) 4.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const hashRegex = /^From (\S*)/; const authorRegex = /^From:\s?([^<].*[^>])?\s+(<(.*)>)?/; const fileNameRegex = /^diff --git "?a\/(.*)"?\s*"?b\/(.*)"?/; const fileLinesRegex = /^@@ -([0-9]*),?\S* \+([0-9]*),?/; const similarityIndexRegex = /^similarity index /; const addedFileModeRegex = /^new file mode /; const deletedFileModeRegex = /^deleted file mode /; function parseGitPatch(patch) { if (typeof patch !== 'string') { throw new Error('Expected first argument (patch) to be a string'); } const lines = patch.split('\n'); const gitPatchMetaInfo = splitMetaInfo(patch, lines); if (!gitPatchMetaInfo) return null; const parsedPatch = { ...gitPatchMetaInfo, files: [], }; splitIntoParts(lines, 'diff --git').forEach(diff => { const fileNameLine = diff.shift(); if (!fileNameLine) return; const match3 = fileNameLine.match(fileNameRegex); if (!match3) return; const [, a, b] = match3; const metaLine = diff.shift(); if (!metaLine) return; const fileData = { added: false, deleted: false, beforeName: a.trim(), afterName: b.trim(), modifiedLines: [], }; parsedPatch.files.push(fileData); if (addedFileModeRegex.test(metaLine)) { fileData.added = true; } if (deletedFileModeRegex.test(metaLine)) { fileData.deleted = true; } if (similarityIndexRegex.test(metaLine)) { return; } splitIntoParts(diff, '@@ ').forEach(lines => { const fileLinesLine = lines.shift(); if (!fileLinesLine) return; const match4 = fileLinesLine.match(fileLinesRegex); if (!match4) return; const [, a, b] = match4; let nA = parseInt(a) - 1; let nB = parseInt(b) - 1; lines.forEach(line => { nA++; nB++; if (line === '-- ' || line === '--') { return; } if (line.startsWith('+')) { nA--; fileData.modifiedLines.push({ added: true, lineNumber: nB, line: line.substring(1), }); } else if (line.startsWith('-')) { nB--; fileData.modifiedLines.push({ added: false, lineNumber: nA, line: line.substring(1), }); } }); }); }); return parsedPatch; } function splitMetaInfo(patch, lines) { // Compatible with git output if (!/^From/g.test(patch)) { return {}; } const hashLine = lines.shift(); if (!hashLine) return null; const match1 = hashLine.match(hashRegex); if (!match1) return null; const [, hash] = match1; let authorLine = lines.shift(); // Parsing of long names while (lines[0].startsWith(' ')) { authorLine += ` ${lines.shift()}`; } if (!authorLine) return null; const match2 = authorLine.match(authorRegex); if (!match2) return null; const [, authorName, , authorEmail] = match2; const dateLine = lines.shift(); if (!dateLine) return null; const [, date] = dateLine.split('Date: '); const messageLine = lines.shift(); if (!messageLine) return null; const [, message] = messageLine.split('Subject: '); return { hash, authorName: formatAuthorName(authorName), authorEmail, date, message, }; } function splitIntoParts(lines, separator) { const parts = []; let currentPart; lines.forEach(line => { if (line.startsWith(separator)) { if (currentPart) { parts.push(currentPart); } currentPart = [line]; } else if (currentPart) { currentPart.push(line); } }); if (currentPart) { parts.push(currentPart); } return parts; } function formatAuthorName(name) { return (name.startsWith('"') && name.endsWith('"') ? name.slice(1, -1) : name).trim(); } exports.default = parseGitPatch;