summaryrefslogtreecommitdiff
path: root/src/lib/git/log.ts
diff options
context:
space:
mode:
authorJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-06-24 12:08:41 -0300
committerJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-06-24 12:50:43 -0300
commitf9a77c5c27aede4e5978eb55d9b7af781b680a1d (patch)
treed545e325ba1ae756fc2eac66fac1001b6753c40d /src/lib/git/log.ts
feat!: initial commit
Signed-off-by: João Augusto Costa Branco Marado Torres <torres.dev@disroot.org>
Diffstat (limited to 'src/lib/git/log.ts')
-rw-r--r--src/lib/git/log.ts131
1 files changed, 131 insertions, 0 deletions
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<URL>,
+): Promise<Commit | undefined> {
+ 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<URL>,
+): Promise<CommitFile[]> {
+ 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);
+}