Skip to content

lore-revision: discard reverted unstaged adds when a scanned folder vanishes - #1

Open
jochenhz wants to merge 2 commits into
mainfrom
fix/ap2-162-scan-reverted-add-discard
Open

lore-revision: discard reverted unstaged adds when a scanned folder vanishes#1
jochenhz wants to merge 2 commits into
mainfrom
fix/ap2-162-scan-reverted-add-discard

Conversation

@jochenhz

Copy link
Copy Markdown

Motivation

Adding a folder with files to a repository (untracked, never committed) and then deleting the folder from disk leaves permanent phantom D entries in lore status --scan — reproduced on a revision-0 repository reporting D for paths no revision ever contained. The scan persists DirtyDelete on the staged nodes, so every later scan re-reports the phantom subtree.

The missing-file case already handles this (reverted-add discard in diff_filesystem_directory_walk); the missing-directory subtree walk (emit_filesystem_subtree_deletes) and the path-scoped missing-root case (diff_filesystem_missing) did not check membership in the current revision at all.

Change

  • emit_filesystem_subtree_deletes resolves each node's counterpart in the current revision (find_subnode by name hash). During a scan, an unstaged node with no counterpart is a reverted add: files/links are queued for discard; a directory is queued post-order after its children and only when every child was visited and discarded (staged descendants and filter-excluded children keep the ancestor chain alive — no orphaned nodes). A committed directory whose staged-tree children were all discarded still reports its own deletion.
  • apply_pending_discards applies in insertion order with a seen-set dedup instead of sorting by node id — ids are not monotonic with tree depth, and a directory can only be unlinked after its children.
  • diff_filesystem_missing applies the same membership rule via collect_revertible_subtree (aborts to the legacy delete report if the subtree contains a staged node).
  • Membership (not the DirtyAdd flag) is the discriminator, so staged states already wedged by the old behavior — nodes re-marked DirtyDelete by node_mark_dirty's latest-wins replacement — self-heal on their first scan with this fix.

Test Plan

New lore/tests/scan_reverted_add.rs (offline on-disk globals, staged state persisted across status calls):

  • scan_forgets_deleted_untracked_folder — failed before the fix (phantom D for the vanished folder), passes now, including a second scan proving no lingering nodes/flags.
  • scan_reports_deletes_for_committed_folder — guards real deletes; passed before and after.
  • scan_mixed_folder_reports_only_committed_deletes — failed before the fix; committed content still reports D, the untracked file vanishes from the report.

Also green: lore-revision lib tests (130), state/dirty/stage/reset/commit/clone/unstage integration suites, all lore crate tests, cargo clippy -p lore-revision warning-free, cargo fmt.

jochenhz added 2 commits July 17, 2026 12:51
…anishes

A folder created and deleted again without ever being committed left
phantom Delete entries in `status --scan`: the missing-directory subtree
walk marked every staged node DirtyDelete and reported Delete regardless
of whether the node exists in the current revision. The file-level walk
already discarded such reverted adds; directory subtrees (and path-scoped
scans whose root vanished) did not, and because the DirtyDelete flags are
persisted in the staged state, every subsequent scan re-reported the
phantom subtree forever.

The missing-subtree delete walk now resolves each node's counterpart in
the current revision (find_subnode by name hash) and, during a scan,
queues unstaged nodes without a counterpart for discard instead of
marking and reporting them. Directories are discarded post-order after
their children, and only when every child was visited and discarded — a
staged descendant or filter-excluded child keeps the ancestor chain
alive, so no node is orphaned. A committed directory whose staged-tree
children were all discarded still reports its own deletion.
apply_pending_discards now applies in insertion order with a seen-set
dedup: node ids are not monotonic with tree depth, and a directory can
only be unlinked after its children are gone.

The membership check (rather than a DirtyAdd flag check) also heals
staged states already wedged by the old behavior: their nodes were
re-marked DirtyDelete by node_mark_dirty's latest-wins replacement and
are discarded by the first scan running this code.

Regression tests cover the untracked-folder revert, committed-folder
deletes, and the mixed case (committed folder containing an untracked
file), including staged-state cleanliness on repeat scans.

AP2-162
A never-committed empty directory deleted from disk must not be reported
by later scans, independent of whether the scan registered a node for it.
jochenhz pushed a commit that referenced this pull request Jul 17, 2026
Fixed typo in documentation: `lore-revision/README-PROTOCOLS.md`

```
Imported-PR: #1
Imported-From: 3bb4abe
Imported-Base: 4bffa76
Imported-Merge: fc2725e
Imported-Author: Duncan Grist (duncangrist)
Signed-off-by: Duncan Grist <[email protected]>
GH-URL: EpicGames#1
```

Lore-RevId: 302
Lore-Signature: 1450b41da6fd8e46246b1869d8b8f10b1c9e33fef6b568a695c40753af025f16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant