pub fn normalize_path(path: impl AsRef<Path>) -> StringExpand description
Normalize a path for source-tree identity and matching.
All backslashes are converted to forward slashes, Unicode is normalized to
NFC (composed form), ./.. segments are folded, and the Windows verbatim
prefix (\\?\) is stripped. This is a lexical normalization only — it does
not touch the filesystem.
§Unicode NFC normalization (#1823)
macOS (HFS+/APFS) and git may represent the same path in different Unicode
normalization forms (NFC composed vs NFD decomposed). Without NFC
normalization, files.sort(); files.dedup() in the inventory treats NFC
and NFD forms of the same path as distinct, and finding→entry matching
(normalize_path(finding_path) == normalize_path(entry_path)) produces
false positives/false negatives split across platforms — a real
unwrap() finding goes unreceipted on macOS but matched on Linux.
Normalizing to NFC inside this function ensures all downstream matching,
fingerprinting, and identity keying sees one canonical Unicode form.
§Windows absolute paths (#1821)
normalize_path handles three Windows absolute shapes:
- Verbatim prefix (
\\?\C:\...or\\?\UNC\server\share\...): stripped so the path degrades to its non-verbatim form (C:/...or//server/share/...). This is the case that silently produced wrong identity keys because the\\?\prefix survived as path segments. - Drive letters (
C:\...): preserved asC:/.... The drive letter is a meaningful absolute-path identity component, not a repo-relative segment, and several callers (e.g. migrate evidence diagnostics) pass absolute roots through this function. Stripping it would corrupt those identities. - UNC roots (
\\server\share\...): preserved as//server/share/.... Two leading slashes fold to a single Unix-style absolute root (/) during the segment walk, so//server/share/foo→/server/share/foo.
The scanner resolves finding paths against the source-tree root before calling this function, so repo-relative paths are the normal input.