Git Hooks
Git hook system for automatic code indexing, maintaining synchronized knowledge base across projects.
Overview
Git hooks automatically trigger indexing when code changes:
- post-commit: Index newly committed changes
- post-merge: Index merged changes from pull/merge
- post-checkout: Handle branch switches
Hook Installation
Automatic Installation
Hooks are automatically installed when:
bash
# Creating new project with Wikit
wdg create my-site --init-wikit
# Adding repository to project
wdg my-site repo add https://github.com/client/repo
# Connecting central repository
wdg repo connect my-site client-repoManual Installation
bash
# Copy hooks to repository
cp hooks/* /path/to/repository/.git/hooks/
# Make executable
chmod +x /path/to/repository/.git/hooks/*Hook Implementations
post-commit Hook
Indexes files changed in the latest commit:
bash
#!/bin/bash
# .git/hooks/post-commit
# Get repository info
REPO_PATH=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename "$REPO_PATH")
PROJECT_NAME=$(basename $(dirname $(dirname "$REPO_PATH")))
# Get changed files
CHANGED_FILES=$(git diff --name-only HEAD^ HEAD)
# Filter indexable files
INDEXABLE=$(echo "$CHANGED_FILES" | grep -E '\.(php|js|jsx|scss|css|json)$')
if [ -z "$INDEXABLE" ]; then
echo "No indexable files changed"
exit 0
fi
# Get commit info
COMMIT_HASH=$(git rev-parse HEAD)
COMMIT_MSG=$(git log -1 --pretty=%B)
AUTHOR=$(git log -1 --pretty=%an)
echo "Indexing ${PROJECT_NAME}/${REPO_NAME}..."
echo "Commit: ${COMMIT_HASH:0:7} - $COMMIT_MSG"
echo "Files: $(echo "$INDEXABLE" | wc -l)"
# Trigger indexing
python3 ${WDG_HOME}/indexer/index_changes.py \
--project "$PROJECT_NAME" \
--repository "$REPO_NAME" \
--files "$INDEXABLE" \
--commit "$COMMIT_HASH" \
--author "$AUTHOR"
echo "✓ Indexing complete"post-merge Hook
Indexes changes after git pull/merge:
bash
#!/bin/bash
# .git/hooks/post-merge
# Get merge info
SQUASH_MERGE=$1 # 1 if squash merge, 0 otherwise
MERGE_HEAD=$(cat .git/ORIG_HEAD 2>/dev/null || echo "HEAD^")
# Get changed files
CHANGED_FILES=$(git diff --name-only $MERGE_HEAD HEAD)
# Filter indexable files
INDEXABLE=$(echo "$CHANGED_FILES" | grep -E '\.(php|js|jsx|scss|css|json)$')
if [ -z "$INDEXABLE" ]; then
echo "No indexable files changed in merge"
exit 0
fi
echo "Post-merge indexing..."
echo "Files changed: $(echo "$INDEXABLE" | wc -l)"
# Trigger indexing
python3 ${WDG_HOME}/indexer/index_changes.py \
--project "$PROJECT_NAME" \
--repository "$REPO_NAME" \
--files "$INDEXABLE" \
--merge-head "$MERGE_HEAD"
echo "✓ Merge indexing complete"post-checkout Hook
Handles branch switches and updates:
bash
#!/bin/bash
# .git/hooks/post-checkout
PREV_HEAD=$1
NEW_HEAD=$2
BRANCH_CHECKOUT=$3 # 1 if branch checkout, 0 if file checkout
# Only run on branch checkout
if [ "$BRANCH_CHECKOUT" != "1" ]; then
exit 0
fi
# Get changed files between branches
CHANGED_FILES=$(git diff --name-only $PREV_HEAD $NEW_HEAD)
# Filter indexable files
INDEXABLE=$(echo "$CHANGED_FILES" | grep -E '\.(php|js|jsx|scss|css|json)$')
if [ -z "$INDEXABLE" ]; then
exit 0
fi
echo "Branch switch detected, updating index..."
# Trigger indexing
python3 ${WDG_HOME}/indexer/index_changes.py \
--project "$PROJECT_NAME" \
--repository "$REPO_NAME" \
--files "$INDEXABLE" \
--prev-head "$PREV_HEAD" \
--new-head "$NEW_HEAD"
echo "✓ Index updated for new branch"Hook Configuration
Environment Variables
bash
# .git/hooks/config
PROJECT_NAME="my-site"
REPO_NAME="custom-theme"
INDEXER_PATH="${WDG_HOME}/indexer"
LOG_FILE="${WDG_HOME}/logs/indexing.log"Hook Settings
bash
# Enable/disable hooks
ENABLE_POST_COMMIT=true
ENABLE_POST_MERGE=true
ENABLE_POST_CHECKOUT=true
# Indexing settings
BATCH_SIZE=10
ASYNC_INDEXING=true
LOG_LEVEL=INFOIndexing Script
The hooks call index_changes.py:
python
#!/usr/bin/env python3
# indexer/index_changes.py
import argparse
import sys
from indexer import Indexer
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--project', required=True)
parser.add_argument('--repository', required=True)
parser.add_argument('--files', required=True)
parser.add_argument('--commit', required=False)
parser.add_argument('--author', required=False)
parser.add_argument('--merge-head', required=False)
args = parser.parse_args()
# Parse file list
files = args.files.split('\n')
files = [f.strip() for f in files if f.strip()]
# Initialize indexer
indexer = Indexer(args.project)
# Index changed files
try:
indexer.index_files(
repository=args.repository,
files=files,
commit_hash=args.commit,
author=args.author
)
print(f"✓ Indexed {len(files)} files")
except Exception as e:
print(f"✗ Indexing failed: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()Advanced Features
Conditional Indexing
bash
# Only index on specific branches
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" != "main" ] && [ "$CURRENT_BRANCH" != "develop" ]; then
echo "Skipping indexing on feature branch"
exit 0
fiThrottling
bash
# Only index if last index was > 1 minute ago
LAST_INDEX_FILE=".git/last-index"
CURRENT_TIME=$(date +%s)
if [ -f "$LAST_INDEX_FILE" ]; then
LAST_INDEX=$(cat "$LAST_INDEX_FILE")
TIME_DIFF=$((CURRENT_TIME - LAST_INDEX))
if [ $TIME_DIFF -lt 60 ]; then
echo "Skipping (indexed $TIME_DIFF seconds ago)"
exit 0
fi
fi
# Run indexing...
# Update timestamp
echo "$CURRENT_TIME" > "$LAST_INDEX_FILE"Background Indexing
bash
# Run indexing in background to avoid blocking
{
python3 /path/to/indexer/index_changes.py \
--project "$PROJECT_NAME" \
--files "$INDEXABLE" \
>> "$LOG_FILE" 2>&1
} &
echo "Indexing started in background..."Logging
Hook Logs
bash
# Log to file
exec >> ${WDG_HOME}/logs/hooks.log 2>&1
echo "$(date '+%Y-%m-%d %H:%M:%S') - post-commit hook triggered"
echo "Files changed: $CHANGED_FILES"Indexing Logs
python
import logging
logging.basicConfig(
filename='${WDG_HOME}/logs/indexing.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('indexer')
logger.info(f"Indexing {len(files)} files for {project_name}")Troubleshooting
Hooks Not Running
bash
# Check if hooks are executable
ls -la .git/hooks/
# Make executable
chmod +x .git/hooks/post-commit
chmod +x .git/hooks/post-merge
chmod +x .git/hooks/post-checkout
# Test hook manually
.git/hooks/post-commitIndexing Failures
bash
# Check indexing logs
tail -f ${WDG_HOME}/logs/indexing.log
# Test indexer directly
python3 indexer/index_changes.py \
--project my-site \
--repository custom-theme \
--files "functions.php"Hook Debugging
Add debug output to hooks:
bash
#!/bin/bash
set -x # Print commands as they execute
# Rest of hook...Hook Management Commands
Disable Hooks Temporarily
bash
# 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
bash
git commit --no-verify -m "Skip hooks for this commit"Reinstall Hooks
bash
# Via CLI
wdg repo hooks install my-site custom-theme
# Manual
cp hooks/* /path/to/repo/.git/hooks/
chmod +x /path/to/repo/.git/hooks/*Best 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
bash
#!/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
bash
# 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: