From e9466c4c436f964b53034e28356aa3f5c957a068 Mon Sep 17 00:00:00 2001 From: bptato Date: Sun, 1 Sep 2024 00:49:13 +0200 Subject: git.cgi: add git blame --- bonus/git.cgi | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 11 deletions(-) (limited to 'bonus/git.cgi') diff --git a/bonus/git.cgi b/bonus/git.cgi index 2c8a5df0..bac30776 100755 --- a/bonus/git.cgi +++ b/bonus/git.cgi @@ -1,11 +1,11 @@ #!/usr/bin/env -S qjs --std -/* adds clickable links to git log, git branch and git stash list +/* adds clickable links to git log, branch, blame & stash list * usage: * 0. install QuickJS (https://bellard.org/quickjs) * 1. put this script in your CGI directory * 2. chmod +x /your/cgi-bin/directory/git.cgi * 3. ln -s /your/cgi-bin/directory/git.cgi /usr/local/bin/gitcha - * 4. run `gitcha log', `gitcha branch' or `gitcha stash list' + * 4. run `gitcha log', `gitcha branch', `gitcha blame` or `gitcha stash list' * other params work too, but without any special processing. it's still useful * for ones that open the pager, like git show; this way you can reload the view * with `U'. @@ -13,6 +13,8 @@ * it may be best to just alias the pager-opening commands. * (if you have ansi2html, it also works with w3m. just set GITCHA_CHA=w3m) */ +"use strict"; + const gitcha = std.getenv("GITCHA_GITCHA") ?? "gitcha"; if (scriptArgs[0].split('/').pop() == gitcha) { const cha = std.getenv("GITCHA_CHA") ?? 'cha'; @@ -56,6 +58,7 @@ function startGitCmd(config, params) { function runGitCmd(config, params, regex, subfun) { const f = startGitCmd(config, params); + let l; while ((l = f.getline()) !== null) { console.log(l.replace(regex, subfun)); } @@ -73,26 +76,75 @@ function cgi(cmd) { return `${cgi0}¶ms=${encodeURIComponent(cmd)}`; } -if (params[0] == "log" || params[0] == "blame") { +if (params[0] == "log") { + const showUrl = cgi("show"); + const re = /[a-f0-9]{7}[a-f0-9]*/g; + function sub(x) { + return `${x}`; + } + runGitCmd(config, params, re, sub); +} else if (params[0] == "blame") { const showUrl = cgi("show"); - runGitCmd(config, params, /[a-f0-9]{7}[a-f0-9]*/g, - x => `${x}`); + let cmd = ""; + /* git will give us paths relative to the repo root, so correct that. */ + let [path, _] = os.getcwd(); + let op = ""; + while (path.length > 1) { + const [s, err] = os.stat(path + "/.git"); + if (!err && (s.mode & os.S_IFMT) == os.S_IFDIR) + break; + path = path.substring(0, path.lastIndexOf('/')); + op += "../"; + } + if (path[path.length - 1] != '/') + path += '/'; + /* collect existing flags, but skip commit & file name */ + let flags = params.filter(x => x && x != "-s" && x[0] == '-' && (x[1] != '-' || x[2])).join(' '); + if (flags) + flags += ' '; + const file = params.findLast(x => x[0] != '-'); + cmd = encodeURIComponent(`blame ${flags}%s --`); + /* silence some useless info */ + params.splice(1, 0, "-s"); + const re = /([a-f0-9]{7}[a-f0-9]*) ([^ ]+)? *([0-9]+)\)/g; + function sub(_, x, y, z) { + const f = y ? op + y : file; + return `${x}`; + } + runGitCmd(config, params, re, sub); } else if (params[0] == "branch" && (params.length == 1 || params.length == 2 && ["-l", "--list", "-a", "--all"].includes(params[1]))) { const logUrl = cgi("log"); const checkoutUrl = cgi("checkout"); - runGitCmd(config, params, /^(\s+)()?([\w./-]+)(<.*)?$/g, - (_, ws, $, name) => `${ws}${name}\ -
`); + const re = /^(\s+)()?([\w./-]+)(<.*)?$/g; + function sub(_, ws, $, name) { + return `${ws}${name}\ +
`; + } + runGitCmd(config, params, re, sub); } else if (params[0] == "stash" && params[1] == "list") { const showUrl = cgi("show"); const stashApply = cgi("stash apply"); const stashDrop = cgi("stash drop"); - runGitCmd(config, params, /^stash@\{([0-9]+)\}/g, - (s, n) => `stash@{${n}}\ + const re = /^stash@\{([0-9]+)\}/g; + function sub(s, n) { + return `stash@{${n}}\
` + -`
`); +`
`; + } + runGitCmd(config, params, re, sub); +} else if (params[0] == "show" && query.backlink) { + const cmds = query.backlink.split(' '); + const hash = cmds.pop(); + const i = cmds.indexOf('%s'); + const re = /[a-f0-9]{40}/g; + function sub(x) { + cmds[i] = x + "~1"; + const cmd = cgi(cmds.join(' ')); + return `${x}`; + } + runGitCmd(config, params, re, sub); } else { const safeForGet = ["show", "diff", "blame", "status"]; if (std.getenv("REQUEST_METHOD") != "POST" && -- cgit 1.4.1-2-gfad0