Skip to content

Commit 7755f01

Browse files
committed
cache submodule into git db
1 parent e7246c6 commit 7755f01

File tree

3 files changed

+72
-63
lines changed

3 files changed

+72
-63
lines changed

src/cargo/sources/git/source.rs

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::core::global_cache_tracker;
66
use crate::core::{Dependency, Package, PackageId};
77
use crate::sources::IndexSummary;
88
use crate::sources::RecursivePathSource;
9+
use crate::sources::git::utils::GitDatabase;
910
use crate::sources::git::utils::GitRemote;
1011
use crate::sources::git::utils::rev_to_oid;
1112
use crate::sources::source::MaybePackage;
@@ -18,6 +19,7 @@ use crate::util::hex::short_hash;
1819
use crate::util::interning::InternedString;
1920
use anyhow::Context as _;
2021
use cargo_util::paths::exclude_from_backups_and_indexing;
22+
use git2::Oid;
2123
use std::fmt::{self, Debug, Formatter};
2224
use std::task::Poll;
2325
use tracing::trace;
@@ -155,6 +157,63 @@ impl<'gctx> GitSource<'gctx> {
155157
});
156158
Ok(())
157159
}
160+
161+
pub(crate) fn update_db(&self) -> CargoResult<(GitDatabase, Oid)> {
162+
let db_path = self.gctx.git_db_path().join(&self.ident);
163+
let db_path = db_path.into_path_unlocked();
164+
165+
let db = self.remote.db_at(&db_path).ok();
166+
167+
let (db, actual_rev) = match (&self.locked_rev, db) {
168+
// If we have a locked revision, and we have a preexisting database
169+
// which has that revision, then no update needs to happen.
170+
(Revision::Locked(oid), Some(db)) if db.contains(*oid) => (db, *oid),
171+
172+
// If we're in offline mode, we're not locked, and we have a
173+
// database, then try to resolve our reference with the preexisting
174+
// repository.
175+
(Revision::Deferred(git_ref), Some(db)) if !self.gctx.network_allowed() => {
176+
let offline_flag = self
177+
.gctx
178+
.offline_flag()
179+
.expect("always present when `!network_allowed`");
180+
let rev = db.resolve(&git_ref).with_context(|| {
181+
format!(
182+
"failed to lookup reference in preexisting repository, and \
183+
can't check for updates in offline mode ({offline_flag})"
184+
)
185+
})?;
186+
(db, rev)
187+
}
188+
189+
// ... otherwise we use this state to update the git database. Note
190+
// that we still check for being offline here, for example in the
191+
// situation that we have a locked revision but the database
192+
// doesn't have it.
193+
(locked_rev, db) => {
194+
if let Some(offline_flag) = self.gctx.offline_flag() {
195+
anyhow::bail!(
196+
"can't checkout from '{}': you are in the offline mode ({offline_flag})",
197+
self.remote.url()
198+
);
199+
}
200+
201+
if !self.quiet {
202+
self.gctx.shell().status(
203+
"Updating",
204+
format!("git repository `{}`", self.remote.url()),
205+
)?;
206+
}
207+
208+
trace!("updating git source `{:?}`", self.remote);
209+
210+
let locked_rev = locked_rev.clone().into();
211+
self.remote.checkout(&db_path, db, &locked_rev, self.gctx)?
212+
}
213+
};
214+
215+
Ok((db, actual_rev))
216+
}
158217
}
159218

160219
/// Indicates a [Git revision] that might be locked or deferred to be resolved.
@@ -286,58 +345,7 @@ impl<'gctx> Source for GitSource<'gctx> {
286345
// exists.
287346
exclude_from_backups_and_indexing(&git_path);
288347

289-
let db_path = self.gctx.git_db_path().join(&self.ident);
290-
let db_path = db_path.into_path_unlocked();
291-
292-
let db = self.remote.db_at(&db_path).ok();
293-
294-
let (db, actual_rev) = match (&self.locked_rev, db) {
295-
// If we have a locked revision, and we have a preexisting database
296-
// which has that revision, then no update needs to happen.
297-
(Revision::Locked(oid), Some(db)) if db.contains(*oid) => (db, *oid),
298-
299-
// If we're in offline mode, we're not locked, and we have a
300-
// database, then try to resolve our reference with the preexisting
301-
// repository.
302-
(Revision::Deferred(git_ref), Some(db)) if !self.gctx.network_allowed() => {
303-
let offline_flag = self
304-
.gctx
305-
.offline_flag()
306-
.expect("always present when `!network_allowed`");
307-
let rev = db.resolve(&git_ref).with_context(|| {
308-
format!(
309-
"failed to lookup reference in preexisting repository, and \
310-
can't check for updates in offline mode ({offline_flag})"
311-
)
312-
})?;
313-
(db, rev)
314-
}
315-
316-
// ... otherwise we use this state to update the git database. Note
317-
// that we still check for being offline here, for example in the
318-
// situation that we have a locked revision but the database
319-
// doesn't have it.
320-
(locked_rev, db) => {
321-
if let Some(offline_flag) = self.gctx.offline_flag() {
322-
anyhow::bail!(
323-
"can't checkout from '{}': you are in the offline mode ({offline_flag})",
324-
self.remote.url()
325-
);
326-
}
327-
328-
if !self.quiet {
329-
self.gctx.shell().status(
330-
"Updating",
331-
format!("git repository `{}`", self.remote.url()),
332-
)?;
333-
}
334-
335-
trace!("updating git source `{:?}`", self.remote);
336-
337-
let locked_rev = locked_rev.clone().into();
338-
self.remote.checkout(&db_path, db, &locked_rev, self.gctx)?
339-
}
340-
};
348+
let (db, actual_rev) = self.update_db()?;
341349

342350
// Don’t use the full hash, in order to contribute less to reaching the
343351
// path length limit on Windows. See

src/cargo/sources/git/utils.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! Utilities for handling git repositories, mainly around
22
//! authentication/cloning.
33
4-
use crate::core::{GitReference, Verbosity};
4+
use crate::core::{GitReference, SourceId, Verbosity};
55
use crate::sources::git::fetch::RemoteKind;
66
use crate::sources::git::oxide;
77
use crate::sources::git::oxide::cargo_config_to_gitoxide_overrides;
8+
use crate::sources::git::source::GitSource;
9+
use crate::sources::source::Source;
810
use crate::util::HumanBytes;
911
use crate::util::errors::{CargoResult, GitCliError};
1012
use crate::util::{GlobalContext, IntoUrl, MetricsCounter, Progress, network};
@@ -447,7 +449,7 @@ impl<'a> GitCheckout<'a> {
447449
let target = repo.head()?.target();
448450
Ok((target, repo))
449451
});
450-
let mut repo = match head_and_repo {
452+
let repo = match head_and_repo {
451453
Ok((head, repo)) => {
452454
if child.head_id() == head {
453455
return update_submodules(&repo, gctx, &child_remote_url);
@@ -461,24 +463,23 @@ impl<'a> GitCheckout<'a> {
461463
}
462464
};
463465
// Fetch data from origin and reset to the head commit
464-
let reference = GitReference::Rev(head.to_string());
465466
gctx.shell()
466467
.status("Updating", format!("git submodule `{child_remote_url}`"))?;
467-
fetch(
468-
&mut repo,
469-
&child_remote_url,
470-
&reference,
468+
let mut source = GitSource::new(
469+
SourceId::from_url(&format!("git+{child_remote_url}#{head}"))?,
471470
gctx,
472-
RemoteKind::GitDependency,
473471
)
474472
.with_context(|| {
475473
let name = child.name().unwrap_or("");
476474
format!("failed to fetch submodule `{name}` from {child_remote_url}",)
477475
})?;
476+
source.set_quiet(true);
477+
478+
let (db, actual_rev) = source.update_db()?;
479+
db.copy_to(actual_rev, &repo.path(), gctx)?;
478480

479481
let obj = repo.find_object(head, None)?;
480-
reset(&repo, &obj, gctx)?;
481-
update_submodules(&repo, gctx, &child_remote_url)
482+
reset(&repo, &obj, gctx)
482483
}
483484
}
484485
}

tests/testsuite/git.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,7 @@ Caused by:
10601060
failed to update submodule `src`
10611061
10621062
Caused by:
1063-
object not found - no match for id ([..]); class=Odb (9); code=NotFound (-3)
1063+
revspec '[..]' not found; class=Reference (4); code=NotFound (-3)
10641064
10651065
"#]];
10661066

0 commit comments

Comments
 (0)