Replies: 2 comments 1 reply
-
instead of using To minimise dependencies, try |
Beta Was this translation helpful? Give feedback.
1 reply
-
FYI here's what I came up with. Maybe you can identify more things I can drop? gix = { version = "0,>=0.64", default-features = false, features = ["attributes", "index"] } use std::borrow::Cow;
use anyhow::{anyhow, Result};
use camino::Utf8PathBuf;
use gix::{
attrs::search::Outcome,
bstr::{BStr, BString},
discover,
index::{entry::Mode, Entry, File},
path::to_unix_separators_on_windows,
pathspec::Search,
worktree::{
stack::{
state::{attributes::Source as AttrSource, ignore::Source as IgnSource},
Statistics,
},
IndexPersistedOrInMemory,
},
AttributeStack, Repository,
};
/// Returns `git ls-files <pwd>` paths relative to pwd
pub(crate) fn ls_files(pwd: &Utf8PathBuf) -> Result<Vec<Utf8PathBuf>> {
let repo = discover(pwd).map_err(|e| anyhow!("Failed git-ing dir {pwd}: {e}"))?;
let mut out = vec![];
let _ = list_entries(&repo, "".into(), &mut out)
.map_err(|e| anyhow!("Failed `git ls-files` {pwd}: {e}"))?;
Ok(out)
}
fn list_entries(
repo: &Repository,
prefix: &BStr,
out: &mut Vec<Utf8PathBuf>,
) -> Result<StatisticsBis> {
let (mut pathspec, index, mut cache) = init_cache(repo)?;
let submodules_by_path = repo
.submodules()
.map(|opt| {
opt.map(|submodules| {
submodules
.map(|sm| sm.path().map(Cow::into_owned).map(move |path| (path, sm)))
.collect::<Result<Vec<_>, _>>()
})
})
.transpose()
.transpose()?
.transpose()?;
let mut stats = StatisticsBis { entries: index.entries().len(), ..Default::default() };
if let Some(entries) = index.prefixed_entries(pathspec.common_prefix()) {
stats.entries_after_prune = entries.len();
for entry in entries.iter().peekable() {
// Note that we intentionally ignore `_case` so that we act like git does, attribute matching case is determined
// by the repository, not the pathspec.
let entry_is_excluded = pathspec
.pattern_matching_relative_path(
entry.path(&index),
Some(false),
&mut |rela_path, _case, is_dir, out| {
cache
.as_mut()
.map(|(_attrs, cache)| {
cache
.at_entry(
rela_path,
Some(if is_dir { Mode::DIR } else { Mode::FILE }),
)
.ok()
.map(|platform| platform.matching_attributes(out))
.unwrap_or_default()
})
.unwrap_or_default()
},
)
.map_or(true, |m| m.is_excluded());
let entry_is_submodule = entry.mode.is_submodule();
if entry_is_excluded && !entry_is_submodule {
continue;
}
if let Some(sm) =
submodules_by_path.as_ref().filter(|_| entry_is_submodule).and_then(|sms_by_path| {
let entry_path = entry.path(&index);
sms_by_path
.iter()
.find_map(|(path, sm)| (path == entry_path).then_some(sm))
.filter(|sm| {
sm.git_dir_try_old_form().map_or(false, |dot_git| dot_git.exists())
})
})
{
let sm_path = to_unix_separators_on_windows(sm.path()?);
let sm_repo = sm.open()?.expect("we checked it exists");
let mut prefix = prefix.to_owned();
prefix.extend_from_slice(sm_path.as_ref());
if !sm_path.ends_with(b"/") {
prefix.push(b'/');
}
let sm_stats = list_entries(&sm_repo, prefix.as_ref(), out)?;
stats.submodule.push((sm_path.into_owned(), sm_stats));
} else {
to_human_simple(out, &index, entry, prefix);
}
}
}
stats.cache = cache.map(|c| *c.1.statistics());
Ok(stats)
}
#[allow(clippy::type_complexity)]
fn init_cache(
repo: &Repository,
) -> Result<(Search, IndexPersistedOrInMemory, Option<(Outcome, AttributeStack<'_>)>)> {
let index = repo.index_or_load_from_head()?;
let pathspec = repo.pathspec(
true,
Vec::<BString>::new(),
false,
&index,
AttrSource::WorktreeThenIdMapping.adjust_for_bare(repo.is_bare()),
)?;
let cache = pathspec
.search()
.patterns()
.any(|spec| !spec.attributes.is_empty())
.then(|| {
repo.attributes(&index, AttrSource::IdMapping, IgnSource::IdMapping, None)
.map(|cache| (cache.attribute_matches(), cache))
})
.transpose()?;
Ok((pathspec.into_parts().0, index, cache))
}
#[derive(Default, Debug)]
struct StatisticsBis {
#[allow(dead_code)]
// Not really dead, but Debug doesn't count for it even though it's crucial.
entries: usize,
entries_after_prune: usize,
cache: Option<Statistics>,
submodule: Vec<(BString, StatisticsBis)>,
}
fn to_human_simple(out: &mut Vec<Utf8PathBuf>, file: &File, entry: &Entry, prefix: &BStr) {
out.push(Utf8PathBuf::from(if prefix.is_empty() {
entry.path(file).to_string()
} else {
prefix.to_string() + entry.path(file).to_string().as_str()
}));
} |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi! I'm trying to
git ls-files <local git repo>
so as to obtain a collection of paths.I came around to https://www.reddit.com/r/rust/comments/15r0a98/comment/jw90t4h/ so I have
How can I improve this?
io::Write
ing pathsThanks!
Beta Was this translation helpful? Give feedback.
All reactions