1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
use git_hash::ObjectId;
use git_object::{bstr::BStr, TreeRefIter};
use crate::{object::find, Id, Tree};
/// Initialization
impl<'repo> Tree<'repo> {
/// Obtain a tree instance by handing in all components that it is made up of.
pub fn from_data(id: impl Into<ObjectId>, data: Vec<u8>, repo: &'repo crate::Repository) -> Self {
Tree {
id: id.into(),
data,
repo,
}
}
}
/// Access
impl<'repo> Tree<'repo> {
/// Return this tree's identifier.
pub fn id(&self) -> Id<'repo> {
Id::from_id(self.id, self.repo)
}
// TODO: tests.
/// Follow a sequence of `path` components starting from this instance, and look them up one by one until the last component
/// is looked up and its tree entry is returned.
///
/// # Performance Notes
///
/// Searching tree entries is currently done in sequence, which allows to the search to be allocation free. It would be possible
/// to re-use a vector and use a binary search instead, which might be able to improve performance over all.
/// However, a benchmark should be created first to have some data and see which trade-off to choose here.
///
/// # Why is this consuming?
///
/// The borrow checker shows pathological behaviour in loops that mutate a buffer, but also want to return from it.
/// Workarounds include keeping an index and doing a separate access to the memory, which seems hard to do here without
/// re-parsing the entries.
pub fn lookup_entry<I, P>(mut self, path: I) -> Result<Option<Entry<'repo>>, find::existing::Error>
where
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 repo = self.repo;
drop(self);
self = match repo.find_object(next_id)?.try_into_tree() {
Ok(tree) => tree,
Err(_) => return Ok(None),
};
}
}
None => return Ok(None),
}
}
Ok(None)
}
/// Like [`lookup_entry()`][Self::lookup_entry()], but takes a `Path` directly via `relative_path`, a path relative to this tree.
///
/// # Note
///
/// If any path component contains illformed UTF-8 and thus can't be converted to bytes on platforms which can't do so natively,
/// the returned component will be empty which makes the lookup fail.
pub fn lookup_entry_by_path(
self,
relative_path: impl AsRef<std::path::Path>,
) -> Result<Option<Entry<'repo>>, find::existing::Error> {
self.lookup_entry(relative_path.as_ref().components().map(|c| {
git_path::os_str_into_bstr(c.as_os_str())
.unwrap_or_else(|_| "".into())
.as_ref()
}))
}
}
///
pub mod diff;
///
pub mod traverse;
///
mod iter;
pub use iter::EntryRef;
impl<'r> std::fmt::Debug for Tree<'r> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Tree({})", self.id)
}
}
/// An entry in a [`Tree`], similar to an entry in a directory.
#[derive(PartialEq, Debug, Clone)]
pub struct Entry<'repo> {
inner: git_object::tree::Entry,
repo: &'repo crate::Repository,
}
mod entry {
use crate::{bstr::BStr, ext::ObjectIdExt, object::tree::Entry};
/// Access
impl<'repo> Entry<'repo> {
/// The kind of object to which `oid` is pointing to.
pub fn mode(&self) -> git_object::tree::EntryMode {
self.inner.mode
}
/// The name of the file in the parent tree.
pub fn filename(&self) -> &BStr {
self.inner.filename.as_ref()
}
/// Return the object id of the entry.
pub fn id(&self) -> crate::Id<'repo> {
self.inner.oid.attach(self.repo)
}
/// Return the object this entry points to.
pub fn object(&self) -> Result<crate::Object<'repo>, crate::object::find::existing::Error> {
self.id().object()
}
/// Return the plain object id of this entry, without access to the repository.
pub fn oid(&self) -> &git_hash::oid {
&self.inner.oid
}
/// Return the plain object id of this entry, without access to the repository.
pub fn object_id(&self) -> git_hash::ObjectId {
self.inner.oid
}
}
/// Consuming
impl Entry<'_> {
/// Return the contained object.
pub fn detach(self) -> git_object::tree::Entry {
self.inner
}
}
}