//! This crate provides the [`Repository`] abstraction which serves as a hub into all the functionality of git.//!//! It's powerful and won't sacrifice performance while still increasing convenience compared to using the sub-crates//! individually. Sometimes it may hide complexity under the assumption that the performance difference doesn't matter//! for all but the fewest tools out there, which would be using the underlying crates directly or file an issue.//!//! # The prelude and extensions//!//! With `use git_repository::prelude::*` you should be ready to go as it pulls in various extension traits to make functionality//! available on objects that may use it.//!//! The method signatures are still complex and may require various arguments for configuration and cache control.//!//! ## Easy-Mode//!//! Most extensions to existing objects provide an `obj_with_extension.easy(&repo).an_easier_version_of_a_method()` or `easy(&repo)`//! method to hide all complex arguments and sacrifice some performance for a lot of convenience.//!//! When starting out, use `easy(…)` and migrate to the more detailed method signatures to squeeze out more performance.//!//! # Cargo-features//!//! ## One-stop-shop//!//! To make using _sub-crates_ easier these are re-exported into the root of this crate.//!//! `git_repository::`//! * [`hash`]//! * [`url`]//! * [`actor`]//! * [`object`]//! * [`bstr`][object::bstr]//! * [`odb`]//! * [`pack`][odb::pack]//! * [`refs`]//! * [`interrupt`]//! * [`tempfile`]//! * [`traverse`]//! * [`diff`]//! * [`Progress`]//! * [`progress`]//! * [`interrupt`]//! * [`protocol`]//! * [`transport`][protocol::transport]//!#![deny(unsafe_code, rust_2018_idioms)]#![allow(missing_docs, unused)] use crate::hash::ObjectId;use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc}; // Re-exports to make this a potential one-stop shop crate avoiding people from having to reference various crates themselves.// This also means that their major version changes affect our major version, but that's alright as we directly expose their// APIs/instances anyway.pub use git_actor as actor;#[cfg(feature = "git-diff")]pub use git_diff as diff;pub use git_features::{parallel, progress, progress::Progress};pub use git_hash as hash;pub use git_object as object;pub use git_odb as odb;#[cfg(feature = "git-protocol")]pub use git_protocol as protocol;pub use git_ref as refs;pub use git_tempfile as tempfile;#[cfg(feature = "git-traverse")]pub use git_traverse as traverse;#[cfg(feature = "git-url")]pub use git_url as url; pub mod interrupt; #[cfg(feature = "git-traverse")]pub mod ext;pub mod prelude { pub use git_features::parallel::reduce::Finalize; pub use git_odb::{Find, FindExt, Write}; #[cfg(all(feature = "git-traverse"))] pub use crate::ext::*; pub use crate::reference::ReferencesExt;} pub mod init; pub mod path;pub use path::Path; pub mod repository; pub struct Repository { pub refs: git_ref::file::Store, pub odb: git_odb::linked::Store, pub working_tree: Option<PathBuf>,} mod handles { use std::{cell::RefCell, rc::Rc, sync::Arc}; use crate::{Cache, Repository}; pub struct Shared { pub repo: Rc<Repository>, pub cache: RefCell<Cache>, } /// A handle is what threaded programs would use to have thread-local but otherwise shared versions the same `Repository`. /// /// Mutable data present in the `Handle` itself while keeping the parent `Repository` (which has its own cache) shared. /// Otherwise handles reflect the API of a `Repository`. pub struct SharedArc { pub repo: Arc<Repository>, pub cache: RefCell<Cache>, } impl Clone for Shared { fn clone(&self) -> Self { Shared { repo: Rc::clone(&self.repo), cache: RefCell::new(Default::default()), } } } impl Clone for SharedArc { fn clone(&self) -> Self { SharedArc { repo: Arc::clone(&self.repo), cache: RefCell::new(Default::default()), } } } impl From<Repository> for Shared { fn from(repo: Repository) -> Self { Shared { repo: Rc::new(repo), cache: RefCell::new(Default::default()), } } } impl From<Repository> for SharedArc { fn from(repo: Repository) -> Self { SharedArc { repo: Arc::new(repo), cache: RefCell::new(Default::default()), } } } impl Repository { pub fn into_shared(self) -> Shared { self.into() } pub fn into_shared_arc(self) -> SharedArc { self.into() } }}pub use handles::{Shared, SharedArc}; #[derive(Default)]pub struct Cache { packed_refs: Option<refs::packed::Buffer>, pub pack: odb::pack::cache::Never, // TODO: choose great alround cache pub buf: Vec<u8>,} mod traits { use std::cell::RefMut; use crate::{Cache, Repository, Shared, SharedArc}; pub trait Access { fn repo(&self) -> &Repository; fn cache_mut(&self) -> RefMut<'_, Cache>; } impl Access for Shared { fn repo(&self) -> &Repository { self.repo.as_ref() } fn cache_mut(&self) -> RefMut<'_, Cache> { self.cache.borrow_mut() } } impl Access for SharedArc { fn repo(&self) -> &Repository { self.repo.as_ref() } fn cache_mut(&self) -> RefMut<'_, Cache> { self.cache.borrow_mut() } }}pub use traits::Access; mod cache { use crate::{ refs::{file, packed}, Cache, }; impl Cache { pub fn packed_refs( &mut self, file: &file::Store, ) -> Result<Option<&packed::Buffer>, packed::buffer::open::Error> { match self.packed_refs { Some(ref packed) => Ok(Some(packed)), None => { self.packed_refs = file.packed()?; Ok(self.packed_refs.as_ref()) } } } }} pub struct Object<'r, A> { id: ObjectId, // data: odb::pack::data::Object<'a>, access: &'r A,} pub struct Reference<'r, A> { pub(crate) backing: Option<reference::Backing>, pub(crate) access: &'r A,} pub mod reference { use std::cell::RefCell; use git_hash::ObjectId; use crate::{refs, refs::mutable, Access, Object, Reference, Repository}; pub(crate) enum Backing { OwnedPacked { /// The validated full name of the reference. name: mutable::FullName, /// The target object id of the reference, hex encoded. target: ObjectId, /// The fully peeled object id, hex encoded, that the ref is ultimately pointing to /// i.e. when all indirections are removed. object: Option<ObjectId>, }, LooseFile(refs::file::loose::Reference), } pub mod peel_to_id_in_place { use quick_error::quick_error; use crate::refs; quick_error! { #[derive(Debug)] pub enum Error { LoosePeelToId(err: refs::file::loose::reference::peel::to_id::Error) { display("Could not peel loose reference") from() source(err) } PackedRefsOpen(err: refs::packed::buffer::open::Error) { display("The packed-refs file could not be opened") from() source(err) } } } } impl<'r, A> Reference<'r, A> where A: Access + Sized, { pub fn peel_to_id_in_place(&mut self) -> Result<Object<'r, A>, peel_to_id_in_place::Error> { let repo = self.access.repo(); match &mut self.backing.take().expect("a ref must be set") { Backing::LooseFile(r) => { let oid = r.peel_to_id_in_place( &repo.refs, self.access.cache_mut().packed_refs(&repo.refs)?, |oid, buf| { repo.odb .find(oid, buf, &mut self.access.cache_mut().pack) .map(|po| po.map(|o| (o.kind, o.data))) }, )?; todo!("loose peeling") } Backing::OwnedPacked { name, target, object } => { todo!("packed peeling") } } } } mod access { use std::{cell::RefCell, convert::TryInto}; use crate::{ hash::ObjectId, reference::Backing, refs, refs::{file::find::Error, PartialName}, Access, Reference, Repository, }; /// Obtain and alter references comfortably pub trait ReferencesExt: Access + Sized { fn find_reference<'a, Name, E>( &self, name: Name, ) -> Result<Option<Reference<'_, Self>>, crate::reference::find::Error> where Name: TryInto<PartialName<'a>, Error = E>, Error: From<E>, { match self .repo() .refs .find(name, self.cache_mut().packed_refs(&self.repo().refs)?) { Ok(r) => match r { Some(r) => Ok(Some(Reference { backing: match r { refs::file::Reference::Packed(p) => Backing::OwnedPacked { name: p.name.into(), target: p.target(), object: p .object .map(|hex| ObjectId::from_hex(hex).expect("a hash kind we know")), }, refs::file::Reference::Loose(l) => Backing::LooseFile(l), } .into(), access: self, })), None => Ok(None), }, Err(err) => Err(err.into()), } } } impl<A> ReferencesExt for A where A: Access + Sized {} } use crate::odb::Find; pub use access::ReferencesExt; pub mod find { use quick_error::quick_error; use crate::refs; quick_error! { #[derive(Debug)] pub enum Error { Find(err: refs::file::find::Error) { display("An error occurred when trying to find a reference") from() source(err) } PackedRefsOpen(err: refs::packed::buffer::open::Error) { display("The packed-refs file could not be opened") from() source(err) } } } }} #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]pub enum Kind { Bare, WorkingTree,} impl Kind { pub fn is_bare(&self) -> bool { matches!(self, Kind::Bare) }} pub fn discover(directory: impl AsRef<std::path::Path>) -> Result<Repository, repository::discover::Error> { Repository::discover(directory)}
//! This crate provides the [`Repository`] abstraction which serves as a hub into all the functionality of git.
//!
//! It's powerful and won't sacrifice performance while still increasing convenience compared to using the sub-crates
//! individually. Sometimes it may hide complexity under the assumption that the performance difference doesn't matter
//! for all but the fewest tools out there, which would be using the underlying crates directly or file an issue.
//!
//! # The prelude and extensions
//!
//! With `use git_repository::prelude::*` you should be ready to go as it pulls in various extension traits to make functionality
//! available on objects that may use it.
//!
//! The method signatures are still complex and may require various arguments for configuration and cache control.
//!
//! ## Easy-Mode
//!
//! Most extensions to existing objects provide an `obj_with_extension.easy(&repo).an_easier_version_of_a_method()` or `easy(&repo)`
//! method to hide all complex arguments and sacrifice some performance for a lot of convenience.
//!
//! When starting out, use `easy(…)` and migrate to the more detailed method signatures to squeeze out more performance.
//!
//! # Cargo-features
//!
//! ## One-stop-shop
//!
//! To make using _sub-crates_ easier these are re-exported into the root of this crate.
//!
//! `git_repository::`
//! * [`hash`]
//! * [`url`]
//! * [`actor`]
//! * [`object`]
//! * [`bstr`][object::bstr]
//! * [`odb`]
//! * [`pack`][odb::pack]
//! * [`refs`]
//! * [`interrupt`]
//! * [`tempfile`]
//! * [`traverse`]
//! * [`diff`]
//! * [`Progress`]
//! * [`progress`]
//! * [`interrupt`]
//! * [`protocol`]
//! * [`transport`][protocol::transport]
//!
#![deny(unsafe_code, rust_2018_idioms)]
#![allow(missing_docs, unused)]
use crate::hash::ObjectId;
use std::{cell::RefCell, path::PathBuf, rc::Rc, sync::Arc};
// Re-exports to make this a potential one-stop shop crate avoiding people from having to reference various crates themselves.
// This also means that their major version changes affect our major version, but that's alright as we directly expose their
// APIs/instances anyway.
pub use git_actor as actor;
#[cfg(feature = "git-diff")]
pub use git_diff as diff;
pub use git_features::{parallel, progress, progress::Progress};
pub use git_hash as hash;
pub use git_object as object;
pub use git_odb as odb;
#[cfg(feature = "git-protocol")]
pub use git_protocol as protocol;
pub use git_ref as refs;
pub use git_tempfile as tempfile;
#[cfg(feature = "git-traverse")]
pub use git_traverse as traverse;
#[cfg(feature = "git-url")]
pub use git_url as url;
pub mod interrupt;
#[cfg(feature = "git-traverse")]
pub mod ext;
pub mod prelude {
pub use git_features::parallel::reduce::Finalize;
pub use git_odb::{Find, FindExt, Write};
#[cfg(all(feature = "git-traverse"))]
pub use crate::ext::*;
pub use crate::reference::ReferencesExt;
}
pub mod init;
pub mod path;
pub use path::Path;
pub mod repository;
pub struct Repository {
pub refs: git_ref::file::Store,
pub odb: git_odb::linked::Store,
pub working_tree: Option<PathBuf>,
}
mod handles {
use std::{cell::RefCell, rc::Rc, sync::Arc};
use crate::{Cache, Repository};
pub struct Shared {
pub repo: Rc<Repository>,
pub cache: RefCell<Cache>,
}
/// A handle is what threaded programs would use to have thread-local but otherwise shared versions the same `Repository`.
///
/// Mutable data present in the `Handle` itself while keeping the parent `Repository` (which has its own cache) shared.
/// Otherwise handles reflect the API of a `Repository`.
pub struct SharedArc {
pub repo: Arc<Repository>,
pub cache: RefCell<Cache>,
}
impl Clone for Shared {
fn clone(&self) -> Self {
Shared {
repo: Rc::clone(&self.repo),
cache: RefCell::new(Default::default()),
}
}
}
impl Clone for SharedArc {
fn clone(&self) -> Self {
SharedArc {
repo: Arc::clone(&self.repo),
cache: RefCell::new(Default::default()),
}
}
}
impl From<Repository> for Shared {
fn from(repo: Repository) -> Self {
Shared {
repo: Rc::new(repo),
cache: RefCell::new(Default::default()),
}
}
}
impl From<Repository> for SharedArc {
fn from(repo: Repository) -> Self {
SharedArc {
repo: Arc::new(repo),
cache: RefCell::new(Default::default()),
}
}
}
impl Repository {
pub fn into_shared(self) -> Shared {
self.into()
}
pub fn into_shared_arc(self) -> SharedArc {
self.into()
}
}
}
pub use handles::{Shared, SharedArc};
#[derive(Default)]
pub struct Cache {
packed_refs: Option<refs::packed::Buffer>,
pub pack: odb::pack::cache::Never, // TODO: choose great alround cache
pub buf: Vec<u8>,
}
mod traits {
use std::cell::RefMut;
use crate::{Cache, Repository, Shared, SharedArc};
pub trait Access {
fn repo(&self) -> &Repository;
fn cache_mut(&self) -> RefMut<'_, Cache>;
}
impl Access for Shared {
fn repo(&self) -> &Repository {
self.repo.as_ref()
}
fn cache_mut(&self) -> RefMut<'_, Cache> {
self.cache.borrow_mut()
}
}
impl Access for SharedArc {
fn repo(&self) -> &Repository {
self.repo.as_ref()
}
fn cache_mut(&self) -> RefMut<'_, Cache> {
self.cache.borrow_mut()
}
}
}
pub use traits::Access;
mod cache {
use crate::{
refs::{file, packed},
Cache,
};
impl Cache {
pub fn packed_refs(
&mut self,
file: &file::Store,
) -> Result<Option<&packed::Buffer>, packed::buffer::open::Error> {
match self.packed_refs {
Some(ref packed) => Ok(Some(packed)),
None => {
self.packed_refs = file.packed()?;
Ok(self.packed_refs.as_ref())
}
}
}
}
}
pub struct Object<'r, A> {
id: ObjectId,
// data: odb::pack::data::Object<'a>,
access: &'r A,
}
pub struct Reference<'r, A> {
pub(crate) backing: Option<reference::Backing>,
pub(crate) access: &'r A,
}
pub mod reference {
use std::cell::RefCell;
use git_hash::ObjectId;
use crate::{refs, refs::mutable, Access, Object, Reference, Repository};
pub(crate) enum Backing {
OwnedPacked {
/// The validated full name of the reference.
name: mutable::FullName,
/// The target object id of the reference, hex encoded.
target: ObjectId,
/// The fully peeled object id, hex encoded, that the ref is ultimately pointing to
/// i.e. when all indirections are removed.
object: Option<ObjectId>,
},
LooseFile(refs::file::loose::Reference),
}
pub mod peel_to_id_in_place {
use quick_error::quick_error;
use crate::refs;
quick_error! {
#[derive(Debug)]
pub enum Error {
LoosePeelToId(err: refs::file::loose::reference::peel::to_id::Error) {
display("Could not peel loose reference")
from()
source(err)
}
PackedRefsOpen(err: refs::packed::buffer::open::Error) {
display("The packed-refs file could not be opened")
from()
source(err)
}
}
}
}
impl<'r, A> Reference<'r, A>
where
A: Access + Sized,
{
pub fn peel_to_id_in_place(&mut self) -> Result<Object<'r, A>, peel_to_id_in_place::Error> {
let repo = self.access.repo();
match &mut self.backing.take().expect("a ref must be set") {
Backing::LooseFile(r) => {
let oid = r.peel_to_id_in_place(
&repo.refs,
self.access.cache_mut().packed_refs(&repo.refs)?,
|oid, buf| {
repo.odb
.find(oid, buf, &mut self.access.cache_mut().pack)
.map(|po| po.map(|o| (o.kind, o.data)))
},
)?;
todo!("loose peeling")
}
Backing::OwnedPacked { name, target, object } => {
todo!("packed peeling")
}
}
}
}
mod access {
use std::{cell::RefCell, convert::TryInto};
use crate::{
hash::ObjectId,
reference::Backing,
refs,
refs::{file::find::Error, PartialName},
Access, Reference, Repository,
};
/// Obtain and alter references comfortably
pub trait ReferencesExt: Access + Sized {
fn find_reference<'a, Name, E>(
&self,
name: Name,
) -> Result<Option<Reference<'_, Self>>, crate::reference::find::Error>
where
Name: TryInto<PartialName<'a>, Error = E>,
Error: From<E>,
{
match self
.repo()
.refs
.find(name, self.cache_mut().packed_refs(&self.repo().refs)?)
{
Ok(r) => match r {
Some(r) => Ok(Some(Reference {
backing: match r {
refs::file::Reference::Packed(p) => Backing::OwnedPacked {
name: p.name.into(),
target: p.target(),
object: p
.object
.map(|hex| ObjectId::from_hex(hex).expect("a hash kind we know")),
},
refs::file::Reference::Loose(l) => Backing::LooseFile(l),
}
.into(),
access: self,
})),
None => Ok(None),
},
Err(err) => Err(err.into()),
}
}
}
impl<A> ReferencesExt for A where A: Access + Sized {}
}
use crate::odb::Find;
pub use access::ReferencesExt;
pub mod find {
use quick_error::quick_error;
use crate::refs;
quick_error! {
#[derive(Debug)]
pub enum Error {
Find(err: refs::file::find::Error) {
display("An error occurred when trying to find a reference")
from()
source(err)
}
PackedRefsOpen(err: refs::packed::buffer::open::Error) {
display("The packed-refs file could not be opened")
from()
source(err)
}
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Kind {
Bare,
WorkingTree,
}
impl Kind {
pub fn is_bare(&self) -> bool {
matches!(self, Kind::Bare)
}
}
pub fn discover(directory: impl AsRef<std::path::Path>) -> Result<Repository, repository::discover::Error> {
Repository::discover(directory)
}