From 419be355e2c200f940e02aba1a5f503974399bec Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Mon, 25 Mar 2019 13:50:16 +0100 Subject: pkgs.profpatsch: add git-commit-index tools A set of utilities to generate and query a git commit index, which is a database that knows which revs (that is: commits) are in which git repository. That way we can query for the project that contains a commit and show them, e.g. with xdg-open. --- pkgs/profpatsch/default.nix | 7 ++ pkgs/profpatsch/git-commit-index/default.nix | 69 ++++++++++++++++++ pkgs/profpatsch/git-commit-index/lib.sh | 102 +++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 pkgs/profpatsch/git-commit-index/default.nix create mode 100644 pkgs/profpatsch/git-commit-index/lib.sh (limited to 'pkgs') diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix index d7c94ea6..a4378ecf 100644 --- a/pkgs/profpatsch/default.nix +++ b/pkgs/profpatsch/default.nix @@ -40,6 +40,12 @@ let }; in import src { nixpkgs = pkgs; }; + runCommandLocal = name: args: cmd: + pkgs.runCommand name (args // { + preferLocalBuild = true; + allowSubstitutes = false; + }) cmd; + in rec { inherit (nixperiments) # filterSource by parsing a .gitignore file @@ -55,6 +61,7 @@ in rec { backlight = callPackage ./backlight { inherit (pkgs.xorg) xbacklight; }; display-infos = callPackage ./display-infos {}; + git-commit-index = callPackage ./git-commit-index { inherit script runCommandLocal; }; nix-http-serve = callPackage ./nix-http-serve {}; nman = callPackage ./nman {}; show-qr-code = callPackage ./show-qr-code {}; diff --git a/pkgs/profpatsch/git-commit-index/default.nix b/pkgs/profpatsch/git-commit-index/default.nix new file mode 100644 index 00000000..d7b9a781 --- /dev/null +++ b/pkgs/profpatsch/git-commit-index/default.nix @@ -0,0 +1,69 @@ +{ cdb, git, writeShellScriptBin, symlinkJoin, lib, script, runCommandLocal }: + +let + pathPlus = ''PATH="$PATH:${lib.makeBinPath [cdb git]}"''; + + mkIndex = script.withOptions { + name = "git-commit-index-create"; + synopsis = "Create a git commit index for all .git directories, recursively."; + + options = { + index = { + description = "Location of the index. Must not exist."; + checks = [ script.optionChecks.emptyPath ]; + }; + }; + + extraArgs = { + description = "List of root directories to recursively search"; + name = "DIRS"; + checks = [ script.argsChecks.allAreDirs ]; + }; + + script = '' + ${pathPlus} + source ${./lib.sh} + mkdir "$index" + for root in "$@"; do + for repo in $(findRepos "$root"); do + genIndex "$index" "$repo" + done + done + ''; + }; + + queryIndex = script.withOptions { + name = "git-commit-index-query"; + + synopsis = "Search a git commit index for a rev, return the repository that rev is in."; + + options = { + index = { + description = "Location of the populated index."; + checks = [ script.optionChecks.isDir ]; + }; + rev = { + description = "Full git rev hash to look up"; + # TODO: check that it is a commit hash? + checks = []; + }; + }; + + script = '' + ${pathPlus} + source ${./lib.sh} + + query "$index" "$rev" + ''; + }; + + # magitOpenCommit = writeShellScriptBin "git-commit-index-magit-open" '' + # ${pathPlus} +# emacsclient -e '(let ((default-directory "/home/philip/kot/work/tweag/lorri")) +# (magit-mode-setup #\'magit-revision-mode "43a72bc220dae8f34bd0d889f2b1b1ce2a6092b7" nil nil nil))' + +in + runCommandLocal "git-commit-index" {} '' + install -D ${mkIndex} $out/bin/git-commit-index-create + install -D ${queryIndex} $out/bin/git-commit-index-query + '' diff --git a/pkgs/profpatsch/git-commit-index/lib.sh b/pkgs/profpatsch/git-commit-index/lib.sh new file mode 100644 index 00000000..229eec1e --- /dev/null +++ b/pkgs/profpatsch/git-commit-index/lib.sh @@ -0,0 +1,102 @@ +set -euo pipefail + +# nix-shell -p cdb --run 'bash -c \'source ~/tmp/gitcdb.sh; for r in $(findRepos ~/kot); do genIndex . "$r"; done\'' + +findRepos () { + find "$1" -type d -name ".git" + # TODO check each repo is actually git repo +} + +genIndex () { + local indexDir=$(realpath -- "$1") + local path=$(realpath -- "$2") + if [ ! -d "$indexDir" ]; then + echo "index directory does not exist: $indexDir" + exit 111 + fi + # TODO: multimap failure + # + local filename="$indexDir/$(echo $path | sed -e 's|_|__|g' -e 's|/|_|g')" + local pathLength=$(echo "$path" | wc --bytes | tr -d '\n') + (pushd "$path" > /dev/null \ + && ( git log --all --format="format:%H" \ + | sed -e "s/^\(.*\)$/+40,0:\1->/" \ + ; echo \ + ; echo "+8,${pathLength}:git-path->${path}" \ + ; echo; echo \ + ) \ + | cdbmake "$filename" "$filename.tmp" \ + ) +} + +query () { + local indexDir=$(realpath -- "$1") + local key="$2" + + local found=0 + local result= + + # TODO make this parallel (and switch away from bash) + for f in "$indexDir"/*; do + + set +e + # don't need result because empty string + <"$f" cdbget "$key" >/dev/null + local ret=$? + set -e + + case $ret in + 0) + # TODO: back + found=1 + + set +e + # now find original path + local origDotGit=$(<"$f" cdbget "git-path") + local retGitPath=$? + set -e + + case $retGitPath in + 0) + : + ;; + 100) + echo "shouldn’t happen; git-path was not in $f" + exit 127 + ;; + 111) + echo "db error in $f" + exit 111 + ;; + *) + echo "shouldn’t happen; exitcode was $ret" + exit 127 + ;; + esac + + # return workspace file + result=$(dirname "$origDotGit") + break + ;; + 100) + # not found + : + ;; + 111) + echo "db error in $f" + exit 111 + ;; + *) + echo "shouldn’t happen; exitcode was $ret" + exit 127 + ;; + esac + done + + if [ $found -eq 0 ]; then + exit 100 + else + echo "$result" + exit 0 + fi +} -- cgit 1.4.1