From f9a77c5c27aede4e5978eb55d9b7af781b680a1d Mon Sep 17 00:00:00 2001 From: João Augusto Costa Branco Marado Torres Date: Tue, 24 Jun 2025 12:08:41 -0300 Subject: feat!: initial commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: João Augusto Costa Branco Marado Torres --- src/lib/git/log.ts | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/lib/git/log.ts (limited to 'src/lib/git/log.ts') diff --git a/src/lib/git/log.ts b/src/lib/git/log.ts new file mode 100644 index 0000000..86bbe7b --- /dev/null +++ b/src/lib/git/log.ts @@ -0,0 +1,131 @@ +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")}`, + "--", + ...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 = { + 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 ( + 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); +} -- cgit v1.2.3