mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Reuse the result of which git
(#8224)
## Summary Cache the path to git executable in a `LazyLock` and reuse it throughout the process. This might reduce some costs on finding the git executable.
This commit is contained in:
parent
3d27b484ea
commit
0b5cc9595a
7 changed files with 27 additions and 15 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4680,6 +4680,7 @@ dependencies = [
|
|||
"uv-cache-key",
|
||||
"uv-fs",
|
||||
"uv-static",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -32,3 +32,4 @@ thiserror = { workspace = true }
|
|||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
url = { workspace = true }
|
||||
which = { workspace = true }
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use std::fmt::Display;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::{self, FromStr};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::sha::GitOid;
|
||||
use crate::GitSha;
|
||||
|
@ -11,6 +12,7 @@ use anyhow::{anyhow, Context, Result};
|
|||
use cargo_util::{paths, ProcessBuilder};
|
||||
use reqwest::StatusCode;
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
|
||||
use tracing::debug;
|
||||
use url::Url;
|
||||
use uv_fs::Simplified;
|
||||
|
@ -20,6 +22,9 @@ use uv_static::EnvVars;
|
|||
/// checkout is ready to go. See [`GitCheckout::reset`] for why we need this.
|
||||
const CHECKOUT_READY_LOCK: &str = ".ok";
|
||||
|
||||
/// A global cache of the result of `which git`.
|
||||
pub static GIT: LazyLock<Result<PathBuf, which::Error>> = LazyLock::new(|| which::which("git"));
|
||||
|
||||
/// A reference to commit or commit-ish.
|
||||
#[derive(
|
||||
Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
|
||||
|
@ -158,7 +163,7 @@ impl GitRepository {
|
|||
/// Opens an existing Git repository at `path`.
|
||||
pub(crate) fn open(path: &Path) -> Result<GitRepository> {
|
||||
// Make sure there is a Git repository at the specified path.
|
||||
ProcessBuilder::new("git")
|
||||
ProcessBuilder::new(GIT.as_ref()?)
|
||||
.arg("rev-parse")
|
||||
.cwd(path)
|
||||
.exec_with_output()?;
|
||||
|
@ -177,7 +182,7 @@ impl GitRepository {
|
|||
// opts.external_template(false);
|
||||
|
||||
// Initialize the repository.
|
||||
ProcessBuilder::new("git")
|
||||
ProcessBuilder::new(GIT.as_ref()?)
|
||||
.arg("init")
|
||||
.cwd(path)
|
||||
.exec_with_output()?;
|
||||
|
@ -189,7 +194,7 @@ impl GitRepository {
|
|||
|
||||
/// Parses the object ID of the given `refname`.
|
||||
fn rev_parse(&self, refname: &str) -> Result<GitOid> {
|
||||
let result = ProcessBuilder::new("git")
|
||||
let result = ProcessBuilder::new(GIT.as_ref()?)
|
||||
.arg("rev-parse")
|
||||
.arg(refname)
|
||||
.cwd(&self.path)
|
||||
|
@ -295,7 +300,7 @@ impl GitDatabase {
|
|||
|
||||
/// Get a short OID for a `revision`, usually 7 chars or more if ambiguous.
|
||||
pub(crate) fn to_short_id(&self, revision: GitOid) -> Result<String> {
|
||||
let output = ProcessBuilder::new("git")
|
||||
let output = ProcessBuilder::new(GIT.as_ref()?)
|
||||
.arg("rev-parse")
|
||||
.arg("--short")
|
||||
.arg(revision.as_str())
|
||||
|
@ -372,7 +377,7 @@ impl GitCheckout {
|
|||
// Perform a local clone of the repository, which will attempt to use
|
||||
// hardlinks to set up the repository. This should speed up the clone operation
|
||||
// quite a bit if it works.
|
||||
ProcessBuilder::new("git")
|
||||
ProcessBuilder::new(GIT.as_ref()?)
|
||||
.arg("clone")
|
||||
.arg("--local")
|
||||
// Make sure to pass the local file path and not a file://... url. If given a url,
|
||||
|
@ -418,7 +423,7 @@ impl GitCheckout {
|
|||
debug!("reset {} to {}", self.repo.path.display(), self.revision);
|
||||
|
||||
// Perform the hard reset.
|
||||
ProcessBuilder::new("git")
|
||||
ProcessBuilder::new(GIT.as_ref()?)
|
||||
.arg("reset")
|
||||
.arg("--hard")
|
||||
.arg(self.revision.as_str())
|
||||
|
@ -426,7 +431,7 @@ impl GitCheckout {
|
|||
.exec_with_output()?;
|
||||
|
||||
// Update submodules (`git submodule update --recursive`).
|
||||
ProcessBuilder::new("git")
|
||||
ProcessBuilder::new(GIT.as_ref()?)
|
||||
.arg("submodule")
|
||||
.arg("update")
|
||||
.arg("--recursive")
|
||||
|
@ -592,7 +597,7 @@ fn fetch_with_cli(
|
|||
refspecs: &[String],
|
||||
tags: bool,
|
||||
) -> Result<()> {
|
||||
let mut cmd = ProcessBuilder::new("git");
|
||||
let mut cmd = ProcessBuilder::new(GIT.as_ref()?);
|
||||
cmd.arg("fetch");
|
||||
if tags {
|
||||
cmd.arg("--tags");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use url::Url;
|
||||
|
||||
pub use crate::credentials::{store_credentials_from_url, GIT_STORE};
|
||||
pub use crate::git::GitReference;
|
||||
pub use crate::git::{GitReference, GIT};
|
||||
pub use crate::resolver::{
|
||||
GitResolver, GitResolverError, RepositoryReference, ResolvedRepositoryReference,
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ use uv_cli::AuthorFrom;
|
|||
use uv_client::{BaseClientBuilder, Connectivity};
|
||||
use uv_configuration::{VersionControlError, VersionControlSystem};
|
||||
use uv_fs::{Simplified, CWD};
|
||||
use uv_git::GIT;
|
||||
use uv_pep440::Version;
|
||||
use uv_pep508::PackageName;
|
||||
use uv_python::{
|
||||
|
@ -896,14 +897,14 @@ fn get_author_info(path: &Path, author_from: AuthorFrom) -> Option<Author> {
|
|||
|
||||
/// Fetch the default author from git configuration.
|
||||
fn get_author_from_git(path: &Path) -> Result<Author> {
|
||||
let Ok(git) = which::which("git") else {
|
||||
let Ok(git) = GIT.as_ref() else {
|
||||
anyhow::bail!("`git` not found in PATH")
|
||||
};
|
||||
|
||||
let mut name = None;
|
||||
let mut email = None;
|
||||
|
||||
let output = Command::new(&git)
|
||||
let output = Command::new(git)
|
||||
.arg("config")
|
||||
.arg("--get")
|
||||
.arg("user.name")
|
||||
|
@ -915,7 +916,7 @@ fn get_author_from_git(path: &Path) -> Result<Author> {
|
|||
name = Some(String::from_utf8_lossy(&output.stdout).trim().to_string());
|
||||
}
|
||||
|
||||
let output = Command::new(&git)
|
||||
let output = Command::new(git)
|
||||
.arg("config")
|
||||
.arg("--get")
|
||||
.arg("user.email")
|
||||
|
|
|
@ -11397,6 +11397,9 @@ fn git_source_refs() -> Result<()> {
|
|||
fn git_source_missing_tag() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let mut filters = context.filters();
|
||||
filters.push(("`.*/git fetch (.*)`", "`git fetch $1`"));
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(indoc! {r#"
|
||||
[project]
|
||||
|
@ -11410,7 +11413,7 @@ fn git_source_missing_tag() -> Result<()> {
|
|||
uv-public-pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", tag = "missing" }
|
||||
"#})?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.pip_compile()
|
||||
uv_snapshot!(filters, context.pip_compile()
|
||||
.arg("pyproject.toml"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
|
|
|
@ -1594,7 +1594,7 @@ fn install_git_public_https_missing_branch_or_tag() {
|
|||
|
||||
let mut filters = context.filters();
|
||||
// Windows does not style the command the same as Unix, so we must omit it from the snapshot
|
||||
filters.push(("`git fetch .*`", "`git fetch [...]`"));
|
||||
filters.push(("`.*/git(.exe)? fetch .*`", "`git fetch [...]`"));
|
||||
filters.push(("exit status", "exit code"));
|
||||
|
||||
uv_snapshot!(filters, context.pip_install()
|
||||
|
@ -1624,7 +1624,7 @@ fn install_git_public_https_missing_commit() {
|
|||
|
||||
let mut filters = context.filters();
|
||||
// Windows does not style the command the same as Unix, so we must omit it from the snapshot
|
||||
filters.push(("`git fetch .*`", "`git fetch [...]`"));
|
||||
filters.push(("`.*/git(.exe)? fetch .*`", "`git fetch [...]`"));
|
||||
filters.push(("exit status", "exit code"));
|
||||
|
||||
// There are flakes on Windows where this irrelevant error is appended
|
||||
|
@ -1842,6 +1842,7 @@ fn install_git_private_https_pat_not_authorized() {
|
|||
|
||||
let mut filters = context.filters();
|
||||
filters.insert(0, (token, "***"));
|
||||
filters.push(("`.*/git fetch (.*)`", "`git fetch $1`"));
|
||||
|
||||
// We provide a username otherwise (since the token is invalid), the git cli will prompt for a password
|
||||
// and hang the test
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue