summaryrefslogtreecommitdiffstats
path: root/vendor/git2/src/indexer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/git2/src/indexer.rs')
-rw-r--r--vendor/git2/src/indexer.rs255
1 files changed, 255 insertions, 0 deletions
diff --git a/vendor/git2/src/indexer.rs b/vendor/git2/src/indexer.rs
new file mode 100644
index 0000000..0aaf353
--- /dev/null
+++ b/vendor/git2/src/indexer.rs
@@ -0,0 +1,255 @@
+use std::ffi::CStr;
+use std::path::Path;
+use std::{io, marker, mem, ptr};
+
+use libc::c_void;
+
+use crate::odb::{write_pack_progress_cb, OdbPackwriterCb};
+use crate::util::Binding;
+use crate::{raw, Error, IntoCString, Odb};
+
+/// Struct representing the progress by an in-flight transfer.
+pub struct Progress<'a> {
+ pub(crate) raw: ProgressState,
+ pub(crate) _marker: marker::PhantomData<&'a raw::git_indexer_progress>,
+}
+
+pub(crate) enum ProgressState {
+ Borrowed(*const raw::git_indexer_progress),
+ Owned(raw::git_indexer_progress),
+}
+
+/// Callback to be invoked while indexing is in progress.
+///
+/// This callback will be periodically called with updates to the progress of
+/// the indexing so far. The return value indicates whether the indexing or
+/// transfer should continue. A return value of `false` will cancel the
+/// indexing or transfer.
+///
+/// * `progress` - the progress being made so far.
+pub type IndexerProgress<'a> = dyn FnMut(Progress<'_>) -> bool + 'a;
+
+impl<'a> Progress<'a> {
+ /// Number of objects in the packfile being downloaded
+ pub fn total_objects(&self) -> usize {
+ unsafe { (*self.raw()).total_objects as usize }
+ }
+ /// Received objects that have been hashed
+ pub fn indexed_objects(&self) -> usize {
+ unsafe { (*self.raw()).indexed_objects as usize }
+ }
+ /// Objects which have been downloaded
+ pub fn received_objects(&self) -> usize {
+ unsafe { (*self.raw()).received_objects as usize }
+ }
+ /// Locally-available objects that have been injected in order to fix a thin
+ /// pack.
+ pub fn local_objects(&self) -> usize {
+ unsafe { (*self.raw()).local_objects as usize }
+ }
+ /// Number of deltas in the packfile being downloaded
+ pub fn total_deltas(&self) -> usize {
+ unsafe { (*self.raw()).total_deltas as usize }
+ }
+ /// Received deltas that have been hashed.
+ pub fn indexed_deltas(&self) -> usize {
+ unsafe { (*self.raw()).indexed_deltas as usize }
+ }
+ /// Size of the packfile received up to now
+ pub fn received_bytes(&self) -> usize {
+ unsafe { (*self.raw()).received_bytes as usize }
+ }
+
+ /// Convert this to an owned version of `Progress`.
+ pub fn to_owned(&self) -> Progress<'static> {
+ Progress {
+ raw: ProgressState::Owned(unsafe { *self.raw() }),
+ _marker: marker::PhantomData,
+ }
+ }
+}
+
+impl<'a> Binding for Progress<'a> {
+ type Raw = *const raw::git_indexer_progress;
+ unsafe fn from_raw(raw: *const raw::git_indexer_progress) -> Progress<'a> {
+ Progress {
+ raw: ProgressState::Borrowed(raw),
+ _marker: marker::PhantomData,
+ }
+ }
+
+ fn raw(&self) -> *const raw::git_indexer_progress {
+ match self.raw {
+ ProgressState::Borrowed(raw) => raw,
+ ProgressState::Owned(ref raw) => raw as *const _,
+ }
+ }
+}
+
+/// Callback to be invoked while a transfer is in progress.
+///
+/// This callback will be periodically called with updates to the progress of
+/// the transfer so far. The return value indicates whether the transfer should
+/// continue. A return value of `false` will cancel the transfer.
+///
+/// * `progress` - the progress being made so far.
+#[deprecated(
+ since = "0.11.0",
+ note = "renamed to `IndexerProgress` to match upstream"
+)]
+#[allow(dead_code)]
+pub type TransportProgress<'a> = IndexerProgress<'a>;
+
+/// A stream to write and index a packfile
+///
+/// This is equivalent to [`crate::OdbPackwriter`], but allows to store the pack
+/// and index at an arbitrary path. It also does not require access to an object
+/// database if, and only if, the pack file is self-contained (i.e. not "thin").
+pub struct Indexer<'odb> {
+ raw: *mut raw::git_indexer,
+ progress: raw::git_indexer_progress,
+ progress_payload_ptr: *mut OdbPackwriterCb<'odb>,
+}
+
+impl<'a> Indexer<'a> {
+ /// Create a new indexer
+ ///
+ /// The [`Odb`] is used to resolve base objects when fixing thin packs. It
+ /// can be `None` if no thin pack is expected, in which case missing bases
+ /// will result in an error.
+ ///
+ /// `mode` is the permissions to use for the output files, use `0` for defaults.
+ ///
+ /// If `verify` is `false`, the indexer will bypass object connectivity checks.
+ pub fn new(odb: Option<&Odb<'a>>, path: &Path, mode: u32, verify: bool) -> Result<Self, Error> {
+ let path = path.into_c_string()?;
+
+ let odb = odb.map(Binding::raw).unwrap_or_else(ptr::null_mut);
+
+ let mut out = ptr::null_mut();
+ let progress_cb: raw::git_indexer_progress_cb = Some(write_pack_progress_cb);
+ let progress_payload = Box::new(OdbPackwriterCb { cb: None });
+ let progress_payload_ptr = Box::into_raw(progress_payload);
+
+ unsafe {
+ let mut opts = mem::zeroed();
+ try_call!(raw::git_indexer_options_init(
+ &mut opts,
+ raw::GIT_INDEXER_OPTIONS_VERSION
+ ));
+ opts.progress_cb = progress_cb;
+ opts.progress_cb_payload = progress_payload_ptr as *mut c_void;
+ opts.verify = verify.into();
+
+ try_call!(raw::git_indexer_new(&mut out, path, mode, odb, &mut opts));
+ }
+
+ Ok(Self {
+ raw: out,
+ progress: Default::default(),
+ progress_payload_ptr,
+ })
+ }
+
+ /// Finalize the pack and index
+ ///
+ /// Resolves any pending deltas and writes out the index file. The returned
+ /// string is the hexadecimal checksum of the packfile, which is also used
+ /// to name the pack and index files (`pack-<checksum>.pack` and
+ /// `pack-<checksum>.idx` respectively).
+ pub fn commit(mut self) -> Result<String, Error> {
+ unsafe {
+ try_call!(raw::git_indexer_commit(self.raw, &mut self.progress));
+
+ let name = CStr::from_ptr(raw::git_indexer_name(self.raw));
+ Ok(name.to_str().expect("pack name not utf8").to_owned())
+ }
+ }
+
+ /// The callback through which progress is monitored. Be aware that this is
+ /// called inline, so performance may be affected.
+ pub fn progress<F>(&mut self, cb: F) -> &mut Self
+ where
+ F: FnMut(Progress<'_>) -> bool + 'a,
+ {
+ let progress_payload =
+ unsafe { &mut *(self.progress_payload_ptr as *mut OdbPackwriterCb<'_>) };
+ progress_payload.cb = Some(Box::new(cb) as Box<IndexerProgress<'a>>);
+
+ self
+ }
+}
+
+impl io::Write for Indexer<'_> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unsafe {
+ let ptr = buf.as_ptr() as *mut c_void;
+ let len = buf.len();
+
+ let res = raw::git_indexer_append(self.raw, ptr, len, &mut self.progress);
+ if res < 0 {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ Error::last_error(res).unwrap(),
+ ))
+ } else {
+ Ok(buf.len())
+ }
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Drop for Indexer<'_> {
+ fn drop(&mut self) {
+ unsafe {
+ raw::git_indexer_free(self.raw);
+ drop(Box::from_raw(self.progress_payload_ptr))
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{Buf, Indexer};
+ use std::io::prelude::*;
+
+ #[test]
+ fn indexer() {
+ let (_td, repo_source) = crate::test::repo_init();
+ let (_td, repo_target) = crate::test::repo_init();
+
+ let mut progress_called = false;
+
+ // Create an in-memory packfile
+ let mut builder = t!(repo_source.packbuilder());
+ let mut buf = Buf::new();
+ let (commit_source_id, _tree) = crate::test::commit(&repo_source);
+ t!(builder.insert_object(commit_source_id, None));
+ t!(builder.write_buf(&mut buf));
+
+ // Write it to the standard location in the target repo, but via indexer
+ let odb = repo_source.odb().unwrap();
+ let mut indexer = Indexer::new(
+ Some(&odb),
+ repo_target.path().join("objects").join("pack").as_path(),
+ 0o644,
+ true,
+ )
+ .unwrap();
+ indexer.progress(|_| {
+ progress_called = true;
+ true
+ });
+ indexer.write(&buf).unwrap();
+ indexer.commit().unwrap();
+
+ // Assert that target repo picks it up as valid
+ let commit_target = repo_target.find_commit(commit_source_id).unwrap();
+ assert_eq!(commit_target.id(), commit_source_id);
+ assert!(progress_called);
+ }
+}