main install-hooks.sh
Eric Bower  ·  2026-05-07
 1#!/usr/bin/env bash
 2# install-hooks.sh — sync the post-receive hook to all bare repos in a directory.
 3#
 4# Usage:
 5#   ./install-hooks.sh /path/to/bare-repos
 6#   ./install-hooks.sh /path/to/repo1.git /path/to/repo2.git
 7#   ./install-hooks.sh --force /path/to/bare-repos    # overwrite existing hooks
 8#
 9# The hook is read from hooks/post-receive relative to this script.
10# Errors if a repo already has a different post-receive hook (use --force to overwrite).
11
12set -euo pipefail
13
14SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
15HOOK="$SCRIPT_DIR/hooks/post-receive"
16FORCE=false
17
18if [ "${1:-}" = "--force" ]; then
19    FORCE=true
20    shift
21fi
22
23if [ ! -f "$HOOK" ]; then
24    echo "error: $HOOK not found" >&2
25    exit 1
26fi
27
28if [ $# -eq 0 ]; then
29    echo "usage: $0 [--force] <repo.git ... | repos-dir>" >&2
30    exit 1
31fi
32
33repos=()
34for arg in "$@"; do
35    # Strip trailing slash to avoid double slashes in paths
36    arg="${arg%/}"
37    if [ -d "$arg" ] && [ ! -d "$arg/hooks" ]; then
38        # Looks like a directory containing repos, not a repo itself
39        for repo in "$arg"/*.git; do
40            [ -d "$repo" ] && repos+=("$repo")
41        done
42    else
43        repos+=("$arg")
44    fi
45done
46
47if [ ${#repos[@]} -eq 0 ]; then
48    echo "no repos found" >&2
49    exit 1
50fi
51
52installed=0
53skipped=0
54overwritten=0
55errors=0
56
57for repo in "${repos[@]}"; do
58    hooks_dir="$repo/hooks"
59    existing="$hooks_dir/post-receive"
60    mkdir -p "$hooks_dir"
61
62    if [ -f "$existing" ]; then
63        if diff -q "$HOOK" "$existing" >/dev/null 2>&1; then
64            echo "  = $repo (up to date)"
65            skipped=$((skipped + 1))
66            continue
67        fi
68
69        if [ "$FORCE" = true ]; then
70            cp "$HOOK" "$existing"
71            chmod +x "$existing"
72            echo "$repo (overwritten)"
73            overwritten=$((overwritten + 1))
74            continue
75        fi
76
77        echo "$repo (existing hook differs)" >&2
78        errors=$((errors + 1))
79        continue
80    fi
81
82    cp "$HOOK" "$existing"
83    chmod +x "$existing"
84    echo "$repo"
85    installed=$((installed + 1))
86done
87
88echo ""
89echo "installed: $installed  skipped: $skipped  overwritten: $overwritten  errors: $errors"
90
91if [ "$errors" -gt 0 ]; then
92    exit 1
93fi