Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gix-object/src/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ mod ref_iter;
///
pub mod write;

pub use ref_iter::iter_next;

/// The state needed to apply edits instantly to in-memory trees.
///
/// It's made so that each tree is looked at in the object database at most once, and held in memory for
Expand Down
65 changes: 45 additions & 20 deletions gix-object/src/tree/ref_iter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
use std::ops::ControlFlow;

use bstr::BStr;
use winnow::{error::ParserError, prelude::*};

use crate::{tree, tree::EntryRef, TreeRef, TreeRefIter};

/// Yes
pub fn iter_next<'a, I, P>(
components: &mut core::iter::Peekable<I>,
tree: crate::Data<'a>,
) -> core::ops::ControlFlow<Option<EntryRef<'a>>, gix_hash::ObjectId>
where
I: Iterator<Item = P>,
P: PartialEq<BStr>,
{
if !tree.kind.is_tree() {
return ControlFlow::Break(None);
}

let Some(component) = components.next() else {
return ControlFlow::Break(None);
};

let Some(entry) = TreeRefIter::from_bytes(tree.data)
.filter_map(Result::ok)
.find(|entry| component.eq(entry.filename))
else {
return ControlFlow::Break(None);
};

if components.peek().is_none() {
ControlFlow::Break(Some(entry))
} else {
ControlFlow::Continue(entry.oid.to_owned())
}
}

impl<'a> TreeRefIter<'a> {
/// Instantiate an iterator from the given tree data.
pub fn from_bytes(data: &'a [u8]) -> TreeRefIter<'a> {
Expand All @@ -28,30 +61,22 @@ impl<'a> TreeRefIter<'a> {
P: PartialEq<BStr>,
{
buffer.clear();

let mut path = path.into_iter().peekable();
buffer.extend_from_slice(self.data);
while let Some(component) = path.next() {
match TreeRefIter::from_bytes(buffer)
.filter_map(Result::ok)
.find(|entry| component.eq(entry.filename))
{
Some(entry) => {
if path.peek().is_none() {
return Ok(Some(entry.into()));
} else {
let next_id = entry.oid.to_owned();
let obj = odb.try_find(&next_id, buffer)?;
let Some(obj) = obj else { return Ok(None) };
if !obj.kind.is_tree() {
return Ok(None);
}
}

let mut iter = path.into_iter().peekable();
let mut data = crate::Data::new(crate::Kind::Tree, buffer);

loop {
data = match iter_next(&mut iter, data) {
ControlFlow::Continue(oid) => {
let Some(next_tree) = odb.try_find(&oid, buffer)? else {
break Ok(None);
};
next_tree
}
None => return Ok(None),
ControlFlow::Break(v) => break Ok(v.map(Into::into)),
}
}
Ok(None)
}

/// Like [`Self::lookup_entry()`], but takes any [`AsRef<Path>`](`std::path::Path`) directly via `relative_path`,
Expand Down
78 changes: 31 additions & 47 deletions gix/src/object/tree/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::ops::ControlFlow;

use gix_hash::ObjectId;
pub use gix_object::tree::{EntryKind, EntryMode};
use gix_object::{bstr::BStr, FindExt, TreeRefIter};
use gix_object::{bstr::BStr, tree::iter_next, FindExt, TreeRefIter};

use crate::{object::find, Id, ObjectDetached, Repository, Tree};

Expand Down Expand Up @@ -63,34 +65,25 @@ impl<'repo> Tree<'repo> {
I: IntoIterator<Item = P>,
P: PartialEq<BStr>,
{
let mut buf = self.repo.empty_reusable_buffer();
buf.clear();

let mut path = path.into_iter().peekable();
let buf = &mut self.repo.empty_reusable_buffer();
buf.extend_from_slice(&self.data);
while let Some(component) = path.next() {
match TreeRefIter::from_bytes(&buf)
.filter_map(Result::ok)
.find(|entry| component.eq(entry.filename))
{
Some(entry) => {
if path.peek().is_none() {
return Ok(Some(Entry {
inner: entry.into(),
repo: self.repo,
}));
} else {
let next_id = entry.oid.to_owned();
let obj = self.repo.objects.find(&next_id, &mut buf)?;
if !obj.kind.is_tree() {
return Ok(None);
}
}

let mut iter = path.into_iter().peekable();
let mut data = gix_object::Data::new(gix_object::Kind::Tree, buf);

loop {
data = match iter_next(&mut iter, data) {
ControlFlow::Continue(oid) => self.repo.find(&oid, buf)?,
ControlFlow::Break(entry) => {
let mapped = entry.map(|e| Entry {
inner: e.into(),
repo: self.repo,
});

break Ok(mapped);
}
None => return Ok(None),
}
}
Ok(None)
}

/// Follow a sequence of `path` components starting from this instance, and look them up one by one until the last component
Expand All @@ -109,31 +102,22 @@ impl<'repo> Tree<'repo> {
I: IntoIterator<Item = P>,
P: PartialEq<BStr>,
{
let mut path = path.into_iter().peekable();
while let Some(component) = path.next() {
match TreeRefIter::from_bytes(&self.data)
.filter_map(Result::ok)
.find(|entry| component.eq(entry.filename))
{
Some(entry) => {
if path.peek().is_none() {
return Ok(Some(Entry {
inner: entry.into(),
repo: self.repo,
}));
} else {
let next_id = entry.oid.to_owned();
let obj = self.repo.objects.find(&next_id, &mut self.data)?;
self.id = next_id;
if !obj.kind.is_tree() {
return Ok(None);
}
}
let mut iter = path.into_iter().peekable();
let mut data = gix_object::Data::new(gix_object::Kind::Tree, &self.data);

loop {
data = match iter_next(&mut iter, data) {
ControlFlow::Continue(oid) => {
self.id = oid;
self.repo.find(&oid, &mut self.data)?
}
ControlFlow::Break(entry) => {
let repo = self.repo;
let mapped = entry.map(|e| Entry { inner: e.into(), repo });
break Ok(mapped);
}
None => return Ok(None),
}
}
Ok(None)
}

/// Like [`Self::lookup_entry()`], but takes a `Path` directly via `relative_path`, a path relative to this tree.
Expand Down
Loading