(files: GitHubFileEntry[])
| 94 | * contents through the contents API on demand. |
| 95 | */ |
| 96 | export function reconstructGhPatch(files: GitHubFileEntry[]): string { |
| 97 | const parts: string[] = []; |
| 98 | |
| 99 | for (const f of files) { |
| 100 | const oldPath = f.previous_filename ?? f.filename; |
| 101 | const newPath = f.filename; |
| 102 | const isNew = f.status === "added"; |
| 103 | const isDeleted = f.status === "removed"; |
| 104 | |
| 105 | let header = `diff --git ${headerPathToken("a", oldPath)} ${headerPathToken("b", newPath)}`; |
| 106 | if (f.status === "renamed" || f.status === "copied") { |
| 107 | // Git always prints a similarity score before rename/copy lines, and |
| 108 | // diff parsers (e.g. Pierre's) key rename classification off it — |
| 109 | // without the line a rename renders as a plain change with no old path. |
| 110 | // The files API doesn't expose the score: a patch-less entry is by |
| 111 | // definition a 100% match; for patched entries emit a synthetic <100% |
| 112 | // value (consumers only branch on 100% vs not). |
| 113 | header += f.patch ? "\nsimilarity index 99%" : "\nsimilarity index 100%"; |
| 114 | header += f.status === "renamed" |
| 115 | ? `\nrename from ${metadataPathToken(oldPath)}\nrename to ${metadataPathToken(newPath)}` |
| 116 | : `\ncopy from ${metadataPathToken(oldPath)}\ncopy to ${metadataPathToken(newPath)}`; |
| 117 | } |
| 118 | if (isNew) { |
| 119 | header += "\nnew file mode 100644"; |
| 120 | } |
| 121 | if (isDeleted) { |
| 122 | header += "\ndeleted file mode 100644"; |
| 123 | } |
| 124 | |
| 125 | if (f.patch) { |
| 126 | const aToken = isNew ? "/dev/null" : headerPathToken("a", oldPath); |
| 127 | const bToken = isDeleted ? "/dev/null" : headerPathToken("b", newPath); |
| 128 | const body = f.patch.endsWith("\n") ? f.patch : `${f.patch}\n`; |
| 129 | parts.push(`${header}\n--- ${aToken}\n+++ ${bToken}\n${body}`); |
| 130 | } else { |
| 131 | parts.push(`${header}\n`); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | return parts.join(""); |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * True when a files-API entry should carry a patch but doesn't. Patch-less |
no test coverage detected