Fix/scan into link mount - #193
Open
marcusrognes wants to merge 3 commits into
Open
Conversation
A path-filtered scan of a target that is not yet in state resolved it against the parent state alone, so a target inside a link mount used a NodeID that means something else there. The test covers a new file under a tracked directory in the mount, a new directory in the mount, and the already-working tracked directory, and asserts after each that status still runs and the link registry still holds the mount. The linked repository is filled past one node block so the mismatch is a hard "Invalid block index" rather than a silent hit on a colliding node. Signed-off-by: Marcus Rognes <marcus@rognes.guru>
A path-filtered scan resolves its target through find_node_link, which follows link mounts. A target that is not in state yet - a new file, a new directory - does not resolve, and the walk fell back to the parent state while the path still pointed inside a mount. ensure_scan_dir_chain then walked the parent state through the link node, and the add resolved its parent by path to a NodeID owned by the linked state. A NodeID encodes a block index, so that id addressed an unrelated node in the parent, or a block the parent does not have, and it did so after the state had been mutated: status stopped running and the link registry was lost, on a store repository verify still called healthy. Cross the link in ensure_scan_dir_chain and continue the chain in the state that owns it, as the directory walk already does, and return that repository and state for the traversal to adopt. The single-file scan takes the parent node from the chain instead of resolving it by path, which the state it adds into cannot do below a mount. Two guards behind that: refuse a parent node owned by another repository rather than adding under it, and propagate the add and the dirty mark instead of swallowing them - an add that reaches add_change as INVALID_NODE has already mutated the state it cannot describe. Signed-off-by: Marcus Rognes <marcus@rognes.guru>
A fragment the peer is missing used to come back as the server status NOT_FOUND, whose message the client printed verbatim, so the smoke test looked for "Missing fragment". The miss now returns AddressNotFound and the client names its own context instead, and the assertion failed with "Push failed for unrelated reason" on the error it was written for. Assert on "peer is missing a fragment", the context the push adds where it forwards the error. Signed-off-by: Marcus Rognes <marcus@rognes.guru>
marcusrognes
force-pushed
the
fix/scan-into-link-mount
branch
from
September 8, 2026 17:43
17f7ab5 to
5427c60
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In my workflow i use links quite a bit, my structure is usually this:
/assets <- linked repo
/doc <- other linked repo
/engine
/etc...
If I make changes in assets or doc from this repo, and run
lore status --scan <path>orlore stage --scan <path>on a path inside one of those mounts that the repository doesn't already track, like a new file, or a new directory, the command fails and the local repository is left unusable:On a larger mount it comes out as
Invalid block index: Ninstead.After that, plain lore status no longer runs, and lore link list reports fewer mounts than the repository has.
lore repository verify still reports "Verified repository state integrity" throughout, so nothing warns you that it is broken.
How to reproduce
With a repository mounted at
assets/, add a file the parent doesn't track yet and scan its path:you can also check out f53ea99 and run the
scripts/test/regression/test_link_scan_stage.pytest.The fix:
What it does:
A --scan that targets a path inside a link mount now resolves that path in the repository that owns the mount, instead of against the parent repository's state.
ensure_scan_dir_chaincrosses a link segment and continues in the linked state, the same crossing the whole-tree walk already performs, and hands the resolved parent node to the add, so the node is created where it belongs. Two supporting guards: an add never uses a NodeID owned by another state, and a failed add or dirty-mark aborts instead of being swallowed and continuing over already-mutated state.What it prevents:
The local corruption. Before, the scan added under a foreign NodeID, which either named a block the parent state doesn't have (Invalid block index: N) or silently hit an unrelated node at the same coordinates, after mutating.
What it allows:
Adding new content inside a mount from the parent working copy, with a path-targeted scan, status --scan /, stage --scan /, and the change lands in the linked repository. That includes the case that had no workaround at all: a mount whose tracked content sits only at its root, with no already-tracked subdirectory to name, previously reachable only by cloning the linked repository and committing there.
This might fix some #90 style issues, not sure about that exact case tho.
Checklist
Signed-off-by:line (DCO -- see CONTRIBUTING.md)cargo +nightly fmt --allproduces no changescargo clippy --all-targets -- -D warnings --no-depspasses, same two errors onmain, none added (see note)cargo testpassesuv run pytestpasses, full suite on Windows;test_nodeblock(slow, not in CI's-m smoke) needs--timeout=0locally (see note)docs/updated where behavior changed