Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/sources/git/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ where
/// `git reset --hard` to the given `obj` for the `repo`.
///
/// The `obj` is a commit-ish to which the head should be moved.
#[tracing::instrument(skip_all)]

@weihanglo weihanglo Jul 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder why. Did you spot anything?

View changes since the review

fn reset(repo: &git2::Repository, obj: &git2::Object<'_>, gctx: &GlobalContext) -> CargoResult<()> {
let mut pb = Progress::new("Checkout", gctx);
let mut opts = git2::build::CheckoutBuilder::new();
Expand Down Expand Up @@ -1006,6 +1007,7 @@ pub fn with_fetch_options(
/// at this time. It could be extended when libgit2 supports shallow clones.
///
/// [`-Zgitoxide`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#gitoxide
#[tracing::instrument(skip_all)]
pub fn fetch(
repo: &mut git2::Repository,
remote_url: &str,
Expand Down Expand Up @@ -1136,6 +1138,7 @@ fn has_shallow_lock_file(err: &crate::sources::git::fetch::Error) -> bool {
/// speed and portability of using `libgit2`.
///
/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/config.html#netgit-fetch-with-cli
#[tracing::instrument(skip(repo, gctx))]
fn fetch_with_cli(
repo: &mut git2::Repository,
url: &str,
Expand Down Expand Up @@ -1194,13 +1197,21 @@ fn fetch_with_cli(
gctx.shell()
.verbose(|s| s.status("Running", &cmd.to_string()))?;
network::retry::with_retry(gctx, || {
cmd.exec()
.map_err(|error| GitCliError::new(error, true).into())
cmd.exec().map_err(|error| {
GitCliError::new(error)
.spurious(true)
.workaround(
"help: re-try with `net.git-fetch-with-cli = false` to see if it resolves the problem
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli",
)
.into()
})
})?;

Ok(())
}

#[tracing::instrument(skip(repo, gctx))]
fn fetch_with_gitoxide(
repo: &mut git2::Repository,
remote_url: &str,
Expand Down Expand Up @@ -1312,6 +1323,7 @@ fn fetch_with_gitoxide(
res
}

#[tracing::instrument(skip(repo, gctx))]
fn fetch_with_libgit2(
repo: &mut git2::Repository,
remote_url: &str,
Expand Down Expand Up @@ -1563,6 +1575,7 @@ enum FastPathRev {
/// this function and move forward on the normal path.
///
/// [^1]: <https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference>
#[tracing::instrument(skip(repo, gctx))]
fn github_fast_path(
repo: &mut git2::Repository,
url: &str,
Expand Down
27 changes: 24 additions & 3 deletions src/util/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,26 @@ pub type GitCliResult = Result<(), GitCliError>;
pub struct GitCliError {
inner: Error,
is_spurious: bool,
workaround: Option<&'static str>,
}

impl GitCliError {
pub fn new(inner: Error, is_spurious: bool) -> GitCliError {
GitCliError { inner, is_spurious }
pub fn new(inner: Error) -> GitCliError {
GitCliError {
inner,
is_spurious: false,
workaround: None,
}
}

pub fn workaround(mut self, workaround: &'static str) -> Self {
self.workaround = Some(workaround);
self
}

pub fn spurious(mut self, yes: bool) -> Self {
self.is_spurious = yes;
self
}

pub fn is_spurious(&self) -> bool {
Expand All @@ -395,7 +410,13 @@ impl std::error::Error for GitCliError {

impl fmt::Display for GitCliError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
self.inner.fmt(f)?;
if let Some(workaround) = self.workaround {
writeln!(f)?;
writeln!(f)?;
write!(f, "{workaround}")?;
}
Ok(())
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/util/network/retry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,9 @@ fn retry_after_parsing() {

#[test]
fn git_cli_error_spurious() {
let error = GitCliError::new(Error::msg("test-git-cli-error"), false);
let error = GitCliError::new(Error::msg("test-git-cli-error")).spurious(false);
assert!(!maybe_spurious(&error.into()));

let error = GitCliError::new(Error::msg("test-git-cli-error"), true);
let error = GitCliError::new(Error::msg("test-git-cli-error")).spurious(true);
assert!(maybe_spurious(&error.into()));
}
77 changes: 77 additions & 0 deletions tests/testsuite/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3275,6 +3275,71 @@ fn git_fetch_cli_env_clean() {
.run();
}

#[cargo_test(requires = "git")]
fn git_fetch_cli_error_suggests_libgit2() {
let git_dep = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_manifest("dep1", "0.5.0"))
.file("src/lib.rs", "")
});

let p = project()
.file(
"Cargo.toml",
&format!(
r#"
[package]
name = "foo"
version = "0.1.0"
edition = "2015"

[dependencies]
dep1 = {{ git = '{}/missing' }}
"#,
git_dep.url()
),
)
.file("src/lib.rs", "")
.file(
".cargo/config.toml",
r#"
[net]
git-fetch-with-cli = true
retry = 0
"#,
)
.build();

p.cargo("fetch")
.with_status(101)
.with_stderr_data(str![[r#"
[UPDATING] git repository `[ROOTURL]/dep1/missing`
fatal: '[ROOT]/dep1/missing' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
[ERROR] failed to get `dep1` as a dependency of package `foo v0.1.0 ([ROOT]/foo)`

Caused by:
failed to load source for dependency `dep1`

Caused by:
unable to update [ROOTURL]/dep1/missing

Caused by:
failed to clone into: [ROOT]/home/.cargo/git/db/missing-[HASH]

Caused by:
process didn't exit successfully: `git fetch [..]` ([EXIT_STATUS]: 128)

[HELP] re-try with `net.git-fetch-with-cli = false` to see if it resolves the problem
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli

"#]])
.run();
}

#[cargo_test]
fn dirty_submodule() {
// `cargo package` warns for dirty file in submodule.
Expand Down Expand Up @@ -4238,10 +4303,19 @@ fn github_fastpath_error_message() {
[UPDATING] git repository `https://github.com/rust-lang/bitflags.git`
fatal: remote [ERROR] upload-pack: not our ref 11111b376b93484341c68fbca3ca110ae5cd2790
[WARNING] spurious network error (3 tries remaining): process didn't exit successfully: `git fetch --no-tags --quiet --force --update-head-ok [..]

[HELP] re-try with `net.git-fetch-with-cli = false` to see if it resolves the problem
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
fatal: remote [ERROR] upload-pack: not our ref 11111b376b93484341c68fbca3ca110ae5cd2790
[WARNING] spurious network error (2 tries remaining): process didn't exit successfully: `git fetch --no-tags --quiet --force --update-head-ok [..]

[HELP] re-try with `net.git-fetch-with-cli = false` to see if it resolves the problem
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
fatal: remote [ERROR] upload-pack: not our ref 11111b376b93484341c68fbca3ca110ae5cd2790
[WARNING] spurious network error (1 try remaining): process didn't exit successfully: `git fetch --no-tags --quiet --force --update-head-ok [..]

[HELP] re-try with `net.git-fetch-with-cli = false` to see if it resolves the problem
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
fatal: remote [ERROR] upload-pack: not our ref 11111b376b93484341c68fbca3ca110ae5cd2790
[ERROR] failed to get `bitflags` as a dependency of package `foo v0.1.0 ([ROOT]/foo)`

Expand All @@ -4260,6 +4334,9 @@ Caused by:
Caused by:
process didn't exit successfully: `git fetch --no-tags --quiet --force --update-head-ok [..]

[HELP] re-try with `net.git-fetch-with-cli = false` to see if it resolves the problem
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli

"#]])
.run();
}
Expand Down