Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Git LFS support to uv-git crate #10335

Merged
merged 9 commits into from
Jan 13, 2025
53 changes: 52 additions & 1 deletion crates/uv-git/src/git.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Git support is derived from Cargo's implementation.
//! Cargo is dual-licensed under either Apache 2.0 or MIT, at the user's choice.
//! Source: <https://github.com/rust-lang/cargo/blob/23eb492cf920ce051abfc56bbaf838514dc8365c/src/cargo/sources/git/utils.rs>
use std::env;
use std::fmt::Display;
use std::path::{Path, PathBuf};
use std::str::{self, FromStr};
Expand All @@ -13,7 +14,7 @@ use cargo_util::{paths, ProcessBuilder};
use reqwest::StatusCode;
use reqwest_middleware::ClientWithMiddleware;

use tracing::debug;
use tracing::{debug, warn};
use url::Url;
use uv_fs::Simplified;
use uv_static::EnvVars;
Expand Down Expand Up @@ -251,6 +252,8 @@ impl GitRemote {
) -> Result<(GitDatabase, GitOid)> {
let locked_ref = locked_rev.map(|oid| GitReference::FullCommit(oid.to_string()));
let reference = locked_ref.as_ref().unwrap_or(reference);
let enable_lfs_fetch = env::var(EnvVars::UV_GIT_LFS).is_ok();

if let Some(mut db) = db {
fetch(&mut db.repo, self.url.as_str(), reference, client)
.with_context(|| format!("failed to fetch into: {}", into.user_display()))?;
Expand All @@ -261,6 +264,10 @@ impl GitRemote {
};

if let Some(rev) = resolved_commit_hash {
if enable_lfs_fetch {
fetch_lfs(&mut db.repo, self.url.as_str(), &rev)
.with_context(|| format!("failed to fetch LFS objects at {rev}"))?;
}
return Ok((db, rev));
}
}
Expand All @@ -280,6 +287,10 @@ impl GitRemote {
Some(rev) => rev,
None => reference.resolve(&repo)?,
};
if enable_lfs_fetch {
fetch_lfs(&mut repo, self.url.as_str(), &rev)
.with_context(|| format!("failed to fetch LFS objects at {rev}"))?;
}

Ok((GitDatabase { repo }, rev))
}
Expand Down Expand Up @@ -635,6 +646,46 @@ fn fetch_with_cli(
// The required `on...line` callbacks currently do nothing.
// The output appears to be included in error messages by default.
cmd.exec_with_output()?;

Ok(())
}

/// A global cache of the `git lfs` command.
///
/// Returns an error if Git LFS isn't available.
/// Caching the command allows us to only check if LFS is installed once.
static GIT_LFS: LazyLock<Result<ProcessBuilder>> = LazyLock::new(|| {
let mut cmd = ProcessBuilder::new(GIT.as_ref()?);
cmd.arg("lfs");

// Run a simple command to verify LFS is installed
cmd.clone().arg("version").exec_with_output()?;
Ok(cmd)
});

/// Attempts to use `git-lfs` CLI to fetch required LFS objects for a given revision.
fn fetch_lfs(repo: &mut GitRepository, url: &str, revision: &GitOid) -> Result<()> {
let mut cmd = if let Ok(lfs) = GIT_LFS.as_ref() {
debug!("Fetching Git LFS objects");
lfs.clone()
} else {
// Since this feature is opt-in, warn if not available
warn!("Git LFS is not available, skipping LFS fetch");
return Ok(());
};

cmd.arg("fetch")
.arg(url)
.arg(revision.as_str())
// These variables are unset for the same reason as in `fetch_with_cli`.
.env_remove(EnvVars::GIT_DIR)
.env_remove(EnvVars::GIT_WORK_TREE)
.env_remove(EnvVars::GIT_INDEX_FILE)
.env_remove(EnvVars::GIT_OBJECT_DIRECTORY)
.env_remove(EnvVars::GIT_ALTERNATE_OBJECT_DIRECTORIES)
.cwd(&repo.path);

cmd.exec_with_output()?;
Ok(())
}

Expand Down
3 changes: 3 additions & 0 deletions crates/uv-static/src/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,4 +575,7 @@ impl EnvVars {

/// Skip writing `uv` installer metadata files (e.g., `INSTALLER`, `REQUESTED`, and `direct_url.json`) to site-packages `.dist-info` directories.
pub const UV_NO_INSTALLER_METADATA: &'static str = "UV_NO_INSTALLER_METADATA";

/// Enables fetching files stored in Git LFS when installing a package from a Git repository.
pub const UV_GIT_LFS: &'static str = "UV_GIT_LFS";
}
4 changes: 4 additions & 0 deletions docs/configuration/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ updating the `uv.lock` file.

Equivalent to the `--token` argument for self update. A GitHub token for authentication.

### `UV_GIT_LFS`

Enables fetching files stored in Git LFS when installing a package from a Git repository.

### `UV_HTTP_TIMEOUT`

Timeout (in seconds) for HTTP requests. (default: 30 s)
Expand Down
Loading