Path recovery: never discard local work, and deliver the commits it reports #147

Closed
opened 2026-11-10 19:42:08 +00:00 by james.maes · 0 comments
Owner

Problem

"Resolve" (and --sanitize-paths) was built for a repository whose clone was fetched but never checked out. It is also offered when a pull into an ordinary, checked-out clone finds Windows-invalid paths in the incoming commits, and there it does the wrong thing:

  • it writes the tree of the commit that is already checked out, with force, so uncommitted edits are overwritten;
  • the incoming commits are not delivered, yet the row says Done;
  • the repository then counts as recovery-managed, and the next pull moves its branch to origin's tip without checking that local commits are contained in it.

Smaller defects in the same code:

  • the index is never written, so git status in a recovered repository shows every file as deleted and untracked;
  • .gitattributes only applies to files written after it;
  • a stored "skip" always beats a later "rename" of the same path, although the newer choice is documented to win;
  • an apply that fails part-way leaves no marker, so the next pull can call a partial tree up to date.

Scope

  • Applying a recovery to an ordinary clone refuses when there are uncommitted changes or local commits origin does not have; otherwise it fast-forwards to origin's tip first, so Done means the new commits are on disk, and files that left the tree are removed.
  • A recovery-managed repository is only ever fast-forwarded.
  • The pending marker is set for the whole apply.
  • The index is written; attribute files are materialized first; the newer choice wins when merging recoveries.

Acceptance

A regression test for each item. 100% line coverage holds; zero warnings; format clean.

## Problem "Resolve" (and `--sanitize-paths`) was built for a repository whose clone was fetched but never checked out. It is also offered when a pull into an ordinary, checked-out clone finds Windows-invalid paths in the incoming commits, and there it does the wrong thing: - it writes the tree of the commit that is already checked out, with force, so uncommitted edits are overwritten; - the incoming commits are not delivered, yet the row says Done; - the repository then counts as recovery-managed, and the next pull moves its branch to origin's tip without checking that local commits are contained in it. Smaller defects in the same code: - the index is never written, so `git status` in a recovered repository shows every file as deleted and untracked; - `.gitattributes` only applies to files written after it; - a stored "skip" always beats a later "rename" of the same path, although the newer choice is documented to win; - an apply that fails part-way leaves no marker, so the next pull can call a partial tree up to date. ## Scope - Applying a recovery to an ordinary clone refuses when there are uncommitted changes or local commits origin does not have; otherwise it fast-forwards to origin's tip first, so Done means the new commits are on disk, and files that left the tree are removed. - A recovery-managed repository is only ever fast-forwarded. - The pending marker is set for the whole apply. - The index is written; attribute files are materialized first; the newer choice wins when merging recoveries. ## Acceptance A regression test for each item. 100% line coverage holds; zero warnings; format clean.
james.maes added the bugquality labels 2026-11-10 19:42:08 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/gclo#147