Skip to content

Commit 743b433

Browse files
committed
core: implement log_file
Implement the log_file method in gitoxide-core, which allows performing path-delimited log commands. With the new changed paths bloom filter, it is not possible to perform this operation very efficiently.
1 parent 23e12be commit 743b433

File tree

1 file changed

+53
-4
lines changed
  • gitoxide-core/src/repository

1 file changed

+53
-4
lines changed

gitoxide-core/src/repository/log.rs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use anyhow::bail;
2-
use gix::bstr::{BString, ByteSlice};
1+
use gix::bstr::{BStr, BString, ByteSlice};
32

43
pub fn log(mut repo: gix::Repository, out: &mut dyn std::io::Write, path: Option<BString>) -> anyhow::Result<()> {
54
repo.object_cache_size_if_unset(repo.compute_object_cache_size_for_tree_diffs(&**repo.index_or_empty()?));
@@ -25,8 +24,20 @@ fn log_all(repo: gix::Repository, out: &mut dyn std::io::Write) -> Result<(), an
2524
Ok(())
2625
}
2726

28-
fn log_file(_repo: gix::Repository, _out: &mut dyn std::io::Write, _path: BString) -> anyhow::Result<()> {
29-
bail!("File-based lookup isn't yet implemented in a way that is competitively fast");
27+
fn log_file(repo: gix::Repository, out: &mut dyn std::io::Write, path: BString) -> anyhow::Result<()> {
28+
let path = gix::path::to_unix_separators_on_windows(path.as_bstr()).into_owned();
29+
let head = repo.head()?.peel_to_commit()?;
30+
let cache = repo.commit_graph_if_enabled()?;
31+
let topo = gix::traverse::commit::topo::Builder::from_iters(&repo.objects, [head.id], None::<Vec<gix::ObjectId>>)
32+
.build()?;
33+
34+
for info in topo {
35+
let info = info?;
36+
if commit_changes_path(&repo, cache.as_ref(), &info, path.as_ref())? {
37+
write_info(&repo, &mut *out, &info)?;
38+
}
39+
}
40+
Ok(())
3041
}
3142

3243
fn write_info(
@@ -48,3 +59,41 @@ fn write_info(
4859

4960
Ok(())
5061
}
62+
63+
fn commit_changes_path(
64+
repo: &gix::Repository,
65+
cache: Option<&gix::commitgraph::Graph>,
66+
info: &gix::traverse::commit::Info,
67+
path: &BStr,
68+
) -> anyhow::Result<bool> {
69+
let commit = repo.find_commit(info.id)?;
70+
let commit_tree = commit.tree()?;
71+
let commit_entry = lookup_path_entry(&commit_tree, path)?;
72+
73+
if info.parent_ids.is_empty() {
74+
return Ok(commit_entry.is_some());
75+
}
76+
77+
for (index, parent_id) in info.parent_ids.iter().enumerate() {
78+
if index == 0 && cache.and_then(|graph| graph.maybe_contains_path_by_id(info.id, path)) == Some(false) {
79+
continue;
80+
}
81+
82+
let parent = repo.find_commit(*parent_id)?;
83+
let parent_tree = parent.tree()?;
84+
let parent_entry = lookup_path_entry(&parent_tree, path)?;
85+
if commit_entry != parent_entry {
86+
return Ok(true);
87+
}
88+
}
89+
90+
Ok(false)
91+
}
92+
93+
fn lookup_path_entry(
94+
tree: &gix::Tree<'_>,
95+
path: &BStr,
96+
) -> anyhow::Result<Option<(gix::objs::tree::EntryMode, gix::ObjectId)>> {
97+
let entry = tree.lookup_entry(path.split(|b| *b == b'/'))?;
98+
Ok(entry.map(|entry| (entry.mode(), entry.object_id())))
99+
}

0 commit comments

Comments
 (0)