about summary refs log tree commit diff
path: root/pkgs/profpatsch/git-commit-index
diff options
context:
space:
mode:
authorProfpatsch <mail@profpatsch.de>2019-03-25 13:50:16 +0100
committerProfpatsch <mail@profpatsch.de>2019-03-25 13:53:46 +0100
commit419be355e2c200f940e02aba1a5f503974399bec (patch)
tree58b7ed864cd16d85e72a5426a577be66186d2788 /pkgs/profpatsch/git-commit-index
parent2217ba46e3e93d049c5e6aadf3b1c0090e8c7725 (diff)
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.
Diffstat (limited to 'pkgs/profpatsch/git-commit-index')
-rw-r--r--pkgs/profpatsch/git-commit-index/default.nix69
-rw-r--r--pkgs/profpatsch/git-commit-index/lib.sh102
2 files changed, 171 insertions, 0 deletions
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
+}