diff --git a/src/cmd/audit.rs b/src/cmd/audit.rs index cfae3670..60c42cfd 100644 --- a/src/cmd/audit.rs +++ b/src/cmd/audit.rs @@ -48,7 +48,11 @@ pub fn run(sess: &Session, args: &AuditArgs) -> Result<()> { .map(|&pkg| async move { futures::join!( async { *pkg }, - io_ref.dependency_versions(*pkg, args.fetch, None) + io_ref.dependency_versions( + *pkg, + if args.fetch { Some(true) } else { None }, + None + ) ) }) .collect::>(); diff --git a/src/resolver.rs b/src/resolver.rs index cf27aa9d..4c9983ab 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -290,17 +290,30 @@ impl<'ctx> DependencyResolver<'ctx> { tmp }) .collect(); - let ids: IndexSet = names.iter().map(|(_, &id)| id).collect(); // debugln!("resolve: dep names {:?}", names); - // debugln!("resolve: dep ids {:?}", ids); // Determine the available versions for the dependencies. - let versions: Vec<_> = ids + // Skip fetching for dependencies that are locked. + let locked_names: IndexSet<&str> = self + .locked .iter() - .map(|&id| async move { - io.dependency_versions(id, false, None) - .await - .map(move |v| (id, v)) + .filter(|(_, (_, _, _, is_locked))| *is_locked) + .map(|(&name, _)| name) + .collect(); + let fetched_names: IndexSet<&str> = names + .keys() + .filter(|name| !locked_names.contains(*name)) + .copied() + .collect(); + let versions: Vec<_> = names + .iter() + .map(|(&name, &id)| { + let skip = locked_names.contains(name); + async move { + io.dependency_versions(id, if skip { Some(false) } else { None }, None) + .await + .map(move |v| (id, v)) + } }) .collect(); let versions: IndexMap<_, _> = rt @@ -309,6 +322,13 @@ impl<'ctx> DependencyResolver<'ctx> { .collect::>>()? .into_iter() .collect::>(); + + // Record fetched deps so refetch attempts in mark/pick can skip them. + for (&name, &id) in &names { + if fetched_names.contains(name) { + self.refetched.insert((id, None)); + } + } // debugln!("resolve: versions {:#?}", versions); // Register the versions. @@ -456,7 +476,11 @@ impl<'ctx> DependencyResolver<'ctx> { async move { Ok::<(_, _), Error>(( dep.to_string(), - (hash, io.dependency_versions(src, false, None).await?, src), + ( + hash, + io.dependency_versions(src, Some(false), None).await?, + src, + ), )) } }))) @@ -658,7 +682,7 @@ impl<'ctx> DependencyResolver<'ctx> { // attempt plain refetch self.refetched.insert((*id, None)); if let Ok(new_versions) = - rt.block_on(io.dependency_versions(*id, true, None)) + rt.block_on(io.dependency_versions(*id, Some(true), None)) { src.versions = new_versions; } @@ -688,9 +712,11 @@ impl<'ctx> DependencyResolver<'ctx> { if !self.refetched.contains(&(*id, Some(rev.clone()))) { // attempt refetch with rev self.refetched.insert((*id, Some(rev.clone()))); - if let Ok(new_versions) = - rt.block_on(io.dependency_versions(*id, true, Some(rev.as_str()))) - { + if let Ok(new_versions) = rt.block_on(io.dependency_versions( + *id, + Some(true), + Some(rev.as_str()), + )) { src.versions = new_versions; } } @@ -945,7 +971,7 @@ impl<'ctx> DependencyResolver<'ctx> { ); let refetched_versions = rt.block_on(io.dependency_versions( decision.1, - true, + Some(true), fetch_ref.as_deref(), )); if let Ok(new_versions) = refetched_versions { diff --git a/src/sess.rs b/src/sess.rs index 403fc82b..92df6b61 100644 --- a/src/sess.rs +++ b/src/sess.rs @@ -491,10 +491,14 @@ impl<'io, 'sess: 'io, 'ctx: 'sess> SessionIo<'sess, 'ctx> { /// If `fetch_ref` is provided, a specific ref (e.g. a commit hash not on /// any branch) is fetched and a temporary tag is created so that /// `rev-list --all` can discover it. + /// `force_fetch` controls fetching behavior: + /// - `None`: use manifest modification time check (default) + /// - `Some(true)`: force a fetch regardless of modification time + /// - `Some(false)`: skip fetching regardless of modification time pub async fn dependency_versions( &'io self, dep_id: DependencyRef, - force_fetch: bool, + force_fetch: Option, fetch_ref: Option<&str>, ) -> Result> { self.sess.stats.num_calls_dependency_versions.increment(); @@ -548,7 +552,7 @@ impl<'io, 'sess: 'io, 'ctx: 'sess> SessionIo<'sess, 'ctx> { &'io self, name: &str, url: &str, - force_fetch: bool, + force_fetch: Option, fetch_ref: Option<&str>, ) -> Result> { // TODO: Make the assembled future shared and keep it in a lookup table. @@ -631,7 +635,12 @@ impl<'io, 'sess: 'io, 'ctx: 'sess> SessionIo<'sess, 'ctx> { } else { // Update if the manifest has been modified since the last fetch. let db_mtime = try_modification_time(db_dir.join("FETCH_HEAD")); - if (self.sess.manifest_mtime < db_mtime && !force_fetch) || self.sess.local_only { + let skip_fetch = match force_fetch { + Some(true) => false, + Some(false) => true, + None => self.sess.manifest_mtime < db_mtime, + }; + if skip_fetch || self.sess.local_only { debugln!("sess: skipping fetch of {:?}", db_dir); return Ok(git); } @@ -900,7 +909,7 @@ impl<'io, 'sess: 'io, 'ctx: 'sess> SessionIo<'sess, 'ctx> { ); if current == revision { CheckoutState::Clean - } else if let Ok(db) = self.git_database(name, url, false, None).await { + } else if let Ok(db) = self.git_database(name, url, Some(false), None).await { if let Ok(remote) = local_git.clone().remote_url("origin").await { if remote == db.path.to_str().unwrap() { CheckoutState::ToCheckout @@ -968,7 +977,9 @@ impl<'io, 'sess: 'io, 'ctx: 'sess> SessionIo<'sess, 'ctx> { let tag_name_0 = format!("bender-tmp-{}", revision).clone(); let tag_name_1 = tag_name_0.clone(); let tag_name_2 = tag_name_0.clone(); - let git = self.git_database(name, url, false, Some(revision)).await?; + let git = self + .git_database(name, url, Some(false), Some(revision)) + .await?; match git .clone() .spawn_with( @@ -1386,7 +1397,7 @@ impl<'io, 'sess: 'io, 'ctx: 'sess> SessionIo<'sess, 'ctx> { } (DepSrc::Git(url), DepVer::Git(rev)) => { let dep_name = self.sess.intern_string(dep.name.as_str()); - let db = self.git_database(&dep.name, url, false, None).await?; + let db = self.git_database(&dep.name, url, Some(false), None).await?; let entries = db.clone().list_files(rev, Some("Bender.yml")).await?; let data = match entries.into_iter().next() { None => Ok(None),