Git Hooks
Git hook system for automatic code indexing, maintaining synchronized knowledge base across projects.
Overview
Git hooks automatically trigger incremental indexing when code changes. Two hooks ship in the platform's hooks/ directory:
- post-commit: Index files changed in the new commit
- post-merge: Index files changed by a pull/merge
Both hooks POST the changed file list to the indexer's HTTP service. There is no post-checkout hook and no index_changes.py script — indexing goes through the running wdg-indexer service.
Hook Installation
Automatic Installation
Hooks are installed into a repository's .git/hooks/ by hooks/install-hooks.sh, which the CLI runs when a project or repository is set up:
# Creating a new project with Wikit
wdg create my-site --init-wikit
# Adding / cloning a repository into a project
wdg my-site repo add https://github.com/client/repo
# Re-install hooks for a specific repo
wdg my-site repo hooks <repo-name>Manual Installation
# From the platform root, run the installer against a repo
hooks/install-hooks.sh /path/to/repository
# Or copy the two hooks directly
cp hooks/post-commit hooks/post-merge /path/to/repository/.git/hooks/
chmod +x /path/to/repository/.git/hooks/post-commit /path/to/repository/.git/hooks/post-mergeHook Implementations
Both hooks follow the same shape: locate the WDG platform root, derive the project name from the repo path, skip silently if the wdg-indexer container is not running, translate host paths to the container's /workspace, and fire a backgrounded curl at the indexer.
post-commit Hook
#!/usr/bin/env bash
# .git/hooks/post-commit
REPO_DIR=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename "$REPO_DIR")
# Walk up to find the platform root (dir containing docker-compose.yml + projects/)
find_wdg_root() { ... }
WDG_ROOT=$(find_wdg_root)
[ -z "$WDG_ROOT" ] && exit 0 # not inside a WDG checkout
# Derive the index target from the repo's location
if [[ "$REPO_DIR" == */repositories/* ]]; then
INDEX_PROJECT="$REPO_NAME"
elif [[ "$REPO_DIR" == */projects/* ]]; then
INDEX_PROJECT=$(echo "$REPO_DIR" | sed 's|.*/projects/\([^/]*\).*|\1|')
else
exit 0
fi
# Skip if the indexer service isn't running (no blocking, no errors)
docker ps --format '{{.Names}}' | grep -q "wdg-indexer" || exit 0
# Changed files in this commit, with host paths rewritten to the container's /workspace
CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r HEAD \
| while read -r file; do echo "$REPO_DIR/$file"; done)
CHANGED_FILES=$(echo "$CHANGED_FILES" | sed "s|${WDG_ROOT}|/workspace|g")
FILES_JSON=$(echo "$CHANGED_FILES" | jq -R -s -c 'split("\n") | map(select(length > 0))')
# Fire-and-forget: POST to the indexer in the background so the commit isn't blocked
(curl -s --max-time 300 -X POST http://localhost:8666/index/incremental \
-H "Content-Type: application/json" \
-d "{\"files\": $FILES_JSON, \"project\": \"$INDEX_PROJECT\"}" \
> /dev/null 2>&1) &Key behaviors:
- No file-extension filtering in the hook — the indexer service decides what is indexable (unsupported extensions are skipped server-side).
- Host → container path translation —
${WDG_ROOT}is rewritten to/workspace, matching the read-only source mounts in theindexerservice so the paths resolve inside the container. - Backgrounded, 300 s cap — large changesets can take 60–120 s; the request is detached so the commit returns immediately.
post-merge Hook
Identical to post-commit except for how it computes the changed-file set. It diffs from the pre-merge head:
ORIG_HEAD=$(git rev-parse ORIG_HEAD 2>/dev/null || git rev-parse HEAD~1)
CHANGED_FILES=$(git diff --name-only "$ORIG_HEAD" HEAD \
| while read -r file; do echo "$REPO_DIR/$file"; done)
# ...same /workspace rewrite and backgrounded curl to /index/incrementalLogging
The hooks themselves write only two echo lines (a "indexing N files (background)" notice). Indexing output goes to the indexer service logs:
wdg logs indexer # or: docker logs wdg-indexerTroubleshooting
Hooks Not Running
# Check the two hooks are present and executable
ls -la .git/hooks/post-commit .git/hooks/post-merge
# Make executable
chmod +x .git/hooks/post-commit .git/hooks/post-merge
# Re-install via the platform installer
hooks/install-hooks.sh "$(git rev-parse --show-toplevel)"
# Test manually (no-op if wdg-indexer isn't running)
.git/hooks/post-commitNothing Gets Indexed
# The hooks exit silently if the indexer container isn't up
docker ps --format '{{.Names}}' | grep wdg-indexer
# Confirm the service is reachable on the host
curl http://localhost:6666/health # default host port (container 8666)
# Hit the endpoint the hooks use, by hand
curl -X POST http://localhost:6666/index/incremental \
-H "Content-Type: application/json" \
-d '{"files": ["/workspace/projects/my-site/repositories/my-site/functions.php"], "project": "my-site"}'Inside a container the indexer listens on
8666; the hooks targetlocalhost:8666because they translate paths for the container, but the service is published to the host on6666— use6666for manual checks from your machine.
Hook Debugging
Add debug output to hooks:
#!/bin/bash
set -x # Print commands as they execute
# Rest of hook...Hook Management Commands
Disable Hooks Temporarily
# Rename hooks to disable
mv .git/hooks/post-commit .git/hooks/post-commit.disabled
# Re-enable
mv .git/hooks/post-commit.disabled .git/hooks/post-commitSkip Hooks for Single Commit
git commit --no-verify -m "Skip hooks for this commit"Reinstall Hooks
# Via CLI
wdg my-site repo hooks custom-theme
# Manual (the two shipped hooks)
hooks/install-hooks.sh /path/to/repo
# or
cp hooks/post-commit hooks/post-merge /path/to/repo/.git/hooks/
chmod +x /path/to/repo/.git/hooks/post-commit /path/to/repo/.git/hooks/post-mergeBest Practices
- Keep hooks fast - Run indexing in background if needed
- Add logging - Track what gets indexed and when
- Error handling - Don't block commits on indexing failures
- Conditional logic - Skip indexing when appropriate
- Version control - Keep hook templates in repository
Custom Hooks
Creating Custom Hook
#!/bin/bash
# .git/hooks/pre-push
# Run linting before push
echo "Running linter..."
wdg theme lint $(basename $(pwd))
if [ $? -ne 0 ]; then
echo "Linting failed! Fix errors before pushing."
exit 1
fi
echo "✓ Linting passed"Shared Hook Logic
# hooks/common.sh
get_project_name() {
basename $(dirname $(dirname $(git rev-parse --show-toplevel)))
}
get_repo_name() {
basename $(git rev-parse --show-toplevel)
}
# Source in hooks
source "$(dirname $0)/common.sh"
PROJECT=$(get_project_name)See Also: