import { defined } from "../../utils/anonymous.ts"; import { type MaybeIterable, surelyIterable } from "../../utils/iterator.ts"; import { gitDir } from "./index.ts"; import type { Commit, CommitFile } from "./types.ts"; const format = [ "H", "h", "aI", "aN", "aE", "cI", "cN", "cE", // "G?", "GS", "GK", "GF", "GG", ]; export async function getLastCommitForOneOfFiles( sources: MaybeIterable, ): Promise { const files = surelyIterable(sources); const gitLog = new Deno.Command("git", { args: [ "log", "-1", `--pretty=format:${format.map((x) => `%${x}`).join("%n")}`, "--", // deno-lint-ignore no-undef ...Iterator.from(files).map((x) => x.pathname), ], }); const { stdout } = await gitLog.output(); const result = new TextDecoder().decode(stdout).trim(); if (result.length <= 0) { return undefined; } const [ hash, abbrHash, authorDate, authorName, authorEmail, committerDate, committerName, committerEmail, // signatureValidation, signer, key, keyFingerPrint, ...rawLines ] = result.split("\n"); const raw = rawLines.join("\n").trim(); const commit: Commit = { // deno-lint-ignore no-undef files: await fileStatusFromCommit(hash, Iterator.from(files)), hash: { long: hash, short: abbrHash }, author: { date: new Date(authorDate), name: authorName, email: authorEmail, }, committer: { date: new Date(committerDate), name: committerName, email: committerEmail, }, }; if (raw.length > 0) { commit.signature = { type: raw.startsWith("gpgsm:") ? "x509" : raw.startsWith("gpg:") ? "gpg" : "ssh", signer, key: { long: keyFingerPrint, short: key }, rawMessage: raw, }; } return commit; } async function fileStatusFromCommit( hash: string, files: Iterable, ): Promise { const gitDiffTree = new Deno.Command("git", { args: [ "diff-tree", "--no-commit-id", "--name-status", "-r", hash, ], }); const { stdout } = await gitDiffTree.output(); const result = new TextDecoder().decode(stdout).trim().split("\n").filter( defined, ); const dir = await gitDir(); return result.map((line) => { const [status, path] = line.split("\t"); if ( // deno-lint-ignore no-undef Iterator.from(files).some((file) => file.pathname.replace(dir.pathname, "").includes(path) ) ) { return { path: new URL(path, dir), status: status === "A" ? "added" : status === "D" ? "deleted" : "modified", } as const; } return undefined; }).filter(defined); }