diff --git a/src/lib.rs b/src/lib.rs index a7fac14b81..9901ba8f52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,6 +123,7 @@ pub use crate::pathspec::{PathspecDiffEntries, PathspecEntries}; pub use crate::proxy_options::ProxyOptions; pub use crate::push_update::PushUpdate; pub use crate::rebase::{Rebase, RebaseOperation, RebaseOperationType, RebaseOptions}; +pub use crate::refdb::Refdb; pub use crate::reference::{Reference, ReferenceNames, References}; pub use crate::reflog::{Reflog, ReflogEntry, ReflogIter}; pub use crate::refspec::Refspec; @@ -727,6 +728,7 @@ mod pathspec; mod proxy_options; mod push_update; mod rebase; +mod refdb; mod reference; mod reflog; mod refspec; diff --git a/src/refdb.rs b/src/refdb.rs new file mode 100644 index 0000000000..6d94c6ca5e --- /dev/null +++ b/src/refdb.rs @@ -0,0 +1,85 @@ +use std::marker; + +use crate::util::Binding; +use crate::{raw, Error, Repository}; + +/// A structure to represent a git reference database. +pub struct Refdb<'repo> { + raw: *mut raw::git_refdb, + _marker: marker::PhantomData<&'repo Repository>, +} + +impl Drop for Refdb<'_> { + fn drop(&mut self) { + unsafe { raw::git_refdb_free(self.raw) } + } +} + +impl<'repo> Binding for Refdb<'repo> { + type Raw = *mut raw::git_refdb; + + unsafe fn from_raw(raw: *mut raw::git_refdb) -> Refdb<'repo> { + Refdb { + raw, + _marker: marker::PhantomData, + } + } + + fn raw(&self) -> *mut raw::git_refdb { + self.raw + } +} + +impl<'repo> Refdb<'repo> { + /// Suggests that the reference database compress or optimize its + /// references. This mechanism is implementation specific. For on-disk + /// reference databases, for example, this may pack all loose references. + pub fn compress(&self) -> Result<(), Error> { + unsafe { + try_call!(raw::git_refdb_compress(self.raw)); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn smoke() { + let (_td, repo) = crate::test::repo_init(); + let refdb = repo.refdb().unwrap(); + refdb.compress().unwrap(); + } + + #[test] + fn set_refdb_roundtrip() { + let (_td, repo) = crate::test::repo_init(); + let refdb = repo.refdb().unwrap(); + repo.set_refdb(&refdb).unwrap(); + // References should still be resolvable after setting the same refdb back. + repo.refname_to_id("HEAD").unwrap(); + } + + #[test] + fn compress_with_loose_refs() { + let (_td, repo) = crate::test::repo_init(); + let head_id = repo.refname_to_id("HEAD").unwrap(); + for i in 0..10 { + repo.reference( + &format!("refs/tags/refdb-test-{}", i), + head_id, + false, + "test", + ) + .unwrap(); + } + let refdb = repo.refdb().unwrap(); + refdb.compress().unwrap(); + assert_eq!( + repo.references_glob("refs/tags/refdb-test-*") + .unwrap() + .count(), + 10 + ); + } +} diff --git a/src/reference.rs b/src/reference.rs index 0af845d7c5..5ab4a16e00 100644 --- a/src/reference.rs +++ b/src/reference.rs @@ -22,20 +22,20 @@ const GIT_REFNAME_MAX: usize = 1024; /// [`marker::PhantomData`], since all that matters is that it is tied to the /// lifetime of the [`Repository`], but this helps distinguish the actual /// references involved. -struct Refdb<'repo>(#[allow(dead_code)] &'repo Repository); +struct RefdbMarker<'repo>(#[allow(dead_code)] &'repo Repository); /// A structure to represent a git [reference][1]. /// /// [1]: http://git-scm.com/book/en/Git-Internals-Git-References pub struct Reference<'repo> { raw: *mut raw::git_reference, - _marker: marker::PhantomData>, + _marker: marker::PhantomData>, } /// An iterator over the references in a repository. pub struct References<'repo> { raw: *mut raw::git_reference_iterator, - _marker: marker::PhantomData>, + _marker: marker::PhantomData>, } /// An iterator over the names of references in a repository. diff --git a/src/repo.rs b/src/repo.rs index ab686b5bc0..dad9c0dc48 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -34,7 +34,7 @@ use crate::{ Blob, BlobWriter, Branch, BranchType, Branches, Commit, Config, Index, IndexEntry, Oid, Tree, }; use crate::{Describe, IntoCString, Reflog, RepositoryInitMode, RevparseMode}; -use crate::{DescribeOptions, Diff, DiffOptions, Odb, PackBuilder, TreeBuilder}; +use crate::{DescribeOptions, Diff, DiffOptions, Odb, PackBuilder, Refdb, TreeBuilder}; use crate::{Note, Notes, ObjectType, Revwalk, Status, StatusOptions, Statuses, Tag, Transaction}; type MergeheadForeachCb<'a> = dyn FnMut(&Oid) -> bool + 'a; @@ -1232,20 +1232,30 @@ impl Repository { Ok(()) } - /// Suggests that the reference database compress or optimize its - /// references. This mechanism is implementation specific. For on-disk - /// reference databases, for example, this may pack all loose references. - pub fn refdb_compress(&self) -> Result<(), Error> { + /// Get the reference database for this repository. + pub fn refdb(&self) -> Result, Error> { let mut refdb = ptr::null_mut(); unsafe { try_call!(raw::git_repository_refdb(&mut refdb, self.raw())); - let result = crate::call::c_try(raw::git_refdb_compress(refdb)); - raw::git_refdb_free(refdb); - result?; + Ok(Binding::from_raw(refdb)) + } + } + + /// Override the reference database for this repository + pub fn set_refdb(&self, refdb: &Refdb<'_>) -> Result<(), Error> { + unsafe { + try_call!(raw::git_repository_set_refdb(self.raw(), refdb.raw())); } Ok(()) } + /// Suggests that the reference database compress or optimize its + /// references. This mechanism is implementation specific. For on-disk + /// reference databases, for example, this may pack all loose references. + pub fn refdb_compress(&self) -> Result<(), Error> { + self.refdb()?.compress() + } + /// Create a new branch pointing at a target commit /// /// A new direct reference will be created pointing to this target commit.