Diff options
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Keep the commit-graph current in an example hook
cgit's log, blame and graph pages read commits from the commit-graph when a repository has one, and its changed path filters let a walk limited to one file skip the commits that never touched it. git gc writes a graph only now and then and starts those filters only when a write asks for them, so the hook writes a split layer with them after each push.
-rwxr-xr-xcustom/hooks/post-receive.commit-graph34
-rwxr-xr-xtests/t0507-hooks.sh24
2 files changed, 57 insertions, 1 deletion
diff --git a/custom/hooks/post-receive.commit-graph b/custom/hooks/post-receive.commit-graph
new file mode 100755
index 0000000..51d8c3c
--- /dev/null
+++ b/custom/hooks/post-receive.commit-graph
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Brings the repository's commit-graph up to date after a push. cgit's log,
+# blame and graph pages then read commits from it instead of parsing each one
+# out of the object store, and its changed path filters let a log or blame of
+# one file skip the commits that never touched it. git gc writes a graph now
+# and then, but starts the filters only when one is written with them, as this
+# does, and keeps them from then on.
+#
+# --split writes each push's new commits as a small layer over the last, and
+# git merges the layers as they pile up, so a push costs little. A git too old
+# for these options leaves the graph as it was, and nothing here complains or
+# exits nonzero, so the pusher is not shown an error over a missing speedup.
+#
+# To install the hook, copy or link it to hooks/post-receive in each
+# repository. Git runs one post-receive per repository, so fold this together
+# with the other hooks here to have more than one.
+
+set -eu
+
+# The pushing user's umask decides the mode of a new graph file in a
+# repository git does not share, so force one that leaves it readable by the
+# web server. git writes a shared repository's files group writable itself.
+case "$(git config --get core.sharedRepository | tr '[:upper:]' '[:lower:]')" in
+""|umask|0|false|no|off)
+ umask 022
+ ;;
+*)
+ umask 002
+ ;;
+esac
+
+git commit-graph write --reachable --split --changed-paths --no-progress \
+ >/dev/null 2>&1 || :
diff --git a/tests/t0507-hooks.sh b/tests/t0507-hooks.sh
index 8802ccc..8a45f81 100755
--- a/tests/t0507-hooks.sh
+++ b/tests/t0507-hooks.sh
@@ -2,7 +2,7 @@
# Checks the post-receive hooks under custom/hooks. The cache hook runs from a
# copy pointed at a cache and a scan path in the trash directory, since the
-# shipped one names the system's own, and both hooks run inside bare
+# shipped one names the system's own, and the hooks run inside bare
# repositories the way git runs them after a push.
test_description='Check the shipped post-receive hooks'
@@ -125,4 +125,26 @@ test_expect_success SANITY 'the age hook keeps a shared directory group writable
grep "^drwxrw[xs]" mode
'
+test_expect_success 'the commit-graph hook writes a graph with path filters' '
+ git init -q --bare graph.git &&
+ git -C work push -q ../graph.git HEAD:refs/heads/main &&
+ (cd graph.git && sh "$HOOK_DIRECTORY/post-receive.commit-graph" 2>../graph-err) &&
+ test_must_be_empty graph-err &&
+ test_path_is_file graph.git/objects/info/commit-graphs/commit-graph-chain &&
+ grep -q BDAT graph.git/objects/info/commit-graphs/*.graph &&
+ git -C graph.git commit-graph verify
+'
+
+for shell in $shells
+do
+ test_expect_success "a later push moves the graph on under $shell" "
+ cat graph.git/objects/info/commit-graphs/commit-graph-chain >chain-$shell &&
+ test_commit -C work graph-$shell &&
+ git -C work push -q ../graph.git HEAD:refs/heads/main &&
+ (cd graph.git && $shell \"\$HOOK_DIRECTORY/post-receive.commit-graph\") &&
+ ! test_cmp chain-$shell graph.git/objects/info/commit-graphs/commit-graph-chain &&
+ git -C graph.git commit-graph verify
+ "
+done
+
test_done