1 use std::io;
2 use std::marker;
3 use std::mem::MaybeUninit;
4 use std::ptr;
5 use std::slice;
6
7 use std::ffi::CString;
8
9 use libc::{c_char, c_int, c_void, size_t};
10
11 use crate::panic;
12 use crate::util::Binding;
13 use crate::{raw, Error, IndexerProgress, Mempack, Object, ObjectType, Oid, Progress};
14
15 /// A structure to represent a git object database
16 pub struct Odb<'repo> {
17 raw: *mut raw::git_odb,
18 _marker: marker::PhantomData<Object<'repo>>,
19 }
20
21 // `git_odb` uses locking and atomics internally.
22 unsafe impl<'repo> Send for Odb<'repo> {}
23 unsafe impl<'repo> Sync for Odb<'repo> {}
24
25 impl<'repo> Binding for Odb<'repo> {
26 type Raw = *mut raw::git_odb;
27
from_raw(raw: *mut raw::git_odb) -> Odb<'repo>28 unsafe fn from_raw(raw: *mut raw::git_odb) -> Odb<'repo> {
29 Odb {
30 raw,
31 _marker: marker::PhantomData,
32 }
33 }
raw(&self) -> *mut raw::git_odb34 fn raw(&self) -> *mut raw::git_odb {
35 self.raw
36 }
37 }
38
39 impl<'repo> Drop for Odb<'repo> {
drop(&mut self)40 fn drop(&mut self) {
41 unsafe { raw::git_odb_free(self.raw) }
42 }
43 }
44
45 impl<'repo> Odb<'repo> {
46 /// Creates an object database without any backends.
new<'a>() -> Result<Odb<'a>, Error>47 pub fn new<'a>() -> Result<Odb<'a>, Error> {
48 crate::init();
49 unsafe {
50 let mut out = ptr::null_mut();
51 try_call!(raw::git_odb_new(&mut out));
52 Ok(Odb::from_raw(out))
53 }
54 }
55
56 /// Create object database reading stream.
57 ///
58 /// Note that most backends do not support streaming reads because they store their objects as compressed/delta'ed blobs.
59 /// If the backend does not support streaming reads, use the `read` method instead.
reader(&self, oid: Oid) -> Result<(OdbReader<'_>, usize, ObjectType), Error>60 pub fn reader(&self, oid: Oid) -> Result<(OdbReader<'_>, usize, ObjectType), Error> {
61 let mut out = ptr::null_mut();
62 let mut size = 0usize;
63 let mut otype: raw::git_object_t = ObjectType::Any.raw();
64 unsafe {
65 try_call!(raw::git_odb_open_rstream(
66 &mut out,
67 &mut size,
68 &mut otype,
69 self.raw,
70 oid.raw()
71 ));
72 Ok((
73 OdbReader::from_raw(out),
74 size,
75 ObjectType::from_raw(otype).unwrap(),
76 ))
77 }
78 }
79
80 /// Create object database writing stream.
81 ///
82 /// The type and final length of the object must be specified when opening the stream.
83 /// If the backend does not support streaming writes, use the `write` method instead.
writer(&self, size: usize, obj_type: ObjectType) -> Result<OdbWriter<'_>, Error>84 pub fn writer(&self, size: usize, obj_type: ObjectType) -> Result<OdbWriter<'_>, Error> {
85 let mut out = ptr::null_mut();
86 unsafe {
87 try_call!(raw::git_odb_open_wstream(
88 &mut out,
89 self.raw,
90 size as raw::git_object_size_t,
91 obj_type.raw()
92 ));
93 Ok(OdbWriter::from_raw(out))
94 }
95 }
96
97 /// Iterate over all objects in the object database.s
foreach<C>(&self, mut callback: C) -> Result<(), Error> where C: FnMut(&Oid) -> bool,98 pub fn foreach<C>(&self, mut callback: C) -> Result<(), Error>
99 where
100 C: FnMut(&Oid) -> bool,
101 {
102 unsafe {
103 let mut data = ForeachCbData {
104 callback: &mut callback,
105 };
106 let cb: raw::git_odb_foreach_cb = Some(foreach_cb);
107 try_call!(raw::git_odb_foreach(
108 self.raw(),
109 cb,
110 &mut data as *mut _ as *mut _
111 ));
112 Ok(())
113 }
114 }
115
116 /// Read an object from the database.
read(&self, oid: Oid) -> Result<OdbObject<'_>, Error>117 pub fn read(&self, oid: Oid) -> Result<OdbObject<'_>, Error> {
118 let mut out = ptr::null_mut();
119 unsafe {
120 try_call!(raw::git_odb_read(&mut out, self.raw, oid.raw()));
121 Ok(OdbObject::from_raw(out))
122 }
123 }
124
125 /// Reads the header of an object from the database
126 /// without reading the full content.
read_header(&self, oid: Oid) -> Result<(usize, ObjectType), Error>127 pub fn read_header(&self, oid: Oid) -> Result<(usize, ObjectType), Error> {
128 let mut size: usize = 0;
129 let mut kind_id: i32 = ObjectType::Any.raw();
130
131 unsafe {
132 try_call!(raw::git_odb_read_header(
133 &mut size as *mut size_t,
134 &mut kind_id as *mut raw::git_object_t,
135 self.raw,
136 oid.raw()
137 ));
138
139 Ok((size, ObjectType::from_raw(kind_id).unwrap()))
140 }
141 }
142
143 /// Write an object to the database.
write(&self, kind: ObjectType, data: &[u8]) -> Result<Oid, Error>144 pub fn write(&self, kind: ObjectType, data: &[u8]) -> Result<Oid, Error> {
145 unsafe {
146 let mut out = raw::git_oid {
147 id: [0; raw::GIT_OID_RAWSZ],
148 };
149 try_call!(raw::git_odb_write(
150 &mut out,
151 self.raw,
152 data.as_ptr() as *const c_void,
153 data.len(),
154 kind.raw()
155 ));
156 Ok(Oid::from_raw(&mut out))
157 }
158 }
159
160 /// Create stream for writing a pack file to the ODB
packwriter(&self) -> Result<OdbPackwriter<'_>, Error>161 pub fn packwriter(&self) -> Result<OdbPackwriter<'_>, Error> {
162 let mut out = ptr::null_mut();
163 let progress = MaybeUninit::uninit();
164 let progress_cb: raw::git_indexer_progress_cb = Some(write_pack_progress_cb);
165 let progress_payload = Box::new(OdbPackwriterCb { cb: None });
166 let progress_payload_ptr = Box::into_raw(progress_payload);
167
168 unsafe {
169 try_call!(raw::git_odb_write_pack(
170 &mut out,
171 self.raw,
172 progress_cb,
173 progress_payload_ptr as *mut c_void
174 ));
175 }
176
177 Ok(OdbPackwriter {
178 raw: out,
179 progress,
180 progress_payload_ptr,
181 })
182 }
183
184 /// Checks if the object database has an object.
exists(&self, oid: Oid) -> bool185 pub fn exists(&self, oid: Oid) -> bool {
186 unsafe { raw::git_odb_exists(self.raw, oid.raw()) != 0 }
187 }
188
189 /// Potentially finds an object that starts with the given prefix.
exists_prefix(&self, short_oid: Oid, len: usize) -> Result<Oid, Error>190 pub fn exists_prefix(&self, short_oid: Oid, len: usize) -> Result<Oid, Error> {
191 unsafe {
192 let mut out = raw::git_oid {
193 id: [0; raw::GIT_OID_RAWSZ],
194 };
195 try_call!(raw::git_odb_exists_prefix(
196 &mut out,
197 self.raw,
198 short_oid.raw(),
199 len
200 ));
201 Ok(Oid::from_raw(&out))
202 }
203 }
204
205 /// Refresh the object database.
206 /// This should never be needed, and is
207 /// provided purely for convenience.
208 /// The object database will automatically
209 /// refresh when an object is not found when
210 /// requested.
refresh(&self) -> Result<(), Error>211 pub fn refresh(&self) -> Result<(), Error> {
212 unsafe {
213 try_call!(raw::git_odb_refresh(self.raw));
214 Ok(())
215 }
216 }
217
218 /// Adds an alternate disk backend to the object database.
add_disk_alternate(&self, path: &str) -> Result<(), Error>219 pub fn add_disk_alternate(&self, path: &str) -> Result<(), Error> {
220 unsafe {
221 let path = CString::new(path)?;
222 try_call!(raw::git_odb_add_disk_alternate(self.raw, path));
223 Ok(())
224 }
225 }
226
227 /// Create a new mempack backend, and add it to this odb with the given
228 /// priority. Higher values give the backend higher precedence. The default
229 /// loose and pack backends have priorities 1 and 2 respectively (hard-coded
230 /// in libgit2). A reference to the new mempack backend is returned on
231 /// success. The lifetime of the backend must be contained within the
232 /// lifetime of this odb, since deletion of the odb will also result in
233 /// deletion of the mempack backend.
234 ///
235 /// Here is an example that fails to compile because it tries to hold the
236 /// mempack reference beyond the odb's lifetime:
237 ///
238 /// ```compile_fail
239 /// use git2::Odb;
240 /// let mempack = {
241 /// let odb = Odb::new().unwrap();
242 /// odb.add_new_mempack_backend(1000).unwrap()
243 /// };
244 /// ```
add_new_mempack_backend<'odb>( &'odb self, priority: i32, ) -> Result<Mempack<'odb>, Error>245 pub fn add_new_mempack_backend<'odb>(
246 &'odb self,
247 priority: i32,
248 ) -> Result<Mempack<'odb>, Error> {
249 unsafe {
250 let mut mempack = ptr::null_mut();
251 // The mempack backend object in libgit2 is only ever freed by an
252 // odb that has the backend in its list. So to avoid potentially
253 // leaking the mempack backend, this API ensures that the backend
254 // is added to the odb before returning it. The lifetime of the
255 // mempack is also bound to the lifetime of the odb, so that users
256 // can't end up with a dangling reference to a mempack object that
257 // was actually freed when the odb was destroyed.
258 try_call!(raw::git_mempack_new(&mut mempack));
259 try_call!(raw::git_odb_add_backend(
260 self.raw,
261 mempack,
262 priority as c_int
263 ));
264 Ok(Mempack::from_raw(mempack))
265 }
266 }
267 }
268
269 /// An object from the Object Database.
270 pub struct OdbObject<'a> {
271 raw: *mut raw::git_odb_object,
272 _marker: marker::PhantomData<Object<'a>>,
273 }
274
275 impl<'a> Binding for OdbObject<'a> {
276 type Raw = *mut raw::git_odb_object;
277
from_raw(raw: *mut raw::git_odb_object) -> OdbObject<'a>278 unsafe fn from_raw(raw: *mut raw::git_odb_object) -> OdbObject<'a> {
279 OdbObject {
280 raw,
281 _marker: marker::PhantomData,
282 }
283 }
284
raw(&self) -> *mut raw::git_odb_object285 fn raw(&self) -> *mut raw::git_odb_object {
286 self.raw
287 }
288 }
289
290 impl<'a> Drop for OdbObject<'a> {
drop(&mut self)291 fn drop(&mut self) {
292 unsafe { raw::git_odb_object_free(self.raw) }
293 }
294 }
295
296 impl<'a> OdbObject<'a> {
297 /// Get the object type.
kind(&self) -> ObjectType298 pub fn kind(&self) -> ObjectType {
299 unsafe { ObjectType::from_raw(raw::git_odb_object_type(self.raw)).unwrap() }
300 }
301
302 /// Get the object size.
len(&self) -> usize303 pub fn len(&self) -> usize {
304 unsafe { raw::git_odb_object_size(self.raw) }
305 }
306
307 /// Get the object data.
data(&self) -> &[u8]308 pub fn data(&self) -> &[u8] {
309 unsafe {
310 let size = self.len();
311 let ptr: *const u8 = raw::git_odb_object_data(self.raw) as *const u8;
312 let buffer = slice::from_raw_parts(ptr, size);
313 return buffer;
314 }
315 }
316
317 /// Get the object id.
id(&self) -> Oid318 pub fn id(&self) -> Oid {
319 unsafe { Oid::from_raw(raw::git_odb_object_id(self.raw)) }
320 }
321 }
322
323 /// A structure to represent a git ODB rstream
324 pub struct OdbReader<'repo> {
325 raw: *mut raw::git_odb_stream,
326 _marker: marker::PhantomData<Object<'repo>>,
327 }
328
329 // `git_odb_stream` is not thread-safe internally, so it can't use `Sync`, but moving it to another
330 // thread and continuing to read will work.
331 unsafe impl<'repo> Send for OdbReader<'repo> {}
332
333 impl<'repo> Binding for OdbReader<'repo> {
334 type Raw = *mut raw::git_odb_stream;
335
from_raw(raw: *mut raw::git_odb_stream) -> OdbReader<'repo>336 unsafe fn from_raw(raw: *mut raw::git_odb_stream) -> OdbReader<'repo> {
337 OdbReader {
338 raw,
339 _marker: marker::PhantomData,
340 }
341 }
raw(&self) -> *mut raw::git_odb_stream342 fn raw(&self) -> *mut raw::git_odb_stream {
343 self.raw
344 }
345 }
346
347 impl<'repo> Drop for OdbReader<'repo> {
drop(&mut self)348 fn drop(&mut self) {
349 unsafe { raw::git_odb_stream_free(self.raw) }
350 }
351 }
352
353 impl<'repo> io::Read for OdbReader<'repo> {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>354 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
355 unsafe {
356 let ptr = buf.as_ptr() as *mut c_char;
357 let len = buf.len();
358 let res = raw::git_odb_stream_read(self.raw, ptr, len);
359 if res < 0 {
360 Err(io::Error::new(io::ErrorKind::Other, "Read error"))
361 } else {
362 Ok(len)
363 }
364 }
365 }
366 }
367
368 /// A structure to represent a git ODB wstream
369 pub struct OdbWriter<'repo> {
370 raw: *mut raw::git_odb_stream,
371 _marker: marker::PhantomData<Object<'repo>>,
372 }
373
374 // `git_odb_stream` is not thread-safe internally, so it can't use `Sync`, but moving it to another
375 // thread and continuing to write will work.
376 unsafe impl<'repo> Send for OdbWriter<'repo> {}
377
378 impl<'repo> OdbWriter<'repo> {
379 /// Finish writing to an ODB stream
380 ///
381 /// This method can be used to finalize writing object to the database and get an identifier.
382 /// The object will take its final name and will be available to the odb.
383 /// This method will fail if the total number of received bytes differs from the size declared with odb_writer()
384 /// Attepting write after finishing will be ignored.
finalize(&mut self) -> Result<Oid, Error>385 pub fn finalize(&mut self) -> Result<Oid, Error> {
386 let mut raw = raw::git_oid {
387 id: [0; raw::GIT_OID_RAWSZ],
388 };
389 unsafe {
390 try_call!(raw::git_odb_stream_finalize_write(&mut raw, self.raw));
391 Ok(Binding::from_raw(&raw as *const _))
392 }
393 }
394 }
395
396 impl<'repo> Binding for OdbWriter<'repo> {
397 type Raw = *mut raw::git_odb_stream;
398
from_raw(raw: *mut raw::git_odb_stream) -> OdbWriter<'repo>399 unsafe fn from_raw(raw: *mut raw::git_odb_stream) -> OdbWriter<'repo> {
400 OdbWriter {
401 raw,
402 _marker: marker::PhantomData,
403 }
404 }
raw(&self) -> *mut raw::git_odb_stream405 fn raw(&self) -> *mut raw::git_odb_stream {
406 self.raw
407 }
408 }
409
410 impl<'repo> Drop for OdbWriter<'repo> {
drop(&mut self)411 fn drop(&mut self) {
412 unsafe { raw::git_odb_stream_free(self.raw) }
413 }
414 }
415
416 impl<'repo> io::Write for OdbWriter<'repo> {
write(&mut self, buf: &[u8]) -> io::Result<usize>417 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
418 unsafe {
419 let ptr = buf.as_ptr() as *const c_char;
420 let len = buf.len();
421 let res = raw::git_odb_stream_write(self.raw, ptr, len);
422 if res < 0 {
423 Err(io::Error::new(io::ErrorKind::Other, "Write error"))
424 } else {
425 Ok(buf.len())
426 }
427 }
428 }
flush(&mut self) -> io::Result<()>429 fn flush(&mut self) -> io::Result<()> {
430 Ok(())
431 }
432 }
433
434 struct OdbPackwriterCb<'repo> {
435 cb: Option<Box<IndexerProgress<'repo>>>,
436 }
437
438 /// A stream to write a packfile to the ODB
439 pub struct OdbPackwriter<'repo> {
440 raw: *mut raw::git_odb_writepack,
441 progress: MaybeUninit<raw::git_indexer_progress>,
442 progress_payload_ptr: *mut OdbPackwriterCb<'repo>,
443 }
444
445 impl<'repo> OdbPackwriter<'repo> {
446 /// Finish writing the packfile
commit(&mut self) -> Result<i32, Error>447 pub fn commit(&mut self) -> Result<i32, Error> {
448 unsafe {
449 let writepack = &*self.raw;
450 let res = match writepack.commit {
451 Some(commit) => commit(self.raw, self.progress.as_mut_ptr()),
452 None => -1,
453 };
454
455 if res < 0 {
456 Err(Error::last_error(res).unwrap())
457 } else {
458 Ok(res)
459 }
460 }
461 }
462
463 /// The callback through which progress is monitored. Be aware that this is
464 /// called inline, so performance may be affected.
progress<F>(&mut self, cb: F) -> &mut OdbPackwriter<'repo> where F: FnMut(Progress<'_>) -> bool + 'repo,465 pub fn progress<F>(&mut self, cb: F) -> &mut OdbPackwriter<'repo>
466 where
467 F: FnMut(Progress<'_>) -> bool + 'repo,
468 {
469 let progress_payload =
470 unsafe { &mut *(self.progress_payload_ptr as *mut OdbPackwriterCb<'_>) };
471
472 progress_payload.cb = Some(Box::new(cb) as Box<IndexerProgress<'repo>>);
473 self
474 }
475 }
476
477 impl<'repo> io::Write for OdbPackwriter<'repo> {
write(&mut self, buf: &[u8]) -> io::Result<usize>478 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
479 unsafe {
480 let ptr = buf.as_ptr() as *mut c_void;
481 let len = buf.len();
482
483 let writepack = &*self.raw;
484 let res = match writepack.append {
485 Some(append) => append(self.raw, ptr, len, self.progress.as_mut_ptr()),
486 None => -1,
487 };
488
489 if res < 0 {
490 Err(io::Error::new(io::ErrorKind::Other, "Write error"))
491 } else {
492 Ok(buf.len())
493 }
494 }
495 }
flush(&mut self) -> io::Result<()>496 fn flush(&mut self) -> io::Result<()> {
497 Ok(())
498 }
499 }
500
501 impl<'repo> Drop for OdbPackwriter<'repo> {
drop(&mut self)502 fn drop(&mut self) {
503 unsafe {
504 let writepack = &*self.raw;
505 match writepack.free {
506 Some(free) => free(self.raw),
507 None => (),
508 };
509
510 Box::from_raw(self.progress_payload_ptr);
511 }
512 }
513 }
514
515 pub type ForeachCb<'a> = dyn FnMut(&Oid) -> bool + 'a;
516
517 struct ForeachCbData<'a> {
518 pub callback: &'a mut ForeachCb<'a>,
519 }
520
foreach_cb(id: *const raw::git_oid, payload: *mut c_void) -> c_int521 extern "C" fn foreach_cb(id: *const raw::git_oid, payload: *mut c_void) -> c_int {
522 panic::wrap(|| unsafe {
523 let data = &mut *(payload as *mut ForeachCbData<'_>);
524 let res = {
525 let callback = &mut data.callback;
526 callback(&Binding::from_raw(id))
527 };
528
529 if res {
530 0
531 } else {
532 1
533 }
534 })
535 .unwrap_or(1)
536 }
537
write_pack_progress_cb( stats: *const raw::git_indexer_progress, payload: *mut c_void, ) -> c_int538 extern "C" fn write_pack_progress_cb(
539 stats: *const raw::git_indexer_progress,
540 payload: *mut c_void,
541 ) -> c_int {
542 let ok = panic::wrap(|| unsafe {
543 let payload = &mut *(payload as *mut OdbPackwriterCb<'_>);
544
545 let callback = match payload.cb {
546 Some(ref mut cb) => cb,
547 None => return true,
548 };
549
550 let progress: Progress<'_> = Binding::from_raw(stats);
551 callback(progress)
552 });
553 if ok == Some(true) {
554 0
555 } else {
556 -1
557 }
558 }
559
560 #[cfg(test)]
561 mod tests {
562 use crate::{Buf, ObjectType, Oid, Repository};
563 use std::io::prelude::*;
564 use tempfile::TempDir;
565
566 #[test]
read()567 fn read() {
568 let td = TempDir::new().unwrap();
569 let repo = Repository::init(td.path()).unwrap();
570 let dat = [4, 3, 5, 6, 9];
571 let id = repo.blob(&dat).unwrap();
572 let db = repo.odb().unwrap();
573 let obj = db.read(id).unwrap();
574 let data = obj.data();
575 let size = obj.len();
576 assert_eq!(size, 5);
577 assert_eq!(dat, data);
578 assert_eq!(id, obj.id());
579 }
580
581 #[test]
read_header()582 fn read_header() {
583 let td = TempDir::new().unwrap();
584 let repo = Repository::init(td.path()).unwrap();
585 let dat = [4, 3, 5, 6, 9];
586 let id = repo.blob(&dat).unwrap();
587 let db = repo.odb().unwrap();
588 let (size, kind) = db.read_header(id).unwrap();
589
590 assert_eq!(size, 5);
591 assert_eq!(kind, ObjectType::Blob);
592 }
593
594 #[test]
write()595 fn write() {
596 let td = TempDir::new().unwrap();
597 let repo = Repository::init(td.path()).unwrap();
598 let dat = [4, 3, 5, 6, 9];
599 let db = repo.odb().unwrap();
600 let id = db.write(ObjectType::Blob, &dat).unwrap();
601 let blob = repo.find_blob(id).unwrap();
602 assert_eq!(blob.content(), dat);
603 }
604
605 #[test]
writer()606 fn writer() {
607 let td = TempDir::new().unwrap();
608 let repo = Repository::init(td.path()).unwrap();
609 let dat = [4, 3, 5, 6, 9];
610 let db = repo.odb().unwrap();
611 let mut ws = db.writer(dat.len(), ObjectType::Blob).unwrap();
612 let wl = ws.write(&dat[0..3]).unwrap();
613 assert_eq!(wl, 3);
614 let wl = ws.write(&dat[3..5]).unwrap();
615 assert_eq!(wl, 2);
616 let id = ws.finalize().unwrap();
617 let blob = repo.find_blob(id).unwrap();
618 assert_eq!(blob.content(), dat);
619 }
620
621 #[test]
exists()622 fn exists() {
623 let td = TempDir::new().unwrap();
624 let repo = Repository::init(td.path()).unwrap();
625 let dat = [4, 3, 5, 6, 9];
626 let db = repo.odb().unwrap();
627 let id = db.write(ObjectType::Blob, &dat).unwrap();
628 assert!(db.exists(id));
629 }
630
631 #[test]
exists_prefix()632 fn exists_prefix() {
633 let td = TempDir::new().unwrap();
634 let repo = Repository::init(td.path()).unwrap();
635 let dat = [4, 3, 5, 6, 9];
636 let db = repo.odb().unwrap();
637 let id = db.write(ObjectType::Blob, &dat).unwrap();
638 let id_prefix_str = &id.to_string()[0..10];
639 let id_prefix = Oid::from_str(id_prefix_str).unwrap();
640 let found_oid = db.exists_prefix(id_prefix, 10).unwrap();
641 assert_eq!(found_oid, id);
642 }
643
644 #[test]
packwriter()645 fn packwriter() {
646 let (_td, repo_source) = crate::test::repo_init();
647 let (_td, repo_target) = crate::test::repo_init();
648 let mut builder = t!(repo_source.packbuilder());
649 let mut buf = Buf::new();
650 let (commit_source_id, _tree) = crate::test::commit(&repo_source);
651 t!(builder.insert_object(commit_source_id, None));
652 t!(builder.write_buf(&mut buf));
653 let db = repo_target.odb().unwrap();
654 let mut packwriter = db.packwriter().unwrap();
655 packwriter.write(&buf).unwrap();
656 packwriter.commit().unwrap();
657 let commit_target = repo_target.find_commit(commit_source_id).unwrap();
658 assert_eq!(commit_target.id(), commit_source_id);
659 }
660
661 #[test]
packwriter_progress()662 fn packwriter_progress() {
663 let mut progress_called = false;
664 {
665 let (_td, repo_source) = crate::test::repo_init();
666 let (_td, repo_target) = crate::test::repo_init();
667 let mut builder = t!(repo_source.packbuilder());
668 let mut buf = Buf::new();
669 let (commit_source_id, _tree) = crate::test::commit(&repo_source);
670 t!(builder.insert_object(commit_source_id, None));
671 t!(builder.write_buf(&mut buf));
672 let db = repo_target.odb().unwrap();
673 let mut packwriter = db.packwriter().unwrap();
674 packwriter.progress(|_| {
675 progress_called = true;
676 true
677 });
678 packwriter.write(&buf).unwrap();
679 packwriter.commit().unwrap();
680 }
681 assert_eq!(progress_called, true);
682 }
683
684 #[test]
write_with_mempack()685 fn write_with_mempack() {
686 use crate::{Buf, ResetType};
687 use std::io::Write;
688 use std::path::Path;
689
690 // Create a repo, add a mempack backend
691 let (_td, repo) = crate::test::repo_init();
692 let odb = repo.odb().unwrap();
693 let mempack = odb.add_new_mempack_backend(1000).unwrap();
694
695 // Sanity check that foo doesn't exist initially
696 let foo_file = Path::new(repo.workdir().unwrap()).join("foo");
697 assert!(!foo_file.exists());
698
699 // Make a commit that adds foo. This writes new stuff into the mempack
700 // backend.
701 let (oid1, _id) = crate::test::commit(&repo);
702 let commit1 = repo.find_commit(oid1).unwrap();
703 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
704 assert!(foo_file.exists());
705
706 // Dump the mempack modifications into a buf, and reset it. This "erases"
707 // commit-related objects from the repository. Ensure the commit appears
708 // to have become invalid, by checking for failure in `reset --hard`.
709 let mut buf = Buf::new();
710 mempack.dump(&repo, &mut buf).unwrap();
711 mempack.reset().unwrap();
712 assert!(repo
713 .reset(commit1.as_object(), ResetType::Hard, None)
714 .is_err());
715
716 // Write the buf into a packfile in the repo. This brings back the
717 // missing objects, and we verify everything is good again.
718 let mut packwriter = odb.packwriter().unwrap();
719 packwriter.write(&buf).unwrap();
720 packwriter.commit().unwrap();
721 t!(repo.reset(commit1.as_object(), ResetType::Hard, None));
722 assert!(foo_file.exists());
723 }
724 }
725