Owner-reported: frequent real-world failures. Repos containing paths that are legal in git but invalid on Windows (trailing spaces/dots, invalid characters, reserved device names like CON/NUL, case-only filename collisions, or paths exceeding MAX_PATH) currently fail checkout with a raw LibGit2SharpException.
Plan (spec agreed with owner)
Phase 1 — quick win (path length): switch clones to two-phase (Checkout = false, then explicit checkout), setting core.longpaths=true in the repo config before any file touches disk. Should eliminate the "filename or folder too long" class outright.
Phase 2 — validation & recovery:
After the no-checkout clone, walk the HEAD tree validating every path: Path.GetInvalidFileNameChars, trailing space/dot, reserved names (CON, PRN, AUX, NUL, COM1-9, LPT1-9), segment length, case-insensitive collisions, and full destination path length (target root + repo path).
Return a structured result (InvalidPathInfo { RepoPath, Reason, SuggestedSanitizedName }) instead of throwing; happy path proceeds to normal checkout.
Recovery contract for UI/CLI: Rename (editable suggested name), Skip, Abort (Clone-in-WSL verb reserved, see the deferred issue). Rename/skip apply a mapping via manual blob materialization (blob.GetContentStream(new FilteringOptions(path)) so .gitattributes filters apply).
Mapped repos are flagged: future updates fetch + re-materialize instead of fast-forward merge (the working tree intentionally diverges from the index), with a clear status.
Abort keeps the existing didn't-exist-before directory cleanup. Log invalid-path counts and chosen recovery action (no sensitive content).
Tests / acceptance
Fixture repos with offending tree entries built via ObjectDatabase plumbing (such files cannot be created on a Windows working tree).
No LibGit2SharpException bubbles to the UI for invalid paths; structured list returned; mapping produces a usable working tree; valid repos are unaffected.
Known limitation to document alongside: LibGit2Sharp 0.31's CloneOptions has no cancelable checkout callback, so cancellation covers transfer/indexing but not the final checkout phase.
**Owner-reported: frequent real-world failures.** Repos containing paths that are legal in git but invalid on Windows (trailing spaces/dots, invalid characters, reserved device names like CON/NUL, case-only filename collisions, or paths exceeding MAX_PATH) currently fail checkout with a raw `LibGit2SharpException`.
## Plan (spec agreed with owner)
**Phase 1 — quick win (path length):** switch clones to two-phase (`Checkout = false`, then explicit checkout), setting `core.longpaths=true` in the repo config before any file touches disk. Should eliminate the "filename or folder too long" class outright.
**Phase 2 — validation & recovery:**
- After the no-checkout clone, walk the HEAD tree validating every path: `Path.GetInvalidFileNameChars`, trailing space/dot, reserved names (CON, PRN, AUX, NUL, COM1-9, LPT1-9), segment length, case-insensitive collisions, and full destination path length (target root + repo path).
- Return a structured result (`InvalidPathInfo { RepoPath, Reason, SuggestedSanitizedName }`) instead of throwing; happy path proceeds to normal checkout.
- Recovery contract for UI/CLI: **Rename** (editable suggested name), **Skip**, **Abort** (Clone-in-WSL verb reserved, see the deferred issue). Rename/skip apply a mapping via manual blob materialization (`blob.GetContentStream(new FilteringOptions(path))` so .gitattributes filters apply).
- Mapped repos are flagged: future updates fetch + re-materialize instead of fast-forward merge (the working tree intentionally diverges from the index), with a clear status.
- Abort keeps the existing didn't-exist-before directory cleanup. Log invalid-path counts and chosen recovery action (no sensitive content).
## Tests / acceptance
- Fixture repos with offending tree entries built via `ObjectDatabase` plumbing (such files cannot be created on a Windows working tree).
- No `LibGit2SharpException` bubbles to the UI for invalid paths; structured list returned; mapping produces a usable working tree; valid repos are unaffected.
Known limitation to document alongside: LibGit2Sharp 0.31's `CloneOptions` has no cancelable checkout callback, so cancellation covers transfer/indexing but not the final checkout phase.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Owner-reported: frequent real-world failures. Repos containing paths that are legal in git but invalid on Windows (trailing spaces/dots, invalid characters, reserved device names like CON/NUL, case-only filename collisions, or paths exceeding MAX_PATH) currently fail checkout with a raw
LibGit2SharpException.Plan (spec agreed with owner)
Phase 1 — quick win (path length): switch clones to two-phase (
Checkout = false, then explicit checkout), settingcore.longpaths=truein the repo config before any file touches disk. Should eliminate the "filename or folder too long" class outright.Phase 2 — validation & recovery:
Path.GetInvalidFileNameChars, trailing space/dot, reserved names (CON, PRN, AUX, NUL, COM1-9, LPT1-9), segment length, case-insensitive collisions, and full destination path length (target root + repo path).InvalidPathInfo { RepoPath, Reason, SuggestedSanitizedName }) instead of throwing; happy path proceeds to normal checkout.blob.GetContentStream(new FilteringOptions(path))so .gitattributes filters apply).Tests / acceptance
ObjectDatabaseplumbing (such files cannot be created on a Windows working tree).LibGit2SharpExceptionbubbles to the UI for invalid paths; structured list returned; mapping produces a usable working tree; valid repos are unaffected.Known limitation to document alongside: LibGit2Sharp 0.31's
CloneOptionshas no cancelable checkout callback, so cancellation covers transfer/indexing but not the final checkout phase.Fixed end to end: core.longpaths + pre-checkout validation (phase 1, shipped in beta.2) and rename/skip recovery with GUI dialog + CLI --sanitize-paths (phase 2, PR #17). WSL verb remains tracked in #8.