mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 07:29:33 +00:00
Let merge graphs restore the previous merged community by node id
merge graphs offsets each input's own communities into a shared id space so they do not fuse (number 3014), but that per source partition has no relation to the previous MERGED clustering a labels sidecar was built against. A later cluster only run's label reuse remap read merge graphs' output as though its community field were that previous clustering, and silently mismatched almost every community since it was actually per source ids. Add a previous flag that reads a prior merged graph.json and restores its real community value onto each node that survives the merge, by node id, before the per source offset is ever written out. A node with no match (new since the previous merge) gets no community rather than keeping an unrelated per source offset, since an invented value is worse than none here. The per source partition stays available regardless, in local community. Fixes #3858.
This commit is contained in:
1 parent
e10df08877
commit
60e153176c
2 files changed
+40
-1
No files matched your search
@@ -760,6 +760,9 @@ def _run_cli() -> None:
|
||||
print(" merge-driver <base> <current> <other> git merge driver: union-merge two graph.json files (set up via hook install)")
|
||||
print(" merge-graphs <g1> <g2> merge two or more graph.json files into one cross-repo graph")
|
||||
print(" --out <path> output path (default: graphify-out/merged-graph.json)")
|
||||
print(" --previous <path> restore the previous merged clustering's community ids")
|
||||
print(" (by node id) instead of each input's own per-source ids,")
|
||||
print(" so a later cluster-only run reuses labels correctly")
|
||||
print(" --branch <branch> checkout a specific branch (default: repo default)")
|
||||
print(" --out <dir> clone to a custom directory (default: ~/.graphify/repos/<owner>/<repo>)")
|
||||
print(" add <url> fetch a URL and save it to ./raw, then update the graph")
|
||||
|
||||
+37
-1
@@ -2663,20 +2663,44 @@ def dispatch_command(cmd: str) -> None:
|
||||
args = sys.argv[2:]
|
||||
graph_paths: list[Path] = []
|
||||
out_path = Path(_GRAPHIFY_OUT) / "merged-graph.json"
|
||||
previous_path: "Path | None" = None
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i] == "--out" and i + 1 < len(args):
|
||||
out_path = Path(args[i + 1])
|
||||
i += 2
|
||||
elif args[i] == "--previous" and i + 1 < len(args):
|
||||
previous_path = Path(args[i + 1])
|
||||
i += 2
|
||||
else:
|
||||
graph_paths.append(Path(args[i]))
|
||||
i += 1
|
||||
if len(graph_paths) < 2:
|
||||
print(
|
||||
"Usage: graphify merge-graphs <graph1.json> <graph2.json> [...] [--out merged.json]",
|
||||
"Usage: graphify merge-graphs <graph1.json> <graph2.json> [...] "
|
||||
"[--out merged.json] [--previous previous-merged-graph.json]",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
# Each input numbers its own communities from 0, so the merged output
|
||||
# carries per-source ids (offset since #3014) that have no relation to
|
||||
# the previous MERGED clustering .graphify_labels.json was written
|
||||
# against. cluster-only's label-reuse remap reads this file's
|
||||
# `community` field as "the previous clustering" and silently mismatches
|
||||
# almost every community when it is actually per-source ids (#3858).
|
||||
# --previous restores the real previous merged `community` onto each
|
||||
# node that survives the merge (by id), so a subsequent cluster-only
|
||||
# run remaps against the clustering its labels were actually built
|
||||
# from. Per-source ids stay available in `local_community` regardless.
|
||||
previous_community: dict[str, int] = {}
|
||||
if previous_path is not None:
|
||||
if not previous_path.exists():
|
||||
print(f"error: --previous file not found: {previous_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
_prev_raw = json.loads(previous_path.read_text(encoding="utf-8"))
|
||||
for _n in _prev_raw.get("nodes", []):
|
||||
if isinstance(_n, dict) and isinstance(_n.get("community"), int) and _n.get("id"):
|
||||
previous_community[_n["id"]] = _n["community"]
|
||||
import networkx as _nx
|
||||
from networkx.readwrite import json_graph as _jg
|
||||
from graphify.build import prefix_graph_for_global as _prefix, distinct_repo_tags as _repo_tags
|
||||
@@ -2781,6 +2805,18 @@ def dispatch_command(cmd: str) -> None:
|
||||
call_links = _link_calls(merged)
|
||||
if call_links:
|
||||
print(f" resolved {call_links} member call(s) across repos")
|
||||
if previous_path is not None:
|
||||
_restored = 0
|
||||
for _nid, _data in merged.nodes(data=True):
|
||||
if _nid in previous_community:
|
||||
_data["community"] = previous_community[_nid]
|
||||
_restored += 1
|
||||
else:
|
||||
_data.pop("community", None)
|
||||
print(
|
||||
f" restored the previous merged community on {_restored} node(s) "
|
||||
f"from {previous_path}"
|
||||
)
|
||||
# Drop whatever compose left behind (the last input's list, possibly
|
||||
# with internal duplicates) so attach_hyperedges dedups the full
|
||||
# collection by id from a clean slate.
|
||||
|
||||
Reference in new issue
Block a user