Re-run AST extraction + build + optional cluster + report for code files. No LLM needed. When ``force`` is True the node-count safety check in ``to_json`` is bypassed so the rebuilt graph overwrites graph.json even if it has fewer nodes. Use this after refactors that legitimately delete
(
watch_path: Path,
*,
changed_paths: list[Path] | None = None,
follow_symlinks: bool = False,
force: bool = False,
no_cluster: bool = False,
acquire_lock: bool = True,
block_on_lock: bool = False,
)
| 658 | |
| 659 | |
| 660 | def _rebuild_code( |
| 661 | watch_path: Path, |
| 662 | *, |
| 663 | changed_paths: list[Path] | None = None, |
| 664 | follow_symlinks: bool = False, |
| 665 | force: bool = False, |
| 666 | no_cluster: bool = False, |
| 667 | acquire_lock: bool = True, |
| 668 | block_on_lock: bool = False, |
| 669 | ) -> bool: |
| 670 | """Re-run AST extraction + build + optional cluster + report for code files. No LLM needed. |
| 671 | |
| 672 | When ``force`` is True the node-count safety check in ``to_json`` is bypassed |
| 673 | so the rebuilt graph overwrites graph.json even if it has fewer nodes. |
| 674 | Use this after refactors that legitimately delete code. |
| 675 | |
| 676 | When ``changed_paths`` is provided, only those files are re-extracted; nodes |
| 677 | for unchanged files are preserved from the existing graph. Deleted paths |
| 678 | in ``changed_paths`` (paths that no longer exist on disk) are dropped from |
| 679 | the preserved set. When ``changed_paths`` is None the full code corpus is |
| 680 | re-extracted (used by the watcher and post-checkout hook). |
| 681 | |
| 682 | ``acquire_lock`` (default True) takes a non-blocking per-repo flock around |
| 683 | the rebuild so concurrent post-commit hooks across multiple repos do not |
| 684 | pile up. Returns False with a log line if the lock is held. Pass |
| 685 | ``block_on_lock=True`` to wait instead of skip (used by the interactive |
| 686 | ``graphify update`` CLI). |
| 687 | |
| 688 | ``no_cluster`` skips community detection and writes raw merged extraction |
| 689 | JSON to graphify-out/graph.json (mirrors ``extract --no-cluster``). |
| 690 | |
| 691 | Returns True on success, False on error or skipped-due-to-lock. |
| 692 | """ |
| 693 | out = watch_path / _GRAPHIFY_OUT |
| 694 | if acquire_lock: |
| 695 | # #1059: incremental (changed_paths is not None) hooks must not drop |
| 696 | # their change set when another rebuild is already running. Queue |
| 697 | # before attempting the lock so a non-blocking failure still records |
| 698 | # the work; the lock-holder drains the queue and merges it in. Full- |
| 699 | # corpus rebuilds skip the queue entirely — they already cover every |
| 700 | # file, so there is nothing to merge. |
| 701 | if changed_paths is not None and not block_on_lock: |
| 702 | _queue_pending(out, list(changed_paths)) |
| 703 | with _rebuild_lock(out, blocking=block_on_lock) as got: |
| 704 | if not got: |
| 705 | print("[graphify watch] Rebuild already in progress for " |
| 706 | f"{watch_path.resolve()} - changes queued.") |
| 707 | return False |
| 708 | # Lock acquired. Drain anything queued by earlier contenders |
| 709 | # (including, importantly, the paths we just queued ourselves) |
| 710 | # and merge with our own change set so a single rebuild covers |
| 711 | # everything outstanding. |
| 712 | if changed_paths is not None: |
| 713 | merged = _merge_changed_paths(changed_paths, _drain_pending(out)) |
| 714 | else: |
| 715 | # Full-corpus rebuild supersedes any queued incremental work. |
| 716 | _drain_pending(out) |
| 717 | merged = None |