diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 08ec07ee403..66301c1da1b 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -11,9 +11,10 @@ use serde::ser; use url::Url; use core::GitReference; -use util::{internal, network, Config, Progress, ToUrl}; -use util::paths; use util::errors::{CargoError, CargoResult, CargoResultExt}; +use util::paths; +use util::process_builder::process; +use util::{internal, network, Config, Progress, ToUrl}; #[derive(PartialEq, Clone, Debug)] pub struct GitRevision(git2::Oid); @@ -691,6 +692,18 @@ pub fn fetch( // request we're about to issue. maybe_gc_repo(repo)?; + // Unfortuantely `libgit2` is notably lacking in the realm of authentication + // when compared to the `git` command line. As a result, allow an escape + // hatch for users that would prefer to use `git`-the-CLI for fetching + // repositories instead of `libgit2`-the-library. This should make more + // flavors of authentication possible while also still giving us all the + // speed and portability of using `libgit2`. + if let Some(val) = config.get_bool("net.git-fetch-with-cli")? { + if val.val { + return fetch_with_cli(repo, url, refspec, config); + } + } + debug!("doing a fetch for {}", url); let git_config = git2::Config::open_default()?; with_fetch_options(&git_config, url, config, &mut |mut opts| { @@ -732,6 +745,24 @@ pub fn fetch( }) } +fn fetch_with_cli( + repo: &mut git2::Repository, + url: &Url, + refspec: &str, + config: &Config, +) -> CargoResult<()> { + let mut cmd = process("git"); + cmd.arg("fetch") + .arg("--tags") // fetch all tags + .arg("--quiet") + .arg(url.to_string()) + .arg(refspec) + .cwd(repo.path()); + config.shell().verbose(|s| s.status("Running", &cmd.to_string()))?; + cmd.exec()?; + Ok(()) +} + /// Cargo has a bunch of long-lived git repositories in its global cache and /// some, like the index, are updated very frequently. Right now each update /// creates a new "pack file" inside the git database, and over time this can diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index eb28864f815..1eaf4511e09 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -113,6 +113,7 @@ color = 'auto' # whether cargo colorizes output # Network configuration [net] retry = 2 # number of times a network call will automatically retried +git-fetch-with-cli = false # if `true` we'll use `git`-the-CLI to fetch git repos # Alias cargo commands. The first 3 aliases are built in. If your # command requires grouped whitespace use the list format. diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 8963ad70a43..d4614eddfd2 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -2748,3 +2748,53 @@ fn failed_submodule_checkout() { drop(TcpStream::connect(&addr)); t.join().unwrap(); } + +#[test] +fn use_the_cli() { + let project = project(); + let git_project = git::new("dep1", |project| { + project.file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) + .file("src/lib.rs", "") + }).unwrap(); + + let project = project + .file( + "Cargo.toml", + &format!( + r#" + [project] + name = "foo" + version = "0.5.0" + authors = [] + + [dependencies] + dep1 = {{ git = '{}' }} + "#, + git_project.url() + ), + ) + .file("src/lib.rs", "") + .file( + ".cargo/config", + " + [net] + git-fetch-with-cli = true + " + ) + .build(); + + let stderr = "\ +[UPDATING] git repository `[..]` +[RUNNING] `git fetch [..]` +[COMPILING] dep1 [..] +[RUNNING] `rustc [..]` +[COMPILING] foo [..] +[RUNNING] `rustc [..]` +[FINISHED] [..] +"; + + assert_that( + project.cargo("build -v"), + execs().with_stderr(stderr) + ); +}