Skip to content

hook install never registers the graph.json merge driver that README and CHANGELOG 0.7.0 both document #1902

Description

@luizbon

Summary

The README (twice) and CHANGELOG 0.7.0 state that graphify hook install configures a git merge driver for graphify-out/graph.json. It doesn't. hooks.install() writes only the post-commit and post-checkout scripts — it never writes .gitattributes and never registers merge.graphify in .git/config.

The graphify merge-driver subcommand itself is fine. Only the registration half is missing, so the fix looks small.

Docs that promise it

README.md (team setup):

Run graphify hook install to auto-rebuild after each commit (AST only, no API cost). This also sets up a git merge driver so graph.json is never left with conflict markers — two devs committing in parallel get their graphs union-merged automatically.

README.md (troubleshooting):

graph.json has conflict markers after two devs commit at once
Run graphify hook install — it sets up a git merge driver that union-merges graph.json automatically so conflicts never happen.

CHANGELOG.md 0.7.0:

Feat: graphify hook install now also configures a git merge driver for graphify-out/graph.json … writes .gitattributes and registers graphify merge-driver in .git/config

cli.py help text also says the driver is "set up via hook install".

Repro (v0.9.15)

cd "$(mktemp -d)" && git init -q
graphify hook install
# post-commit: installed at .../.git/hooks/post-commit
# post-checkout: installed at .../.git/hooks/post-checkout

git config --get-regexp '^merge\.'   # -> nothing
ls .gitattributes                    # -> No such file or directory

Expected: merge.graphify.driver registered and graphify-out/graph.json merge=graphify in .gitattributes.
Actual: neither. The two hook scripts are the only side effects.

Code path

cli.py elif cmd == "hook": (~L336) → hooks.install() (hooks.py L507–542), whose entire body resolves the hooks dir, pins sys.executable into both scripts, then:

commit_msg   = _install_hook(hooks_dir, "post-commit",   hook,     _HOOK_MARKER)
checkout_msg = _install_hook(hooks_dir, "post-checkout", checkout, _CHECKOUT_MARKER)
return f"post-commit: {commit_msg}\npost-checkout: {checkout_msg}"

No merge-driver work anywhere in the function.

It doesn't appear to have regressed — it looks like it never shipped

Counting gitattributes|merge_driver|merge\.graphify|merge=graphify in each file at each tag:

file v0.7.0 v0.8.0 v0.9.0 v0.9.15
graphify/hooks.py 0 0 0 0
graphify/install.py 0 0
graphify/cli.py 0 0
graphify/__main__.py 0 0

tests/test_hooks.py has 12 tests (test_install_creates_hook, test_install_idempotent, test_install_creates_post_checkout_hook, …) and zero reference merge. Nothing asserts the registration, which is consistent with the payload landing in 0.7.0 while the wiring didn't.

Impact

graphify-out/ is documented as something to commit, so this bites exactly the multi-dev workflow 0.7.0 was aimed at: two branches touching code both regenerate graph.json, and the merge leaves conflict markers in a large generated file nobody can hand-resolve. The troubleshooting entry for that symptom prescribes a command that doesn't fix it.

The driver works when wired by hand

Registering it manually and merging two branches that each add a distinct node union-merges cleanly, no conflict markers:

git config merge.graphify.name "graphify graph.json union merge"
git config merge.graphify.driver "$(command -v graphify) merge-driver %O %A %B"
printf 'graphify-out/graph.json merge=graphify\n' > .gitattributes
# branch A adds MarkWatched, branch B adds SyncQueue, then:
git merge feat-a
# -> nodes: ['Base', 'MarkWatched', 'SyncQueue']; no conflict markers

So merge-driver itself is good — hooks.install() just needs to register it.

Suggested fix

In hooks.install(), alongside the two _install_hook calls:

  1. git config merge.graphify.name / merge.graphify.driver in the repo's .git/config.
  2. Ensure graphify-out/graph.json merge=graphify in .gitattributes (append idempotently; it's a tracked file, so don't clobber).

One wrinkle worth considering: the driver command should probably be an absolute path, for the same reason the hook scripts already pin sys.executable. Git invokes the driver through a shell, and ~/.local/bin is routinely absent from PATH in GUI git clients and CI — the exact environments the pinning comment in install() calls out. A bare graphify merge-driver … would fail there and silently fall back to a conflicting merge.

If the intent is instead that users wire this themselves, the two README passages and the CHANGELOG entry need correcting — the troubleshooting one especially, since it's the answer given to someone already hitting the bug.

Found while setting this up on a fresh repo; happy to send a PR if the approach above looks right.


graphify 0.9.15 · macOS 15.5 (arm64) · Python 3.14 · uv tool install

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions