fix(dedup): rank a relative source_file against the scan root, not the CWD (#4281)

Nodes are root-relative by dedup time, but _rank_path resolved them, which
prepends the CWD. From root/firmware, a node keyed as
firmware/firmware/lib/portal.html and its edge as firmware/lib/portal.html,
so the edge never followed the renamed node.

Resolve only absolute paths. _source_key is unchanged: at extraction time
relative paths are CWD-relative.

Co-authored-by: Gijs Mos <1526544+gmos@users.noreply.github.com>
(cherry picked from commit 22f87c266a)
This commit is contained in:
Deepjyoti-Sarmah authored and safishamsi committed 2026-10-11 20:48:53 +01:00
1 parent 22bc4c6ab4
commit f905db770d
1 file changed
+8 -5
+8 -5
View File
@@ -428,13 +428,16 @@ def _lifecycle_penalty(rank_path: str) -> int:
def _rank_path(source_file: str, root: Path | None) -> str:
"""The root-relative form of ``source_file`` used for collision ranking.
Mirrors ``_source_key`` in extractors/resolution.py: with a scan root, an
absolute stored path and its repo-relative twin rank identically, and the
checkout location's own segments never participate (#2532). Without a root
(or when relativizing fails) the normalized stored path is used as-is.
With a scan root, an absolute stored path and its repo-relative twin rank
identically, and the checkout location's own segments never participate
(#2532). A relative stored path is already root-relative and is used as-is;
unlike ``_source_key`` in extractors/resolution.py, it is not resolved
against the CWD, so a run from a subdirectory of the root keys it the same
(#4281). Without a root (or when relativizing fails) the normalized stored
path is used as-is.
"""
normalized = source_file.replace("\\", "/")
if root is not None and normalized:
if root is not None and normalized and Path(normalized).is_absolute():
try:
return Path(normalized).resolve().relative_to(root).as_posix()
except Exception: