Skip to content

Commit

Permalink
Auto merge of #14036 - weihanglo:gix-list, r=hi-rustin
Browse files Browse the repository at this point in the history
fix: remove `__CARGO_GITOXIDE_DISABLE_LIST_FILES` env var

### What does this PR try to resolve?

`__CARGO_GITOXIDE_DISABLE_LIST_FILES` is temporary and supposed to remove before 1.79 is out. However nobody remembers this.

See #13696

### How should we test and review this PR?

The test suite should have covered it.

### Additional information
<!-- homu-ignore:end -->
  • Loading branch information
bors committed Jun 10, 2024
2 parents b3425d8 + 911a10f commit dba851d
Showing 1 changed file with 3 additions and 220 deletions.
223 changes: 3 additions & 220 deletions src/cargo/sources/path.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::HashSet;
use std::fmt::{self, Debug, Formatter};
use std::path::{Path, PathBuf};
use std::task::Poll;
Expand Down Expand Up @@ -422,16 +421,7 @@ fn _list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult<Vec<PathBuf>>
let root = pkg.root();
let no_include_option = pkg.manifest().include().is_empty();
let git_repo = if no_include_option {
if gctx
.get_env("__CARGO_GITOXIDE_DISABLE_LIST_FILES")
.ok()
.as_deref()
== Some("1")
{
discover_git_repo(root)?.map(Git2OrGixRepository::Git2)
} else {
discover_gix_repo(root)?.map(Git2OrGixRepository::Gix)
}
discover_gix_repo(root)?
} else {
None
};
Expand Down Expand Up @@ -489,61 +479,12 @@ fn _list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult<Vec<PathBuf>>
// Attempt Git-prepopulate only if no `include` (see rust-lang/cargo#4135).
if no_include_option {
if let Some(repo) = git_repo {
return match repo {
Git2OrGixRepository::Git2(repo) => list_files_git(pkg, &repo, &filter, gctx),
Git2OrGixRepository::Gix(repo) => list_files_gix(pkg, &repo, &filter, gctx),
};
return list_files_gix(pkg, &repo, &filter, gctx);
}
}
list_files_walk(pkg, &filter, gctx)
}

enum Git2OrGixRepository {
Git2(git2::Repository),
Gix(gix::Repository),
}

/// Returns `Some(git2::Repository)` if found sibling `Cargo.toml` and `.git`
/// directory; otherwise, caller should fall back on full file list.
fn discover_git_repo(root: &Path) -> CargoResult<Option<git2::Repository>> {
let repo = match git2::Repository::discover(root) {
Ok(repo) => repo,
Err(e) => {
tracing::debug!(
"could not discover git repo at or above {}: {}",
root.display(),
e
);
return Ok(None);
}
};
let index = repo
.index()
.with_context(|| format!("failed to open git index at {}", repo.path().display()))?;
let repo_root = repo.workdir().ok_or_else(|| {
anyhow::format_err!(
"did not expect repo at {} to be bare",
repo.path().display()
)
})?;
let repo_relative_path = match paths::strip_prefix_canonical(root, repo_root) {
Ok(p) => p,
Err(e) => {
warn!(
"cannot determine if path `{:?}` is in git repo `{:?}`: {:?}",
root, repo_root, e
);
return Ok(None);
}
};
let manifest_path = repo_relative_path.join("Cargo.toml");
if index.get_path(&manifest_path, 0).is_some() {
return Ok(Some(repo));
}
// Package Cargo.toml is not in git, don't use git to guide our selection.
Ok(None)
}

/// Returns [`Some(gix::Repository)`](gix::Repository) if the discovered repository
/// (searched upwards from `root`) contains a tracked `<root>/Cargo.toml`.
/// Otherwise, the caller should fall back on full file list.
Expand Down Expand Up @@ -589,164 +530,6 @@ fn discover_gix_repo(root: &Path) -> CargoResult<Option<gix::Repository>> {
Ok(None)
}

/// Lists files relevant to building this package inside this source by
/// consulting both Git index (tracked) or status (untracked) under
/// a given Git repository.
///
/// This looks into Git submodules as well.
fn list_files_git(
pkg: &Package,
repo: &git2::Repository,
filter: &dyn Fn(&Path, bool) -> bool,
gctx: &GlobalContext,
) -> CargoResult<Vec<PathBuf>> {
debug!("list_files_git {}", pkg.package_id());
let index = repo.index()?;
let root = repo
.workdir()
.ok_or_else(|| anyhow::format_err!("can't list files on a bare repository"))?;
let pkg_path = pkg.root();

let mut ret = Vec::<PathBuf>::new();

// We use information from the Git repository to guide us in traversing
// its tree. The primary purpose of this is to take advantage of the
// `.gitignore` and auto-ignore files that don't matter.
//
// Here we're also careful to look at both tracked and untracked files as
// the untracked files are often part of a build and may become relevant
// as part of a future commit.
let index_files = index.iter().map(|entry| {
use libgit2_sys::{GIT_FILEMODE_COMMIT, GIT_FILEMODE_LINK};
// ``is_dir`` is an optimization to avoid calling
// ``fs::metadata`` on every file.
let is_dir = if entry.mode == GIT_FILEMODE_LINK as u32 {
// Let the code below figure out if this symbolic link points
// to a directory or not.
None
} else {
Some(entry.mode == GIT_FILEMODE_COMMIT as u32)
};
(join(root, &entry.path), is_dir)
});
let mut opts = git2::StatusOptions::new();
opts.include_untracked(true);
if let Ok(suffix) = pkg_path.strip_prefix(root) {
opts.pathspec(suffix);
}
let statuses = repo.statuses(Some(&mut opts))?;
let mut skip_paths = HashSet::new();
let untracked: Vec<_> = statuses
.iter()
.filter_map(|entry| {
match entry.status() {
// Don't include Cargo.lock if it is untracked. Packaging will
// generate a new one as needed.
git2::Status::WT_NEW if entry.path() != Some("Cargo.lock") => {
Some(Ok((join(root, entry.path_bytes()), None)))
}
git2::Status::WT_DELETED => {
let path = match join(root, entry.path_bytes()) {
Ok(p) => p,
Err(e) => return Some(Err(e)),
};
skip_paths.insert(path);
None
}
_ => None,
}
})
.collect::<CargoResult<_>>()?;

let mut subpackages_found = Vec::new();

for (file_path, is_dir) in index_files.chain(untracked) {
let file_path = file_path?;
if skip_paths.contains(&file_path) {
continue;
}

// Filter out files blatantly outside this package. This is helped a
// bit above via the `pathspec` function call, but we need to filter
// the entries in the index as well.
if !file_path.starts_with(pkg_path) {
continue;
}

match file_path.file_name().and_then(|s| s.to_str()) {
// The `target` directory is never included.
Some("target") => {
// Only filter out target if its in the package root.
if file_path.parent().unwrap() == pkg_path {
continue;
}
}

// Keep track of all sub-packages found and also strip out all
// matches we've found so far. Note, though, that if we find
// our own `Cargo.toml`, we keep going.
Some("Cargo.toml") => {
let path = file_path.parent().unwrap();
if path != pkg_path {
debug!("subpackage found: {}", path.display());
ret.retain(|p| !p.starts_with(path));
subpackages_found.push(path.to_path_buf());
continue;
}
}

_ => {}
}

// If this file is part of any other sub-package we've found so far,
// skip it.
if subpackages_found.iter().any(|p| file_path.starts_with(p)) {
continue;
}

// `is_dir` is None for symlinks. The `unwrap` checks if the
// symlink points to a directory.
let is_dir = is_dir.unwrap_or_else(|| file_path.is_dir());
if is_dir {
trace!(" found directory {}", file_path.display());
match git2::Repository::open(&file_path) {
Ok(repo) => {
let files = list_files_git(pkg, &repo, filter, gctx)?;
ret.extend(files.into_iter());
}
Err(..) => {
walk(&file_path, &mut ret, false, filter, gctx)?;
}
}
} else if filter(&file_path, is_dir) {
assert!(!is_dir);
// We found a file!
trace!(" found {}", file_path.display());
ret.push(file_path);
}
}
return Ok(ret);

#[cfg(unix)]
fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
use std::ffi::OsStr;
use std::os::unix::prelude::*;
Ok(path.join(<OsStr as OsStrExt>::from_bytes(data)))
}
#[cfg(windows)]
fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
use std::str;
match str::from_utf8(data) {
Ok(s) => Ok(path.join(s)),
Err(e) => Err(anyhow::format_err!(
"cannot process path in git with a non utf8 filename: {}\n{:?}",
e,
data
)),
}
}
}

/// Lists files relevant to building this package inside this source by
/// traversing the git working tree, while avoiding ignored files.
///
Expand Down Expand Up @@ -886,7 +669,7 @@ fn list_files_gix(
/// Lists files relevant to building this package inside this source by
/// walking the filesystem from the package root path.
///
/// This is a fallback for [`list_files_git`] when the package
/// This is a fallback for [`list_files_gix`] when the package
/// is not tracked under a Git repository.
fn list_files_walk(
pkg: &Package,
Expand Down

0 comments on commit dba851d

Please sign in to comment.