normalizePath cleans and normalizes a path for consistent comparison. Uses filepath.Clean (OS-specific separators) and NFC unicode normalization to avoid mismatches between canonically-equivalent strings (e.g. composed vs decomposed forms on some platforms). Tradeoff: canonically-equivalent names co
(path string)
| 109 | // On Windows, we also case-fold to lower to match filesystem semantics and |
| 110 | // avoid false orphans from drive-letter/path casing differences. |
| 111 | func normalizePath(path string) string { |
| 112 | p := filepath.Clean(path) |
| 113 | if runtime.GOOS == goosWindows { |
| 114 | p = strings.ToLower(p) |
| 115 | } |
| 116 | // On Unix, paths can contain arbitrary bytes (not always valid UTF-8). |
| 117 | // Avoid normalizing invalid UTF-8 to prevent replacing bytes with U+FFFD. |
| 118 | if !utf8.ValidString(p) { |
| 119 | return p |
| 120 | } |
| 121 | if !norm.NFC.IsNormalString(p) { |
| 122 | p = norm.NFC.String(p) |
| 123 | } |
| 124 | return p |
| 125 | } |
| 126 | |
| 127 | // canonicalizeHash matches SyncManager's internal hash normalization. |
| 128 | func canonicalizeHash(hash string) string { |