Conversation
5ef5722 to
9febc1f
Compare
|
@cachebag Hi, thanks for this patch! Looking at #1138, it seems like it refers to a design question rather than a bug. Plus there are 3 questions being raised there, and it is not immediately clear which of the 3 you are addressing here. Given that, before starting the review process, I would like to know from your PR description more info about the proposed change, such as:
For a concrete example of the report format that you may use, please refer to #4947. |
Absolutely. Sorry about the confusion. I've updated the PR description with the requested information. |
9febc1f to
d40b684
Compare
This comment has been minimized.
This comment has been minimized.
Overrides are keyed by canonical path, so `rustup override unset --path` resolved a symlink and removed the override of its target instead. An override set on the path before it was turned into a symlink stayed behind, with no way to remove it (rust-lang#1138). `Settings::remove_override()` now first tries the key of the path itself, i.e. its canonical parent joined with its file name, and only falls back to the resolved path when there is no such override.
Overrides are looked up by canonical path, so an override keyed by a path that has since been turned into a symlink can never apply. Yet `rustup override list` showed it as a regular entry and `rustup override unset --nonexistent` left it in place, since both checked `Path::is_dir()`, which follows symlinks (rust-lang#1138). Both now treat such a path as nonexistent as well. Removing it relies on `Settings::remove_override()` removing the override of the symlink itself rather than that of its target.
d40b684 to
b38ae8d
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
@cachebag Thanks a whole lot for your report! Looking at the current situation, it seems that the underlying issue is surprising similar to #4947, where the value saved to For this part, maybe we shouldn't do the canonicalization at all as long as the resulting path is valid, and instead we just join CWD and the provided path upon writing? I know it's a breaking change, but it should be quite minor, I'd say at the same level as #4947. However, we should also consider that since we are storing a dictionary rather than a string, there is another layer where we look for the CWD-keyed entry in: overrides: BTreeMap<String, String>,For the key identity part, we already import I'd like to know your opinions on that matter, and if I got anything wrong here please don't hesitate to correct me. Also cc @ChrisDenton: I'm wondering if the issue with false positives on ReFS1 is worth worrying about on our side regarding our usage of Footnotes
|
|
@rami3l
i agree here. actually i think everything you are mentioning is far better as a solution than mine. under this model an entry on a symlink isn't stale, it just resolves at lookup time. so my last commit ( nicely enough, it also turns out that paths that only differ by case on case-insensitive filesystems (APFS), and re: writes:
for lookup i'm thinking that we can exact string match first. it's cheap and keeps existing (canonical) keys working the same after these changes are merged. else, we can do a same-dir match via
I don't know actually, I have to also think about this more. This runs on every proxy invocation, and the exact-match fast path only helps in dirs that actually have an override. in every other dir we'd open every key on every SO I think yes? But, I would love to measure the overhead before/after if that helps inform the decision though. Let me know if you think otherwise. This is the rough shape I have in mind as far as commits, please let me know if it makes sense to you:
|
@cachebag Wow that's really messy, thanks for pointing out that subtle detail. I'm thinking about 3 cases then:
If you mean going through literal tests first and then fall back to
I'd be glad if the measurements could be made, and actually I'm also moving on that front but not sure if my patch can land while you are working on this issue. See #5103 and #2626. For the moment being you might really need to do some manual testing with
Yeah, looks great to me as long as my analysis above sounds reasonable to you :) |
Closes #1138
Problem Description
The crux of the original problem as seen in #1138, was laid out as such...
Overrides are stored under the directory's resolved path, and lookups resolve the current directory the same way.
If an overridden directory is later replaced by a symlink (say a project moved, and an old path symlinked to the new one), the old entry keeps the old path and can never match again.
This emits 3 main consequences:
unset --path <symlink>removes the target's override instead. The only way to get rid of the dead entry is editingsettings.tomlby hand.Proposed Solution
There are a few different ways we can approach this.
We can store or match overrides by the unresolved path (or
$PWD). That would fix (1), but it changes how every override works, and would need a migration, and$PWDdepends on the shell.We can also keep storage and lookup: essentially, only fix how dead entries are reported and removed. That fixes (2) and (3).
Options for (3):
Options for (2):
This PR employs the following:
And it makes the most sense to me given the problem space.
Impact
There is not much fallout of anything other than the fact that
unset --path <symlink>now removes the symlink's own entry if there is one. Before, it silently removed the target's override. Otherwise the behavior is the same.listand also marks these entries "(not a directory)" and--nonexistentremoves them. But this is safe because lookups resolve symlinks, so such entries can never match anything.Follow-up work
If we wanted, we could extend
--nonexistentto entries whose parent directory became a symlink. This would need careful Windows coverage, since the new tests are Unix-only.Performance
Validation
Validated through:
Each fix follows a test commit that pins today's behavior, and the fix commit flips that snapshot.