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:
Jo 2024-10-16 01:50:43 +08:00 committed by GitHub
parent 3d27b484ea
commit 0b5cc9595a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 27 additions and 15 deletions

View file

@ -32,3 +32,4 @@ thiserror = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }
which = { workspace = true }

View file

@ -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");

View file

@ -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,
};