use gix_error::{ErrorExt, ResultExt};use gix_hash::ObjectId;use gix_object::tree::EntryKind; use crate::{ Id, Repository, bstr::{BStr, BString}, prelude::ObjectIdExt,}; /// A cursor at a specific portion of a tree to [edit](super::Editor).pub struct Cursor<'a, 'repo> { inner: gix_object::tree::editor::Cursor<'a, 'repo>, validate: gix_validate::path::component::Options, repo: &'repo Repository,} /// Lifecycleimpl<'repo> super::Editor<'repo> { /// Initialize a new editor from the given `tree`. pub fn new(tree: &crate::Tree<'repo>) -> Result<Self, crate::Error> { let tree_ref = tree.decode().or_erased()?; let repo = tree.repo; let validate = repo.config.protect_options().or_erased()?; Ok(super::Editor { inner: gix_object::tree::Editor::new(tree_ref.into(), &repo.objects, repo.object_hash()), validate, repo, }) } /// Detach all extras and return the underlying plumbing editor, which won't perform validation /// when writing the tree. pub fn detach(self) -> gix_object::tree::Editor<'repo> { self.inner }} /// Tree editingimpl<'repo> crate::Tree<'repo> { /// Start editing a new tree based on this one. #[doc(alias = "treebuilder", alias = "git2")] pub fn edit(&self) -> Result<super::Editor<'repo>, crate::Error> { super::Editor::new(self) }} /// Obtain an iterator over `BStr`-components.////// Note that the implementation is simple, and it's mainly meant for statically known strings/// or locations obtained during a merge.pub trait ToComponents { /// Return an iterator over the components of a path, without the separator. fn to_components(&self) -> impl Iterator<Item = &BStr>;} impl ToComponents for &str { fn to_components(&self) -> impl Iterator<Item = &BStr> { self.split('/').map(Into::into) }} impl ToComponents for String { fn to_components(&self) -> impl Iterator<Item = &BStr> { self.split('/').map(Into::into) }} impl ToComponents for &String { fn to_components(&self) -> impl Iterator<Item = &BStr> { self.split('/').map(Into::into) }} impl ToComponents for BString { fn to_components(&self) -> impl Iterator<Item = &BStr> { self.split(|b| *b == b'/').map(Into::into) }} impl ToComponents for &BString { fn to_components(&self) -> impl Iterator<Item = &BStr> { self.split(|b| *b == b'/').map(Into::into) }} impl ToComponents for &BStr { fn to_components(&self) -> impl Iterator<Item = &BStr> { self.split(|b| *b == b'/').map(Into::into) }} /// Cursor Handlingimpl<'repo> super::Editor<'repo> { /// Turn ourselves as a cursor, which points to the same tree as the editor. /// /// This is useful if a method takes a [`Cursor`], not an [`Editor`](super::Editor). pub fn to_cursor(&mut self) -> Cursor<'_, 'repo> { Cursor { inner: self.inner.to_cursor(), validate: self.validate, repo: self.repo, } } /// Create a cursor at the given `rela_path`, which must be a tree or is turned into a tree as its own edit. /// /// The returned cursor will then allow applying edits to the tree at `rela_path` as root. /// If `rela_path` is a single empty string, it is equivalent to using the current instance itself. pub fn cursor_at(&mut self, rela_path: impl ToComponents) -> Result<Cursor<'_, 'repo>, gix_error::Error> { Ok(Cursor { inner: self.inner.cursor_at(rela_path.to_components())?, validate: self.validate, repo: self.repo, }) }}/// Operationsimpl<'repo> Cursor<'_, 'repo> { /// Like [`Editor::upsert()`](super::Editor::upsert()), but with the constraint of only editing in this cursor's tree. pub fn upsert( &mut self, rela_path: impl ToComponents, kind: EntryKind, id: impl Into<ObjectId>, ) -> Result<&mut Self, gix_error::Error> { self.inner.upsert(rela_path.to_components(), kind, id.into())?; Ok(self) } /// Like [`Editor::remove()`](super::Editor::remove), but with the constraint of only editing in this cursor's tree. pub fn remove(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> { self.inner.remove(rela_path.to_components())?; Ok(self) } /// Like [`Editor::remove_leaf()`](super::Editor::remove_leaf), but with the constraint of only editing in this cursor's tree. pub fn remove_leaf(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> { self.inner.remove_leaf(rela_path.to_components())?; Ok(self) } /// Like [`Editor::write()`](super::Editor::write()), but will write only the subtree of the cursor. pub fn write(&mut self) -> Result<Id<'repo>, crate::Error> { write_cursor(self) } /// Obtain the entry at `rela_path` or return `None` if none was found, or the tree wasn't yet written /// to that point. /// The root tree is always available. /// Note that after [writing](Self::write) only the root path remains, all other intermediate trees are removed. /// The entry can be anything that can be stored in a tree, but may have a null-id if it's a newly /// inserted tree. Also, ids of trees might not be accurate as they may have been changed in memory. pub fn get(&self, rela_path: impl ToComponents) -> Option<crate::object::tree::EntryRef<'repo, '_>> { self.inner .get(rela_path.to_components()) .map(|entry| crate::object::tree::EntryRef { inner: entry.into(), repo: self.repo, }) }} /// Operationsimpl<'repo> super::Editor<'repo> { /// Set the root tree of the modification to `root`, assuring it has a well-known state. /// /// Note that this erases all previous edits. /// /// This is useful if the same editor is re-used for various trees. pub fn set_root(&mut self, root: &crate::Tree<'repo>) -> Result<&mut Self, crate::Error> { let new_editor = super::Editor::new(root)?; self.inner = new_editor.inner; self.repo = new_editor.repo; Ok(self) } /// Insert a new entry of `kind` with `id` at `rela_path`, an iterator over each path component in the tree, /// like `a/b/c`. Names are matched case-sensitively. /// /// Existing leaf-entries will be overwritten unconditionally, and it is assumed that `id` is available in the object database /// or will be made available at a later point to assure the integrity of the produced tree. /// /// Intermediate trees will be created if they don't exist in the object database, otherwise they will be loaded and entries /// will be inserted into them instead. /// /// Note that `id` can be [null](ObjectId::null()) to create a placeholder. These will not be written, and paths leading /// through them will not be considered a problem. /// /// `id` can also be an empty tree, along with [the respective `kind`](EntryKind::Tree), even though that's normally not allowed /// in Git trees. /// /// Validation of path-components will not be performed here, but when [writing the tree](Self::write()). pub fn upsert( &mut self, rela_path: impl ToComponents, kind: EntryKind, id: impl Into<ObjectId>, ) -> Result<&mut Self, gix_error::Error> { self.inner.upsert(rela_path.to_components(), kind, id.into())?; Ok(self) } /// Remove the entry at `rela_path`, loading all trees on the path accordingly. /// It's no error if the entry doesn't exist, or if `rela_path` doesn't lead to an existing entry at all. pub fn remove(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> { self.inner.remove(rela_path.to_components())?; Ok(self) } /// Remove a non-tree entry at `rela_path`, loading all trees on the path accordingly. /// It's no error if the entry doesn't exist, or if `rela_path` doesn't lead to an existing entry at all. /// /// Return an error if the entry exists and is a tree, as that would otherwise also remove all entries below it. pub fn remove_leaf(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> { self.inner.remove_leaf(rela_path.to_components())?; Ok(self) } /// Write the entire in-memory state of all changed trees (and only changed trees) to the object database. /// Note that the returned object id *can* be the empty tree if everything was removed or if nothing /// was added to the tree. /// /// The last call to `out` will be the changed root tree, whose object-id will also be returned. /// `out` is free to do any kind of additional validation, like to assure that all entries in the tree exist. /// We don't assure that as there is no validation that inserted entries are valid object ids. /// /// Future calls to [`upsert`](Self::upsert) or similar will keep working on the last seen state of the /// just-written root-tree. /// If this is not desired, use [set_root()](Self::set_root()). /// /// Before writing a tree, all of its entries (not only added ones), will be validated to assure they are /// correct. The objects pointed to by entries also have to exist already. pub fn write(&mut self) -> Result<Id<'repo>, crate::Error> { write_cursor(&mut self.to_cursor()) } /// Obtain the entry at `rela_path` or return `None` if none was found, or the tree wasn't yet written /// to that point. /// The root tree is always available. /// Note that after [writing](Self::write) only the root path remains, all other intermediate trees are removed. /// The entry can be anything that can be stored in a tree, but may have a null-id if it's a newly /// inserted tree. Also, ids of trees might not be accurate as they may have been changed in memory. pub fn get(&self, rela_path: impl ToComponents) -> Option<crate::object::tree::EntryRef<'repo, '_>> { self.inner .get(rela_path.to_components()) .map(|entry| crate::object::tree::EntryRef { inner: entry.into(), repo: self.repo, }) }} fn write_cursor<'repo>(cursor: &mut Cursor<'_, 'repo>) -> Result<Id<'repo>, crate::Error> { cursor .inner .write(|tree| -> Result<ObjectId, crate::Error> { for entry in &tree.entries { let kind: EntryKind = entry.mode.into(); gix_validate::path::component( entry.filename.as_ref(), entry .mode .is_link() .then_some(gix_validate::path::component::Mode::Symlink), cursor.validate, ) .map_err(|err| { gix_error::Error::from(err.and_raise(gix_error::message!( "The object {} ({}) has an invalid filename: '{}'", entry.oid, kind.as_octal_str(), entry.filename ))) })?; if !entry.mode.is_commit() && !cursor.repo.has_object(entry.oid) { return Err(gix_error::Error::from_error(gix_error::message!( "The object {} ({}) at '{}' could not be found", entry.oid, kind.as_octal_str(), entry.filename ))); } } Ok(cursor.repo.write_object(tree).or_erased()?.detach()) }) .map(|id| id.attach(cursor.repo))}
use gix_error::{ErrorExt, ResultExt};
use gix_hash::ObjectId;
use gix_object::tree::EntryKind;
use crate::{
Id, Repository,
bstr::{BStr, BString},
prelude::ObjectIdExt,
};
/// A cursor at a specific portion of a tree to [edit](super::Editor).
pub struct Cursor<'a, 'repo> {
inner: gix_object::tree::editor::Cursor<'a, 'repo>,
validate: gix_validate::path::component::Options,
repo: &'repo Repository,
}
/// Lifecycle
impl<'repo> super::Editor<'repo> {
/// Initialize a new editor from the given `tree`.
pub fn new(tree: &crate::Tree<'repo>) -> Result<Self, crate::Error> {
let tree_ref = tree.decode().or_erased()?;
let repo = tree.repo;
let validate = repo.config.protect_options().or_erased()?;
Ok(super::Editor {
inner: gix_object::tree::Editor::new(tree_ref.into(), &repo.objects, repo.object_hash()),
validate,
repo,
})
}
/// Detach all extras and return the underlying plumbing editor, which won't perform validation
/// when writing the tree.
pub fn detach(self) -> gix_object::tree::Editor<'repo> {
self.inner
}
}
/// Tree editing
impl<'repo> crate::Tree<'repo> {
/// Start editing a new tree based on this one.
#[doc(alias = "treebuilder", alias = "git2")]
pub fn edit(&self) -> Result<super::Editor<'repo>, crate::Error> {
super::Editor::new(self)
}
}
/// Obtain an iterator over `BStr`-components.
///
/// Note that the implementation is simple, and it's mainly meant for statically known strings
/// or locations obtained during a merge.
pub trait ToComponents {
/// Return an iterator over the components of a path, without the separator.
fn to_components(&self) -> impl Iterator<Item = &BStr>;
}
impl ToComponents for &str {
fn to_components(&self) -> impl Iterator<Item = &BStr> {
self.split('/').map(Into::into)
}
}
impl ToComponents for String {
fn to_components(&self) -> impl Iterator<Item = &BStr> {
self.split('/').map(Into::into)
}
}
impl ToComponents for &String {
fn to_components(&self) -> impl Iterator<Item = &BStr> {
self.split('/').map(Into::into)
}
}
impl ToComponents for BString {
fn to_components(&self) -> impl Iterator<Item = &BStr> {
self.split(|b| *b == b'/').map(Into::into)
}
}
impl ToComponents for &BString {
fn to_components(&self) -> impl Iterator<Item = &BStr> {
self.split(|b| *b == b'/').map(Into::into)
}
}
impl ToComponents for &BStr {
fn to_components(&self) -> impl Iterator<Item = &BStr> {
self.split(|b| *b == b'/').map(Into::into)
}
}
/// Cursor Handling
impl<'repo> super::Editor<'repo> {
/// Turn ourselves as a cursor, which points to the same tree as the editor.
///
/// This is useful if a method takes a [`Cursor`], not an [`Editor`](super::Editor).
pub fn to_cursor(&mut self) -> Cursor<'_, 'repo> {
Cursor {
inner: self.inner.to_cursor(),
validate: self.validate,
repo: self.repo,
}
}
/// Create a cursor at the given `rela_path`, which must be a tree or is turned into a tree as its own edit.
///
/// The returned cursor will then allow applying edits to the tree at `rela_path` as root.
/// If `rela_path` is a single empty string, it is equivalent to using the current instance itself.
pub fn cursor_at(&mut self, rela_path: impl ToComponents) -> Result<Cursor<'_, 'repo>, gix_error::Error> {
Ok(Cursor {
inner: self.inner.cursor_at(rela_path.to_components())?,
validate: self.validate,
repo: self.repo,
})
}
}
/// Operations
impl<'repo> Cursor<'_, 'repo> {
/// Like [`Editor::upsert()`](super::Editor::upsert()), but with the constraint of only editing in this cursor's tree.
pub fn upsert(
&mut self,
rela_path: impl ToComponents,
kind: EntryKind,
id: impl Into<ObjectId>,
) -> Result<&mut Self, gix_error::Error> {
self.inner.upsert(rela_path.to_components(), kind, id.into())?;
Ok(self)
}
/// Like [`Editor::remove()`](super::Editor::remove), but with the constraint of only editing in this cursor's tree.
pub fn remove(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> {
self.inner.remove(rela_path.to_components())?;
Ok(self)
}
/// Like [`Editor::remove_leaf()`](super::Editor::remove_leaf), but with the constraint of only editing in this cursor's tree.
pub fn remove_leaf(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> {
self.inner.remove_leaf(rela_path.to_components())?;
Ok(self)
}
/// Like [`Editor::write()`](super::Editor::write()), but will write only the subtree of the cursor.
pub fn write(&mut self) -> Result<Id<'repo>, crate::Error> {
write_cursor(self)
}
/// Obtain the entry at `rela_path` or return `None` if none was found, or the tree wasn't yet written
/// to that point.
/// The root tree is always available.
/// Note that after [writing](Self::write) only the root path remains, all other intermediate trees are removed.
/// The entry can be anything that can be stored in a tree, but may have a null-id if it's a newly
/// inserted tree. Also, ids of trees might not be accurate as they may have been changed in memory.
pub fn get(&self, rela_path: impl ToComponents) -> Option<crate::object::tree::EntryRef<'repo, '_>> {
self.inner
.get(rela_path.to_components())
.map(|entry| crate::object::tree::EntryRef {
inner: entry.into(),
repo: self.repo,
})
}
}
/// Operations
impl<'repo> super::Editor<'repo> {
/// Set the root tree of the modification to `root`, assuring it has a well-known state.
///
/// Note that this erases all previous edits.
///
/// This is useful if the same editor is re-used for various trees.
pub fn set_root(&mut self, root: &crate::Tree<'repo>) -> Result<&mut Self, crate::Error> {
let new_editor = super::Editor::new(root)?;
self.inner = new_editor.inner;
self.repo = new_editor.repo;
Ok(self)
}
/// Insert a new entry of `kind` with `id` at `rela_path`, an iterator over each path component in the tree,
/// like `a/b/c`. Names are matched case-sensitively.
///
/// Existing leaf-entries will be overwritten unconditionally, and it is assumed that `id` is available in the object database
/// or will be made available at a later point to assure the integrity of the produced tree.
///
/// Intermediate trees will be created if they don't exist in the object database, otherwise they will be loaded and entries
/// will be inserted into them instead.
///
/// Note that `id` can be [null](ObjectId::null()) to create a placeholder. These will not be written, and paths leading
/// through them will not be considered a problem.
///
/// `id` can also be an empty tree, along with [the respective `kind`](EntryKind::Tree), even though that's normally not allowed
/// in Git trees.
///
/// Validation of path-components will not be performed here, but when [writing the tree](Self::write()).
pub fn upsert(
&mut self,
rela_path: impl ToComponents,
kind: EntryKind,
id: impl Into<ObjectId>,
) -> Result<&mut Self, gix_error::Error> {
self.inner.upsert(rela_path.to_components(), kind, id.into())?;
Ok(self)
}
/// Remove the entry at `rela_path`, loading all trees on the path accordingly.
/// It's no error if the entry doesn't exist, or if `rela_path` doesn't lead to an existing entry at all.
pub fn remove(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> {
self.inner.remove(rela_path.to_components())?;
Ok(self)
}
/// Remove a non-tree entry at `rela_path`, loading all trees on the path accordingly.
/// It's no error if the entry doesn't exist, or if `rela_path` doesn't lead to an existing entry at all.
///
/// Return an error if the entry exists and is a tree, as that would otherwise also remove all entries below it.
pub fn remove_leaf(&mut self, rela_path: impl ToComponents) -> Result<&mut Self, gix_error::Error> {
self.inner.remove_leaf(rela_path.to_components())?;
Ok(self)
}
/// Write the entire in-memory state of all changed trees (and only changed trees) to the object database.
/// Note that the returned object id *can* be the empty tree if everything was removed or if nothing
/// was added to the tree.
///
/// The last call to `out` will be the changed root tree, whose object-id will also be returned.
/// `out` is free to do any kind of additional validation, like to assure that all entries in the tree exist.
/// We don't assure that as there is no validation that inserted entries are valid object ids.
///
/// Future calls to [`upsert`](Self::upsert) or similar will keep working on the last seen state of the
/// just-written root-tree.
/// If this is not desired, use [set_root()](Self::set_root()).
///
/// Before writing a tree, all of its entries (not only added ones), will be validated to assure they are
/// correct. The objects pointed to by entries also have to exist already.
pub fn write(&mut self) -> Result<Id<'repo>, crate::Error> {
write_cursor(&mut self.to_cursor())
}
/// Obtain the entry at `rela_path` or return `None` if none was found, or the tree wasn't yet written
/// to that point.
/// The root tree is always available.
/// Note that after [writing](Self::write) only the root path remains, all other intermediate trees are removed.
/// The entry can be anything that can be stored in a tree, but may have a null-id if it's a newly
/// inserted tree. Also, ids of trees might not be accurate as they may have been changed in memory.
pub fn get(&self, rela_path: impl ToComponents) -> Option<crate::object::tree::EntryRef<'repo, '_>> {
self.inner
.get(rela_path.to_components())
.map(|entry| crate::object::tree::EntryRef {
inner: entry.into(),
repo: self.repo,
})
}
}
fn write_cursor<'repo>(cursor: &mut Cursor<'_, 'repo>) -> Result<Id<'repo>, crate::Error> {
cursor
.inner
.write(|tree| -> Result<ObjectId, crate::Error> {
for entry in &tree.entries {
let kind: EntryKind = entry.mode.into();
gix_validate::path::component(
entry.filename.as_ref(),
entry
.mode
.is_link()
.then_some(gix_validate::path::component::Mode::Symlink),
cursor.validate,
)
.map_err(|err| {
gix_error::Error::from(err.and_raise(gix_error::message!(
"The object {} ({}) has an invalid filename: '{}'",
entry.oid,
kind.as_octal_str(),
entry.filename
)))
})?;
if !entry.mode.is_commit() && !cursor.repo.has_object(entry.oid) {
return Err(gix_error::Error::from_error(gix_error::message!(
"The object {} ({}) at '{}' could not be found",
entry.oid,
kind.as_octal_str(),
entry.filename
)));
}
}
Ok(cursor.repo.write_object(tree).or_erased()?.detach())
})
.map(|id| id.attach(cursor.repo))
}