summaryrefslogtreecommitdiffstats
path: root/vendor/git2/src/revwalk.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/git2/src/revwalk.rs')
-rw-r--r--vendor/git2/src/revwalk.rs316
1 files changed, 316 insertions, 0 deletions
diff --git a/vendor/git2/src/revwalk.rs b/vendor/git2/src/revwalk.rs
new file mode 100644
index 0000000..7837f00
--- /dev/null
+++ b/vendor/git2/src/revwalk.rs
@@ -0,0 +1,316 @@
+use libc::{c_int, c_uint, c_void};
+use std::ffi::CString;
+use std::marker;
+
+use crate::util::Binding;
+use crate::{panic, raw, Error, Oid, Repository, Sort};
+
+/// A revwalk allows traversal of the commit graph defined by including one or
+/// more leaves and excluding one or more roots.
+pub struct Revwalk<'repo> {
+ raw: *mut raw::git_revwalk,
+ _marker: marker::PhantomData<&'repo Repository>,
+}
+
+/// A `Revwalk` with an associated "hide callback", see `with_hide_callback`
+pub struct RevwalkWithHideCb<'repo, 'cb, C>
+where
+ C: FnMut(Oid) -> bool,
+{
+ revwalk: Revwalk<'repo>,
+ _marker: marker::PhantomData<&'cb C>,
+}
+
+extern "C" fn revwalk_hide_cb<C>(commit_id: *const raw::git_oid, payload: *mut c_void) -> c_int
+where
+ C: FnMut(Oid) -> bool,
+{
+ panic::wrap(|| unsafe {
+ let hide_cb = payload as *mut C;
+ if (*hide_cb)(Oid::from_raw(commit_id)) {
+ 1
+ } else {
+ 0
+ }
+ })
+ .unwrap_or(-1)
+}
+
+impl<'repo, 'cb, C: FnMut(Oid) -> bool> RevwalkWithHideCb<'repo, 'cb, C> {
+ /// Consumes the `RevwalkWithHideCb` and returns the contained `Revwalk`.
+ ///
+ /// Note that this will reset the `Revwalk`.
+ pub fn into_inner(mut self) -> Result<Revwalk<'repo>, Error> {
+ self.revwalk.reset()?;
+ Ok(self.revwalk)
+ }
+}
+
+impl<'repo> Revwalk<'repo> {
+ /// Reset a revwalk to allow re-configuring it.
+ ///
+ /// The revwalk is automatically reset when iteration of its commits
+ /// completes.
+ pub fn reset(&mut self) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_revwalk_reset(self.raw()));
+ }
+ Ok(())
+ }
+
+ /// Set the order in which commits are visited.
+ pub fn set_sorting(&mut self, sort_mode: Sort) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_revwalk_sorting(
+ self.raw(),
+ sort_mode.bits() as c_uint
+ ));
+ }
+ Ok(())
+ }
+
+ /// Simplify the history by first-parent
+ ///
+ /// No parents other than the first for each commit will be enqueued.
+ pub fn simplify_first_parent(&mut self) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_revwalk_simplify_first_parent(self.raw));
+ }
+ Ok(())
+ }
+
+ /// Mark a commit to start traversal from.
+ ///
+ /// The given OID must belong to a commitish on the walked repository.
+ ///
+ /// The given commit will be used as one of the roots when starting the
+ /// revision walk. At least one commit must be pushed onto the walker before
+ /// a walk can be started.
+ pub fn push(&mut self, oid: Oid) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_revwalk_push(self.raw(), oid.raw()));
+ }
+ Ok(())
+ }
+
+ /// Push the repository's HEAD
+ ///
+ /// For more information, see `push`.
+ pub fn push_head(&mut self) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_revwalk_push_head(self.raw()));
+ }
+ Ok(())
+ }
+
+ /// Push matching references
+ ///
+ /// The OIDs pointed to by the references that match the given glob pattern
+ /// will be pushed to the revision walker.
+ ///
+ /// A leading 'refs/' is implied if not present as well as a trailing `/ \
+ /// *` if the glob lacks '?', ' \ *' or '['.
+ ///
+ /// Any references matching this glob which do not point to a commitish
+ /// will be ignored.
+ pub fn push_glob(&mut self, glob: &str) -> Result<(), Error> {
+ let glob = CString::new(glob)?;
+ unsafe {
+ try_call!(raw::git_revwalk_push_glob(self.raw, glob));
+ }
+ Ok(())
+ }
+
+ /// Push and hide the respective endpoints of the given range.
+ ///
+ /// The range should be of the form `<commit>..<commit>` where each
+ /// `<commit>` is in the form accepted by `revparse_single`. The left-hand
+ /// commit will be hidden and the right-hand commit pushed.
+ pub fn push_range(&mut self, range: &str) -> Result<(), Error> {
+ let range = CString::new(range)?;
+ unsafe {
+ try_call!(raw::git_revwalk_push_range(self.raw, range));
+ }
+ Ok(())
+ }
+
+ /// Push the OID pointed to by a reference
+ ///
+ /// The reference must point to a commitish.
+ pub fn push_ref(&mut self, reference: &str) -> Result<(), Error> {
+ let reference = CString::new(reference)?;
+ unsafe {
+ try_call!(raw::git_revwalk_push_ref(self.raw, reference));
+ }
+ Ok(())
+ }
+
+ /// Mark a commit as not of interest to this revwalk.
+ pub fn hide(&mut self, oid: Oid) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_revwalk_hide(self.raw(), oid.raw()));
+ }
+ Ok(())
+ }
+
+ /// Hide all commits for which the callback returns true from
+ /// the walk.
+ pub fn with_hide_callback<'cb, C>(
+ self,
+ callback: &'cb mut C,
+ ) -> Result<RevwalkWithHideCb<'repo, 'cb, C>, Error>
+ where
+ C: FnMut(Oid) -> bool,
+ {
+ let r = RevwalkWithHideCb {
+ revwalk: self,
+ _marker: marker::PhantomData,
+ };
+ unsafe {
+ raw::git_revwalk_add_hide_cb(
+ r.revwalk.raw(),
+ Some(revwalk_hide_cb::<C>),
+ callback as *mut _ as *mut c_void,
+ );
+ };
+ Ok(r)
+ }
+
+ /// Hide the repository's HEAD
+ ///
+ /// For more information, see `hide`.
+ pub fn hide_head(&mut self) -> Result<(), Error> {
+ unsafe {
+ try_call!(raw::git_revwalk_hide_head(self.raw()));
+ }
+ Ok(())
+ }
+
+ /// Hide matching references.
+ ///
+ /// The OIDs pointed to by the references that match the given glob pattern
+ /// and their ancestors will be hidden from the output on the revision walk.
+ ///
+ /// A leading 'refs/' is implied if not present as well as a trailing `/ \
+ /// *` if the glob lacks '?', ' \ *' or '['.
+ ///
+ /// Any references matching this glob which do not point to a commitish
+ /// will be ignored.
+ pub fn hide_glob(&mut self, glob: &str) -> Result<(), Error> {
+ let glob = CString::new(glob)?;
+ unsafe {
+ try_call!(raw::git_revwalk_hide_glob(self.raw, glob));
+ }
+ Ok(())
+ }
+
+ /// Hide the OID pointed to by a reference.
+ ///
+ /// The reference must point to a commitish.
+ pub fn hide_ref(&mut self, reference: &str) -> Result<(), Error> {
+ let reference = CString::new(reference)?;
+ unsafe {
+ try_call!(raw::git_revwalk_hide_ref(self.raw, reference));
+ }
+ Ok(())
+ }
+}
+
+impl<'repo> Binding for Revwalk<'repo> {
+ type Raw = *mut raw::git_revwalk;
+ unsafe fn from_raw(raw: *mut raw::git_revwalk) -> Revwalk<'repo> {
+ Revwalk {
+ raw,
+ _marker: marker::PhantomData,
+ }
+ }
+ fn raw(&self) -> *mut raw::git_revwalk {
+ self.raw
+ }
+}
+
+impl<'repo> Drop for Revwalk<'repo> {
+ fn drop(&mut self) {
+ unsafe { raw::git_revwalk_free(self.raw) }
+ }
+}
+
+impl<'repo> Iterator for Revwalk<'repo> {
+ type Item = Result<Oid, Error>;
+ fn next(&mut self) -> Option<Result<Oid, Error>> {
+ let mut out: raw::git_oid = raw::git_oid {
+ id: [0; raw::GIT_OID_RAWSZ],
+ };
+ unsafe {
+ try_call_iter!(raw::git_revwalk_next(&mut out, self.raw()));
+ Some(Ok(Binding::from_raw(&out as *const _)))
+ }
+ }
+}
+
+impl<'repo, 'cb, C: FnMut(Oid) -> bool> Iterator for RevwalkWithHideCb<'repo, 'cb, C> {
+ type Item = Result<Oid, Error>;
+ fn next(&mut self) -> Option<Result<Oid, Error>> {
+ let out = self.revwalk.next();
+ crate::panic::check();
+ out
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn smoke() {
+ let (_td, repo) = crate::test::repo_init();
+ let head = repo.head().unwrap();
+ let target = head.target().unwrap();
+
+ let mut walk = repo.revwalk().unwrap();
+ walk.push(target).unwrap();
+
+ let oids: Vec<crate::Oid> = walk.by_ref().collect::<Result<Vec<_>, _>>().unwrap();
+
+ assert_eq!(oids.len(), 1);
+ assert_eq!(oids[0], target);
+
+ walk.reset().unwrap();
+ walk.push_head().unwrap();
+ assert_eq!(walk.by_ref().count(), 1);
+
+ walk.reset().unwrap();
+ walk.push_head().unwrap();
+ walk.hide_head().unwrap();
+ assert_eq!(walk.by_ref().count(), 0);
+ }
+
+ #[test]
+ fn smoke_hide_cb() {
+ let (_td, repo) = crate::test::repo_init();
+ let head = repo.head().unwrap();
+ let target = head.target().unwrap();
+
+ let mut walk = repo.revwalk().unwrap();
+ walk.push(target).unwrap();
+
+ let oids: Vec<crate::Oid> = walk.by_ref().collect::<Result<Vec<_>, _>>().unwrap();
+
+ assert_eq!(oids.len(), 1);
+ assert_eq!(oids[0], target);
+
+ walk.reset().unwrap();
+ walk.push_head().unwrap();
+ assert_eq!(walk.by_ref().count(), 1);
+
+ walk.reset().unwrap();
+ walk.push_head().unwrap();
+
+ let mut hide_cb = |oid| oid == target;
+ let mut walk = walk.with_hide_callback(&mut hide_cb).unwrap();
+
+ assert_eq!(walk.by_ref().count(), 0);
+
+ let mut walk = walk.into_inner().unwrap();
+ walk.push_head().unwrap();
+ assert_eq!(walk.by_ref().count(), 1);
+ }
+}