Skip to content

Commit e36281d

Browse files
authored
Expose the mempack backend in git2 (#593)
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.
1 parent a3e129e commit e36281d

File tree

4 files changed

+142
-1
lines changed

4 files changed

+142
-1
lines changed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub use crate::index::{
9797
Index, IndexConflict, IndexConflicts, IndexEntries, IndexEntry, IndexMatchedPath,
9898
};
9999
pub use crate::indexer::{IndexerProgress, Progress};
100+
pub use crate::mempack::Mempack;
100101
pub use crate::merge::{AnnotatedCommit, MergeOptions};
101102
pub use crate::message::{message_prettify, DEFAULT_COMMENT_CHAR};
102103
pub use crate::note::{Note, Notes};
@@ -654,6 +655,7 @@ mod diff;
654655
mod error;
655656
mod index;
656657
mod indexer;
658+
mod mempack;
657659
mod merge;
658660
mod message;
659661
mod note;

src/mempack.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::marker;
2+
3+
use crate::util::Binding;
4+
use crate::{raw, Buf, Error, Odb, Repository};
5+
6+
/// A structure to represent a mempack backend for the object database. The
7+
/// Mempack is bound to the Odb that it was created from, and cannot outlive
8+
/// that Odb.
9+
pub struct Mempack<'odb> {
10+
raw: *mut raw::git_odb_backend,
11+
_marker: marker::PhantomData<&'odb Odb<'odb>>,
12+
}
13+
14+
impl<'odb> Binding for Mempack<'odb> {
15+
type Raw = *mut raw::git_odb_backend;
16+
17+
unsafe fn from_raw(raw: *mut raw::git_odb_backend) -> Mempack<'odb> {
18+
Mempack {
19+
raw: raw,
20+
_marker: marker::PhantomData,
21+
}
22+
}
23+
24+
fn raw(&self) -> *mut raw::git_odb_backend {
25+
self.raw
26+
}
27+
}
28+
29+
// We don't need to implement `Drop` for Mempack because it is owned by the
30+
// odb to which it is attached, and that will take care of freeing the mempack
31+
// and associated memory.
32+
33+
impl<'odb> Mempack<'odb> {
34+
/// Dumps the contents of the mempack into the provided buffer.
35+
pub fn dump(&self, repo: &Repository, buf: &mut Buf) -> Result<(), Error> {
36+
unsafe {
37+
try_call!(raw::git_mempack_dump(buf.raw(), repo.raw(), self.raw));
38+
}
39+
Ok(())
40+
}
41+
42+
/// Clears all data in the mempack.
43+
pub fn reset(&self) -> Result<(), Error> {
44+
unsafe {
45+
try_call!(raw::git_mempack_reset(self.raw));
46+
}
47+
Ok(())
48+
}
49+
}

src/odb.rs

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use libc::{c_char, c_int, c_void, size_t};
1010

1111
use crate::panic;
1212
use crate::util::Binding;
13-
use crate::{raw, Error, IndexerProgress, Object, ObjectType, Oid, Progress};
13+
use crate::{raw, Error, IndexerProgress, Mempack, Object, ObjectType, Oid, Progress};
1414

1515
/// A structure to represent a git object database
1616
pub struct Odb<'repo> {
@@ -218,6 +218,47 @@ impl<'repo> Odb<'repo> {
218218
Ok(())
219219
}
220220
}
221+
222+
/// Create a new mempack backend, and add it to this odb with the given
223+
/// priority. Higher values give the backend higher precedence. The default
224+
/// loose and pack backends have priorities 1 and 2 respectively (hard-coded
225+
/// in libgit2). A reference to the new mempack backend is returned on
226+
/// success. The lifetime of the backend must be contained within the
227+
/// lifetime of this odb, since deletion of the odb will also result in
228+
/// deletion of the mempack backend.
229+
///
230+
/// Here is an example that fails to compile because it tries to hold the
231+
/// mempack reference beyond the odb's lifetime:
232+
///
233+
/// ```compile_fail
234+
/// use git2::Odb;
235+
/// let mempack = {
236+
/// let odb = Odb::new().unwrap();
237+
/// odb.add_new_mempack_backend(1000).unwrap()
238+
/// };
239+
/// ```
240+
pub fn add_new_mempack_backend<'odb>(
241+
&'odb self,
242+
priority: i32,
243+
) -> Result<Mempack<'odb>, Error> {
244+
unsafe {
245+
let mut mempack = ptr::null_mut();
246+
// The mempack backend object in libgit2 is only ever freed by an
247+
// odb that has the backend in its list. So to avoid potentially
248+
// leaking the mempack backend, this API ensures that the backend
249+
// is added to the odb before returning it. The lifetime of the
250+
// mempack is also bound to the lifetime of the odb, so that users
251+
// can't end up with a dangling reference to a mempack object that
252+
// was actually freed when the odb was destroyed.
253+
try_call!(raw::git_mempack_new(&mut mempack));
254+
try_call!(raw::git_odb_add_backend(
255+
self.raw,
256+
mempack,
257+
priority as c_int
258+
));
259+
Ok(Mempack::from_raw(mempack))
260+
}
261+
}
221262
}
222263

223264
/// An object from the Object Database.
@@ -626,4 +667,45 @@ mod tests {
626667
}
627668
assert_eq!(progress_called, true);
628669
}
670+
671+
#[test]
672+
fn write_with_mempack() {
673+
use crate::{Buf, ResetType};
674+
use std::io::Write;
675+
use std::path::Path;
676+
677+
// Create a repo, add a mempack backend
678+
let (_td, repo) = crate::test::repo_init();
679+
let odb = repo.odb().unwrap();
680+
let mempack = odb.add_new_mempack_backend(1000).unwrap();
681+
682+
// Sanity check that foo doesn't exist initially
683+
let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
684+
assert!(!foo_file.exists());
685+
686+
// Make a commit that adds foo. This writes new stuff into the mempack
687+
// backend.
688+
let (oid1, _id) = crate::test::commit(&repo);
689+
let commit1 = repo.find_commit(oid1).unwrap();
690+
t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
691+
assert!(foo_file.exists());
692+
693+
// Dump the mempack modifications into a buf, and reset it. This "erases"
694+
// commit-related objects from the repository. Ensure the commit appears
695+
// to have become invalid, by checking for failure in `reset --hard`.
696+
let mut buf = Buf::new();
697+
mempack.dump(&repo, &mut buf).unwrap();
698+
mempack.reset().unwrap();
699+
assert!(repo
700+
.reset(commit1.as_object(), ResetType::Hard, None)
701+
.is_err());
702+
703+
// Write the buf into a packfile in the repo. This brings back the
704+
// missing objects, and we verify everything is good again.
705+
let mut packwriter = odb.packwriter().unwrap();
706+
packwriter.write(&buf).unwrap();
707+
packwriter.commit().unwrap();
708+
t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
709+
assert!(foo_file.exists());
710+
}
629711
}

src/repo.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,14 @@ impl Repository {
10551055
}
10561056
}
10571057

1058+
/// Override the object database for this repository
1059+
pub fn set_odb(&self, odb: &Odb<'_>) -> Result<(), Error> {
1060+
unsafe {
1061+
try_call!(raw::git_repository_set_odb(self.raw(), odb.raw()));
1062+
}
1063+
Ok(())
1064+
}
1065+
10581066
/// Create a new branch pointing at a target commit
10591067
///
10601068
/// A new direct reference will be created pointing to this target commit.

0 commit comments

Comments
 (0)