fix(git): download each locked rev from its own checkout - #17275
fix(git): download each locked rev from its own checkout#17275weihanglo wants to merge 9 commits into
Conversation
Cargo can generate a lockfile can pining packages from one git URL at different revisions. However, it may lead to donwload failures ``` fails to download with "failed to find ... in path source". ``` The test asserts the current buggy behavior on both * the fetch that creates the state * a fresh fetch with a cold git cache.
cargo still silently serves new checkout's source code Even a dep is locked at old revision in the lock file,
Like lockfile_with_multiple_revisions_change_code_content but with a branch ref that moves forward
The two fields are always set together and describe one checkout. Let make them together
Intended to reuse in next fix commit
This turns single checkout slot into a map from revision to checkout. The `GitSource::locked_rev` then selects the entry in use. This prepares `GitSource` for serving packages pinned at multiple revisions of one git URL.
This is a preparation for the actual fix that we will fetch a revision not from `GitSource::locked_rev`.
This fixes a bug existing for a long time in Cargo. The bug is like `SourceId` Hash impl ignores `precise` [^1] so when a lockfile pins packages from one git URL at several revisions, `SourceMap` in `PackageSet` collapses them into a single `GitSource`, and checks out at one of those revisions. Later when Cargo starts downloading for the other revisions then either fail with "failed to find ... in path source", or serve the wrong revision source. Here we fix this by making one `GitSource` instance able to check out multiple revisions. GitSource's fingerprint prevoiusly also used package's `lockec_rev`, so a package didn't rebuild if a sibling revision was loaded last. This is not an ideal solution. I am not happy with it. However, changing the `Hash` impl for `SourceId` is a bit risky, as `SourceId` rignt now latest serves thre different purposes: * At declaration time (Cargo.toml): what user asked * At resolution time (the actual fetched revision): what user actually got * At record time (Cargo.lock): what user remembered In the future, we should probably reconsider `SourceMap` and `SourceId` identity and equality issues. [^1]: https://github.com/rust-lang/cargo/blob/0d83fa61d55f/src/workspace/source_id.rs#L675-L680
|
r? @epage rustbot has assigned @epage. Use Why was this reviewer chosen?The reviewer was selected based on:
|
| } | ||
|
|
||
| /// Ensures a path source exists and is fetched for `rev`. | ||
| fn ensure_checkout(&self, rev: git2::Oid, source_id: SourceId) -> CargoResult<()> { |
There was a problem hiding this comment.
Looking at this issue from a different angle:
We could teach Cargo to stop producing multi-rev lockfiles, and maybe Cargo should try to resolve to a single rev as possible as it could, which is usually what users want unless explicitly specified. However, multi-rev lockfiles already exist today. I think we should support this kinda of lockfile anyways.
|
Yay, more reasons for me to be against hashes/equality that lie! One thing that isn't clear from this is the relationship between having multiple revisions in a Are we using one source to fetch everything and then later multiple sources for checking out? If so, then that really needs to be called out in |
It is more like this:
I am trying to find a place documenting this hack, though I feel like there are a pile of hacks over lies 😞. |
|
To me, this feels too obscure of an interaction that this seems more likely to trip us up in the future. I don't feel comfortable moving forward with this. |
Don't disagree with that, though in what way it would trip us do you see? Is it like with this fix we settle a "working" behavior that we might not want to offer in the future. Or it is more like the Cargo internal implementation that may hard to refactor in the future? We know that the entire story is bad. My thought was in #17275 (comment). The hack commit be9f5e7 is minimal IMO (plus 69ae9c1 which is meaingless outside the fix commit). Probably we should look at what one-way door we are closing if merging this. Or any suggestion to make this less obscure? I had a hard time figuring out how to change SourceId identify, as it is partially exposed in public interface. Regardless, do we want those tests checked in? |
Future contributors are unlikely to take this into account when making changes, either not having problems surface until running tests, after the investgation and development, wasting their time, or breaking things outside of how we test. |
### What does this PR try to resolve? Testes extracted from <rust-lang#17275>. ### How to test and review this PR? CI passes. We adds tests instead of merging the fix because * We are not satisfied with the solution presented in rust-lang#17275. It adds more obscure interactions. The ideal solution should fix the "root cause" — SourceId identity issue. * For the network issue (lockfile_with_multiple_revisions_bump_pkg_version), people can `cargo update` to make revision aligned (which is usually what people want) * For the bug that silently compiling to wrong revision (the other two tests), that is bad and harder to detect. However, `cargo update` should fix and we could probably have a lint to ensure a single rev in a dependency graph. See <rust-lang#6921>.
What does this PR try to resolve?
Fixes #8101
Fixes #12233
Fixes #13641
Fixes #14230
Fixes #16076
This fixes a bug existing for a long time in Cargo.
The bug is like
SourceIdHash impl ignoresprecise1so when a lockfile pins packages from one git URL at several revisions,
SourceMapinPackageSetcollapses them into a singleGitSource,and checks out at one of those revisions.
Later when Cargo starts downloading for the other revisions
then either
fail with "failed to find ... in path source",
or serve the wrong revision source.
Here we fix this by
making one
GitSourceinstance able to check out multiple revisions.GitSource's fingerprint prevoiusly also used package's
lockec_rev,so a package didn't rebuild if a sibling revision was loaded last.
How to test and review this PR?
The first three commits pin the current behavior,
followed by a series of refactors to build the foundation of the last fix commit.
Note
This is not an ideal solution. I am not happy with it.
However, changing the
Hashimpl forSourceIdis a bit risky,as
SourceIdrignt now serves three different purposes:In the future, we should probably reconsider
SourceMapandSourceIdidentity and equality issues.Footnotes
https://github.com/rust-lang/cargo/blob/0d83fa61d55f/src/workspace/source_id.rs#L675-L680 ↩