(uniDiff: string)
| 14 | * information to determine them (e.g. a hunk-only patch with no file headers). |
| 15 | */ |
| 16 | export function parsePatch(uniDiff: string): StructuredPatch[] { |
| 17 | const diffstr = uniDiff.split(/\n/), |
| 18 | list: Partial<StructuredPatch>[] = []; |
| 19 | let i = 0; |
| 20 | |
| 21 | // These helper functions identify line types that can appear between files |
| 22 | // in a multi-file patch. Keeping them in one place avoids subtle |
| 23 | // inconsistencies from having the same regexes duplicated in multiple places. |
| 24 | |
| 25 | // Matches `diff --git ...` lines specifically. |
| 26 | function isGitDiffHeader(line: string): boolean { |
| 27 | return (/^diff --git /).test(line); |
| 28 | } |
| 29 | |
| 30 | // Matches lines that denote the start of a new diff's section in a |
| 31 | // multi-file patch: `diff --git ...`, `Index: ...`, or `diff -r ...`. |
| 32 | function isDiffHeader(line: string): boolean { |
| 33 | return isGitDiffHeader(line) |
| 34 | || (/^Index:\s/).test(line) |
| 35 | || (/^diff(?: -r \w+)+\s/).test(line); |
| 36 | } |
| 37 | |
| 38 | // Matches `--- ...` and `+++ ...` file header lines. |
| 39 | function isFileHeader(line: string): boolean { |
| 40 | return (/^(---|\+\+\+)\s/).test(line); |
| 41 | } |
| 42 | |
| 43 | // Matches `@@ ...` hunk header lines. |
| 44 | function isHunkHeader(line: string): boolean { |
| 45 | return (/^@@\s/).test(line); |
| 46 | } |
| 47 | |
| 48 | function parseIndex() { |
| 49 | const index: Partial<StructuredPatch> = {}; |
| 50 | index.hunks = []; |
| 51 | list.push(index); |
| 52 | |
| 53 | // Parse diff metadata |
| 54 | let seenDiffHeader = false; |
| 55 | while (i < diffstr.length) { |
| 56 | const line = diffstr[i]; |
| 57 | |
| 58 | // File header (---, +++) or hunk header (@@) found; end parsing diff metadata |
| 59 | if (isFileHeader(line) || isHunkHeader(line)) { |
| 60 | break; |
| 61 | } |
| 62 | |
| 63 | // The next two branches handle recognized diff headers. Note that |
| 64 | // isDiffHeader deliberately does NOT match arbitrary `diff` |
| 65 | // commands like `diff -u -p -r1.1 -r1.2`, because in some |
| 66 | // formats (e.g. CVS diffs) such lines appear as metadata within |
| 67 | // a single file's header section, after an `Index:` line. See the |
| 68 | // diffx documentation (https://diffx.org) for examples. |
| 69 | // |
| 70 | // In both branches: if we've already seen a diff header for *this* |
| 71 | // file and now we encounter another one, it must belong to the |
| 72 | // next file, so break. |
| 73 |
no test coverage detected