This guide explains how to use git hooks to automatically keep your code graph database up to date with each commit.
The post-commit git hook automatically:
- Compiles your Elixir project with debug info (if needed)
- Extracts AST data for files changed in the last commit using
ex_ast --git-diff - Updates the SurrealDB database with the new data (using upsert to update existing records)
This provides incremental updates without the need to re-analyze your entire codebase after each change.
- ex_ast installed and available in your PATH
code_searchbinary installed and available in your PATH- An Elixir project with a
mix.exsfile - Git repository
To install the post-commit hook:
code_search setup --install-hooksThis will:
- Install the
post-commithook to.git/hooks/ - Configure git settings:
code-search.mix-env:dev(Mix environment to use)
That's it! The database path is automatically resolved to .code_search/surrealdb.rocksdb in your project root.
To set up everything in one command:
code_search setup --install-skills --install-hooksThis will:
- Create the database schema at
.code_search/surrealdb.rocksdb - Install Claude Code skills to
.claude/skills/ - Install Claude Code agents to
.claude/agents/ - Install the post-commit hook to
.git/hooks/
The hook works out of the box without any configuration. However, you can optionally customize:
To use a different Mix environment (default: dev):
git config code-search.mix-env testgit config --get-regexp code-searchWhen you make a commit, the post-commit hook:
-
Checks prerequisites: Verifies that
mix.exs,ex_ast, andcode_searchare available -
Compiles the project: Runs
mix compile --debug-infoto ensure BEAM files exist- If compilation fails, the hook exits with an error message
- The commit still succeeds, but the database is not updated
-
Extracts changes: Runs
ex_ast --git-diff HEAD~1to extract AST data for files changed in the last commit- Uses the configured Mix environment
- Outputs JSON to a temporary file
-
Updates database: Runs
code_search code importto update the database- Database path auto-resolves to
.code_search/surrealdb.rocksdb - Uses configured project name if set (optional)
- Performs upsert operations (updates existing records, inserts new ones)
- Database path auto-resolves to
The import process uses upsert semantics:
- Modules: Keyed by
(project, name)- updates existing modules - Functions: Keyed by
(project, module, name, arity)- updates existing functions - Calls: Keyed by
(project, caller_module, caller_function, callee_module, callee_function, callee_arity, file, line, column)- updates existing calls - Struct Fields: Keyed by
(project, module, field)- updates existing fields - Function Locations: Keyed by
(project, module, name, arity, line)- updates existing locations - Specs: Keyed by
(project, module, name, arity)- updates existing specs - Types: Keyed by
(project, module, name)- updates existing types
This means:
- Modified functions get their data updated
- Deleted functions remain in the database (intentional - preserves history)
- New functions are added
- If you need to fully rebuild, use
code_search setup --forceand re-import
The hook handles errors gracefully:
If your code doesn't compile, you'll see:
[code-search] Compilation failed - changed files in the last commit don't compile
[code-search] Database update skipped
The commit succeeds, but the database update is skipped.
If ex_ast or code_search are not found:
[code-search] ex_ast not found in PATH, skipping database update
[code-search] Install from: https://github.com/CamonZ/ex_ast
The hook exits gracefully without updating the database.
If the hook runs in a non-Elixir project (no mix.exs):
[code-search] No mix.exs found, skipping database update
Use the --no-verify flag:
git commit --no-verify -m "Skip hook for this commit"Remove the hook file:
rm .git/hooks/post-commitOr make it non-executable:
chmod -x .git/hooks/post-commitGit hooks don't run for:
git rebasegit merge(uses different hooks)git cherry-pick- Operations that don't create new commits
Check if the hook is executable:
ls -la .git/hooks/post-commitShould show: -rwxr-xr-x (executable permission)
Make it executable if needed:
chmod +x .git/hooks/post-commit- Check that ex_ast is working:
ex_ast --git-diff HEAD~1 --format json- Check git configuration:
git config --get-regexp code-search- Check database exists:
ls -la .code_search/surrealdb.rocksdbThe hook adds some time to each commit:
- Compilation: Usually cached, fast after first compile
- AST extraction: ~1-2 seconds for typical changes
- Database import: ~100ms for typical changes
If this is problematic, consider:
- Disabling the hook temporarily during rapid development
- Using
--no-verifyfor quick commits - Running batch updates manually instead
If you prefer manual control, you can run the hook logic manually:
# Compile
mix compile --debug-info
# Extract changes from last commit
ex_ast --git-diff HEAD~1 --format json --output changes.json
# Import
code_search --db call_graph.db code import --file changes.jsonOr for a different git reference:
# Compare against a specific commit
ex_ast --git-diff abc123 --format json --output changes.json
# Compare staged changes (before commit)
ex_ast --git-diff --staged --format json --output changes.jsonThe same approach works in CI/CD pipelines. Example GitHub Actions workflow:
- name: Update code graph
run: |
mix compile --debug-info
ex_ast --git-diff HEAD~1 --format json --output changes.json
code_search --db call_graph.db code import --file changes.jsonTypical performance for a commit changing 5 files with 20 functions:
- Compilation: <1s (cached) to 10s (clean)
- AST extraction: 1-2s
- Database import: 100-500ms
- Total overhead: 2-12s per commit
The hook is designed to be fast for incremental updates. Full project analysis would take much longer.