Skip to content

Commit

Permalink
Expose the mempack backend in git2 (#593)
Browse files Browse the repository at this point in the history
This also adds a method to override the ODB on the repository, which is also
exposed by libgit2. This allows the user to create a new ODB, configure it,
and install it on the repository.
  • Loading branch information
staktrace committed Jul 20, 2020
1 parent a3e129e commit e36281d
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/lib.rs
Expand Up @@ -97,6 +97,7 @@ pub use crate::index::{
Index, IndexConflict, IndexConflicts, IndexEntries, IndexEntry, IndexMatchedPath,
};
pub use crate::indexer::{IndexerProgress, Progress};
pub use crate::mempack::Mempack;
pub use crate::merge::{AnnotatedCommit, MergeOptions};
pub use crate::message::{message_prettify, DEFAULT_COMMENT_CHAR};
pub use crate::note::{Note, Notes};
Expand Down Expand Up @@ -654,6 +655,7 @@ mod diff;
mod error;
mod index;
mod indexer;
mod mempack;
mod merge;
mod message;
mod note;
Expand Down
49 changes: 49 additions & 0 deletions src/mempack.rs
@@ -0,0 +1,49 @@
use std::marker;

use crate::util::Binding;
use crate::{raw, Buf, Error, Odb, Repository};

/// A structure to represent a mempack backend for the object database. The
/// Mempack is bound to the Odb that it was created from, and cannot outlive
/// that Odb.
pub struct Mempack<'odb> {
raw: *mut raw::git_odb_backend,
_marker: marker::PhantomData<&'odb Odb<'odb>>,
}

impl<'odb> Binding for Mempack<'odb> {
type Raw = *mut raw::git_odb_backend;

unsafe fn from_raw(raw: *mut raw::git_odb_backend) -> Mempack<'odb> {
Mempack {
raw: raw,
_marker: marker::PhantomData,
}
}

fn raw(&self) -> *mut raw::git_odb_backend {
self.raw
}
}

// We don't need to implement `Drop` for Mempack because it is owned by the
// odb to which it is attached, and that will take care of freeing the mempack
// and associated memory.

impl<'odb> Mempack<'odb> {
/// Dumps the contents of the mempack into the provided buffer.
pub fn dump(&self, repo: &Repository, buf: &mut Buf) -> Result<(), Error> {
unsafe {
try_call!(raw::git_mempack_dump(buf.raw(), repo.raw(), self.raw));
}
Ok(())
}

/// Clears all data in the mempack.
pub fn reset(&self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_mempack_reset(self.raw));
}
Ok(())
}
}
84 changes: 83 additions & 1 deletion src/odb.rs
Expand Up @@ -10,7 +10,7 @@ use libc::{c_char, c_int, c_void, size_t};

use crate::panic;
use crate::util::Binding;
use crate::{raw, Error, IndexerProgress, Object, ObjectType, Oid, Progress};
use crate::{raw, Error, IndexerProgress, Mempack, Object, ObjectType, Oid, Progress};

/// A structure to represent a git object database
pub struct Odb<'repo> {
Expand Down Expand Up @@ -218,6 +218,47 @@ impl<'repo> Odb<'repo> {
Ok(())
}
}

/// Create a new mempack backend, and add it to this odb with the given
/// priority. Higher values give the backend higher precedence. The default
/// loose and pack backends have priorities 1 and 2 respectively (hard-coded
/// in libgit2). A reference to the new mempack backend is returned on
/// success. The lifetime of the backend must be contained within the
/// lifetime of this odb, since deletion of the odb will also result in
/// deletion of the mempack backend.
///
/// Here is an example that fails to compile because it tries to hold the
/// mempack reference beyond the odb's lifetime:
///
/// ```compile_fail
/// use git2::Odb;
/// let mempack = {
/// let odb = Odb::new().unwrap();
/// odb.add_new_mempack_backend(1000).unwrap()
/// };
/// ```
pub fn add_new_mempack_backend<'odb>(
&'odb self,
priority: i32,
) -> Result<Mempack<'odb>, Error> {
unsafe {
let mut mempack = ptr::null_mut();
// The mempack backend object in libgit2 is only ever freed by an
// odb that has the backend in its list. So to avoid potentially
// leaking the mempack backend, this API ensures that the backend
// is added to the odb before returning it. The lifetime of the
// mempack is also bound to the lifetime of the odb, so that users
// can't end up with a dangling reference to a mempack object that
// was actually freed when the odb was destroyed.
try_call!(raw::git_mempack_new(&mut mempack));
try_call!(raw::git_odb_add_backend(
self.raw,
mempack,
priority as c_int
));
Ok(Mempack::from_raw(mempack))
}
}
}

/// An object from the Object Database.
Expand Down Expand Up @@ -626,4 +667,45 @@ mod tests {
}
assert_eq!(progress_called, true);
}

#[test]
fn write_with_mempack() {
use crate::{Buf, ResetType};
use std::io::Write;
use std::path::Path;

// Create a repo, add a mempack backend
let (_td, repo) = crate::test::repo_init();
let odb = repo.odb().unwrap();
let mempack = odb.add_new_mempack_backend(1000).unwrap();

// Sanity check that foo doesn't exist initially
let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
assert!(!foo_file.exists());

// Make a commit that adds foo. This writes new stuff into the mempack
// backend.
let (oid1, _id) = crate::test::commit(&repo);
let commit1 = repo.find_commit(oid1).unwrap();
t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
assert!(foo_file.exists());

// Dump the mempack modifications into a buf, and reset it. This "erases"
// commit-related objects from the repository. Ensure the commit appears
// to have become invalid, by checking for failure in `reset --hard`.
let mut buf = Buf::new();
mempack.dump(&repo, &mut buf).unwrap();
mempack.reset().unwrap();
assert!(repo
.reset(commit1.as_object(), ResetType::Hard, None)
.is_err());

// Write the buf into a packfile in the repo. This brings back the
// missing objects, and we verify everything is good again.
let mut packwriter = odb.packwriter().unwrap();
packwriter.write(&buf).unwrap();
packwriter.commit().unwrap();
t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
assert!(foo_file.exists());
}
}
8 changes: 8 additions & 0 deletions src/repo.rs
Expand Up @@ -1055,6 +1055,14 @@ impl Repository {
}
}

/// Override the object database for this repository
pub fn set_odb(&self, odb: &Odb<'_>) -> Result<(), Error> {
unsafe {
try_call!(raw::git_repository_set_odb(self.raw(), odb.raw()));
}
Ok(())
}

/// Create a new branch pointing at a target commit
///
/// A new direct reference will be created pointing to this target commit.
Expand Down

0 comments on commit e36281d

Please sign in to comment.