From a3388a13e8368b13ec0a84849c2eca20f28b5f87 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 20 Aug 2018 10:01:16 -0700 Subject: [PATCH] Add a configuration option to fetch with git-the-CLI Currently Cargo always uses `libgit2` to perform all fetches of git repositories, but sometimes this is not sufficient. The `libgit2` library doesn't support all authentication schemes that `git` does and it isn't always quite at feature parity with `git` itself, especially in terms of network configuration. This commit adds a configuration option to Cargo for fetching git repositories with the `git` CLI tool rather than the internal `libgit2`. While this exposes us to changes over time in the `git` CLI it's hopefully a very targeted use case that doesn't change much. The internal `libgit2` library should be sufficient for all other forms of git repository management. (and using `git` for only fetches shouldn't slow us down much) The new configuration option in `.cargo/config` is: [net] git-fetch-with-cli = true which can also be specified with `CARGO_NET_GIT_FETCH_WITH_CLI=true` via an environment variable. Closes #5903 --- src/cargo/sources/git/utils.rs | 35 +++++++++++++++++++++-- src/doc/src/reference/config.md | 1 + tests/testsuite/git.rs | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) 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) + ); +}