()
| 225 | |
| 226 | // ---------- main |
| 227 | async function main() { |
| 228 | const { range, paths } = parseArgs(); |
| 229 | |
| 230 | const shas = revList(range, paths); |
| 231 | if (!shas.length) die("No commits in the specified range/path."); |
| 232 | |
| 233 | // 1) Commit info + primary author counts |
| 234 | const { commitInfo, authorCount } = await fetchCommitsByOidBatch(shas); |
| 235 | |
| 236 | // 2) Collect authors and co-authors |
| 237 | const loginBestName = new Map(); // login -> name hint |
| 238 | const pool = []; // [{ name, email }] to resolve (co-authors + primaries with missing login) |
| 239 | |
| 240 | for (const sha of shas) { |
| 241 | const info = commitInfo.get(sha); |
| 242 | if (!info) continue; |
| 243 | const { login, name, email, message } = info; |
| 244 | |
| 245 | if (login) { |
| 246 | loginBestName.set(login, pickBetterName(loginBestName.get(login) || "", name)); |
| 247 | } else { |
| 248 | const guess = loginFromNoreply(email); |
| 249 | if (guess) loginBestName.set(guess, pickBetterName(loginBestName.get(guess) || "", name)); |
| 250 | else pool.push({ name, email }); |
| 251 | } |
| 252 | for (const ca of parseCoAuthorLines(message)) pool.push(ca); |
| 253 | } |
| 254 | |
| 255 | // 3) Resolve pool (GraphQL users search only) |
| 256 | const emailToLogin = new Map(); // emailLower -> login |
| 257 | const concurrency = 8; |
| 258 | let idx = 0; |
| 259 | |
| 260 | async function worker() { |
| 261 | while (idx < pool.length) { |
| 262 | const i = idx++; |
| 263 | const { name, email } = pool[i]; |
| 264 | const ekey = toLower(email); |
| 265 | if (emailToLogin.has(ekey)) continue; |
| 266 | |
| 267 | let login = loginFromNoreply(email); |
| 268 | if (!login) login = await searchUsersByNameExact(name); |
| 269 | if (!login) { |
| 270 | const cands = candidateHandlesFromEmailAndName(email, name); |
| 271 | for (const cand of cands) { |
| 272 | const solo = await searchUsersByLoginToken(cand); |
| 273 | if (solo) { login = solo; break; } |
| 274 | } |
| 275 | } |
| 276 | if (!login && DEBUG) logd(`Unresolved: "${name}" <${email}>`); |
| 277 | emailToLogin.set(ekey, login || ""); |
| 278 | if (login) loginBestName.set(login, pickBetterName(loginBestName.get(login) || "", name)); |
| 279 | |
| 280 | if (i % 10 === 0) await sleep(60); |
| 281 | } |
| 282 | } |
| 283 | await Promise.all(Array.from({ length: concurrency }, worker)); |
| 284 |
no test coverage detected