Skip to content

[Bug]: installed pre-commit hook omits --repo, so committing from a git worktree builds a second full graph there #953

Description

@arhyneRWU

code-review-graph version

2.3.8

Operating system

Linux

Python version

3.11.15

AI platform

claude-code

Output of code-review-graph status

$ code-review-graph status          # main working tree
Nodes: 14985
Edges: 137385
Files: 997
Languages: bash, javascript, python, tsx, typescript
Last updated: 2026-09-01T19:05:40
Built on branch: fix/review-graph-worktree-and-search-mode
Built at commit: 050fd933dcc7

$ code-review-graph status          # from a linked worktree, before the hook ran
Nodes: 0

Steps to reproduce

  1. code-review-graph install in the main working tree of a repo (this writes pre-commit — see below).
  2. code-review-graph build in that main working tree. Note the node/file counts.
  3. git worktree add ../wt -b some-branch
  4. cd ../wt, change one file, git commit.
  5. ls ../wt/.code-review-graph — a second, complete database is now there.
  6. code-review-graph status from ../wt reports that graph, not the one from step 2.

Expected vs actual behavior

Expected: committing from a linked worktree updates the graph the repository already has, or declines and says why. It should not silently create a second store.

Actual: a full build runs inside the worktree and leaves a complete second database there — 267 MB in my case, on a 997-file repo. Subsequent queries from that worktree answer from whichever store cwd-discovery finds, reporting "status": "ok" either way.

Additional context

Cause. The hook template in code_review_graph/skills.py:1152-1158 passes no --repo:

#!/bin/sh
# Installed by code-review-graph. Remove this file to disable pre-commit graph checks.
if command -v code-review-graph >/dev/null 2>&1; then
    code-review-graph update || true
    code-review-graph detect-changes --brief || true
fi

Both commands then discover the repository from the working directory, which at commit time is the tree being committed. The surrounding installer is careful about where the hook lands — the docstring calls out linked worktrees, submodules and core.hooksPath explicitly (#313), and it resolves via git rev-parse --git-path hooks. So the hook is correctly placed to run inside worktrees, and then misbehaves in exactly those worktrees. Because the resolved path is usually the common dir, one hook file serves every worktree and one fix covers all of them.

repo_root is already in scope at install time, so the information needed is available; it just isn't carried into the script.

Why this is worth fixing rather than documenting. The failure mode is a confident wrong answer, not an error. A freshly created store in a worktree contains only what that worktree has touched, and a graph that has never parsed a file returns 0 for it — indistinguishable from a true 0. callers_of on a live symbol reports no callers, which reads as "nothing depends on this, safe to change". I measured four worktrees on one repo holding 7, 27, 26 and 6 files against a real 967, every one answering "status": "ok".

Not a duplicate, as far as I can tell — I looked at the three closest:

docs/TROUBLESHOOTING.md describes where the hook is placed (including worktrees) but not this behavior.

Suggested fix, if useful — resolving at runtime rather than baking an absolute path in, so the hook survives the repo being moved:

#!/bin/sh
# Installed by code-review-graph. Remove this file to disable pre-commit graph checks.
command -v code-review-graph >/dev/null 2>&1 || exit 0
root=$(git worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')
[ -n "$root" ] || exit 0
here=$(git rev-parse --show-toplevel 2>/dev/null)
gitdir=$(git rev-parse --absolute-git-dir 2>/dev/null)
[ -n "$here" ] && [ -n "$gitdir" ] || exit 0
code-review-graph update --repo "$root" >/dev/null 2>&1 || true
GIT_DIR="$gitdir" GIT_WORK_TREE="$here" \
    code-review-graph detect-changes --repo "$root" --brief || true

The first git worktree list entry is always the main working tree. The two calls need opposite things, which is the part worth keeping if you take a different shape: update should write the shared store, while detect-changes must read this branch's diff — --repo selects both the graph and the tree the diff is read from, so without GIT_DIR/GIT_WORK_TREE it reports on the main branch and prints "no changes detected" on a branch full of them.

I have been running the above locally and can confirm a second commit from a worktree creates no store there and leaves the shared graph correct (14,985 nodes throughout). Happy to open a PR if you'd like it in this shape; equally happy if you'd rather the hook simply refuse to run outside the main working tree, which is a smaller change and also removes the wrong answer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions