diff --git a/crates/uv-git/src/resolver.rs b/crates/uv-git/src/resolver.rs index 8d92a56a575e..fcd71692c9b1 100644 --- a/crates/uv-git/src/resolver.rs +++ b/crates/uv-git/src/resolver.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::path::PathBuf; use std::sync::Arc; @@ -47,6 +48,18 @@ impl GitResolver { ) -> Result { debug!("Fetching source distribution from Git: {url}"); + let reference = RepositoryReference::from(url); + + // If we know the precise commit already, reuse it, to ensure that all fetches within a + // single process are consistent. + let url = { + if let Some(precise) = self.get(&reference) { + Cow::Owned(url.clone().with_precise(*precise)) + } else { + Cow::Borrowed(url) + } + }; + // Avoid races between different processes, too. let lock_dir = cache.join("locks"); fs::create_dir_all(&lock_dir).await?; @@ -58,17 +71,17 @@ impl GitResolver { // Fetch the Git repository. let source = if let Some(reporter) = reporter { - GitSource::new(url.clone(), client, cache).with_reporter(reporter) + GitSource::new(url.as_ref().clone(), client, cache).with_reporter(reporter) } else { - GitSource::new(url.clone(), client, cache) + GitSource::new(url.as_ref().clone(), client, cache) }; let fetch = tokio::task::spawn_blocking(move || source.fetch()) .await? .map_err(GitResolverError::Git)?; - // Insert the resolved URL into the in-memory cache. + // Insert the resolved URL into the in-memory cache. This ensures that subsequent fetches + // resolve to the same precise commit. if let Some(precise) = fetch.git().precise() { - let reference = RepositoryReference::from(url); self.insert(reference, precise); } diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 4608b9e9f39c..e899a48d6284 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -387,6 +387,7 @@ async fn do_lock( // Populate the Git resolver. for ResolvedRepositoryReference { reference, sha } in git { + debug!("Inserting Git reference into resolver: `{reference:?}` at `{sha}`"); state.git.insert(reference, sha); } diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index 32e08e598259..4c75c2888721 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -2385,9 +2385,36 @@ fn lock_git_sha() -> Result<()> { // Rewrite the lockfile, as if it were locked against `main`. let lock = lock.replace("rev=0dacfd662c64cb4ceb16e6cf65a157a8b715b979", "rev=main"); + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12" + + [options] + exclude-newer = "2024-03-25 00:00:00 UTC" + + [[distribution]] + name = "project" + version = "0.1.0" + source = { editable = "." } + dependencies = [ + { name = "uv-public-pypackage" }, + ] + + [[distribution]] + name = "uv-public-pypackage" + version = "0.1.0" + source = { git = "https://github.com/astral-test/uv-public-pypackage?rev=main#0dacfd662c64cb4ceb16e6cf65a157a8b715b979" } + "### + ); + }); + fs_err::write(context.temp_dir.join("uv.lock"), lock)?; - // Lock `anyio` against `main`. + // Lock against `main`. let pyproject_toml = context.temp_dir.child("pyproject.toml"); pyproject_toml.write_str( r#" @@ -2436,7 +2463,7 @@ fn lock_git_sha() -> Result<()> { [[distribution]] name = "uv-public-pypackage" version = "0.1.0" - source = { git = "https://github.com/astral-test/uv-public-pypackage?rev=main#b270df1a2fb5d012294e9aaf05e7e0bab1e6a389" } + source = { git = "https://github.com/astral-test/uv-public-pypackage?rev=main#0dacfd662c64cb4ceb16e6cf65a157a8b715b979" } "### ); });