]> git.proxmox.com Git - cargo.git/blobdiff - vendor/git2/src/commit.rs
New upstream version 0.66.0
[cargo.git] / vendor / git2 / src / commit.rs
index f6086309c8df2b4c305c1a3ca3b73f9eccb9d6af..c6e2bd10e538b8717b07da15991aef3b84200520 100644 (file)
@@ -1,12 +1,12 @@
+use libc;
 use std::marker;
 use std::mem;
 use std::ops::Range;
 use std::ptr;
 use std::str;
-use libc;
 
-use {raw, signature, Oid, Error, Signature, Tree, Time, Object};
-use util::Binding;
+use crate::util::Binding;
+use crate::{raw, signature, Buf, Error, IntoCString, Mailmap, Object, Oid, Signature, Time, Tree};
 
 /// A structure to represent a git [commit][1]
 ///
@@ -17,12 +17,16 @@ pub struct Commit<'repo> {
 }
 
 /// An iterator over the parent commits of a commit.
-pub struct Parents<'commit, 'repo: 'commit> {
+///
+/// Aborts iteration when a commit cannot be found
+pub struct Parents<'commit, 'repo> {
     range: Range<usize>,
     commit: &'commit Commit<'repo>,
 }
 
 /// An iterator over the parent commits' ids of a commit.
+///
+/// Aborts iteration when a commit cannot be found
 pub struct ParentIds<'commit> {
     range: Range<usize>,
     commit: &'commit Commit<'commit>,
@@ -51,7 +55,9 @@ impl<'repo> Commit<'repo> {
     }
 
     /// Get access to the underlying raw pointer.
-    pub fn raw(&self) -> *mut raw::git_commit { self.raw }
+    pub fn raw(&self) -> *mut raw::git_commit {
+        self.raw
+    }
 
     /// Get the full message of a commit.
     ///
@@ -68,9 +74,7 @@ impl<'repo> Commit<'repo> {
     /// The returned message will be slightly prettified by removing any
     /// potential leading newlines.
     pub fn message_bytes(&self) -> &[u8] {
-        unsafe {
-            ::opt_bytes(self, raw::git_commit_message(&*self.raw)).unwrap()
-        }
+        unsafe { crate::opt_bytes(self, raw::git_commit_message(&*self.raw)).unwrap() }
     }
 
     /// Get the encoding for the message of a commit, as a string representing a
@@ -78,10 +82,8 @@ impl<'repo> Commit<'repo> {
     ///
     /// `None` will be returned if the encoding is not known
     pub fn message_encoding(&self) -> Option<&str> {
-        let bytes = unsafe {
-            ::opt_bytes(self, raw::git_commit_message(&*self.raw))
-        };
-        bytes.map(|b| str::from_utf8(b).unwrap())
+        let bytes = unsafe { crate::opt_bytes(self, raw::git_commit_message_encoding(&*self.raw)) };
+        bytes.and_then(|b| str::from_utf8(b).ok())
     }
 
     /// Get the full raw message of a commit.
@@ -93,9 +95,7 @@ impl<'repo> Commit<'repo> {
 
     /// Get the full raw message of a commit.
     pub fn message_raw_bytes(&self) -> &[u8] {
-        unsafe {
-            ::opt_bytes(self, raw::git_commit_message_raw(&*self.raw)).unwrap()
-        }
+        unsafe { crate::opt_bytes(self, raw::git_commit_message_raw(&*self.raw)).unwrap() }
     }
 
     /// Get the full raw text of the commit header.
@@ -105,11 +105,23 @@ impl<'repo> Commit<'repo> {
         str::from_utf8(self.raw_header_bytes()).ok()
     }
 
-    /// Get the full raw text of the commit header.
-    pub fn raw_header_bytes(&self) -> &[u8] {
+    /// Get an arbitrary header field.
+    pub fn header_field_bytes<T: IntoCString>(&self, field: T) -> Result<Buf, Error> {
+        let buf = Buf::new();
+        let raw_field = field.into_c_string()?;
         unsafe {
-            ::opt_bytes(self, raw::git_commit_raw_header(&*self.raw)).unwrap()
+            try_call!(raw::git_commit_header_field(
+                buf.raw(),
+                &*self.raw,
+                raw_field
+            ));
         }
+        Ok(buf)
+    }
+
+    /// Get the full raw text of the commit header.
+    pub fn raw_header_bytes(&self) -> &[u8] {
+        unsafe { crate::opt_bytes(self, raw::git_commit_raw_header(&*self.raw)).unwrap() }
     }
 
     /// Get the short "summary" of the git commit message.
@@ -130,7 +142,30 @@ impl<'repo> Commit<'repo> {
     ///
     /// `None` may be returned if an error occurs
     pub fn summary_bytes(&self) -> Option<&[u8]> {
-        unsafe { ::opt_bytes(self, raw::git_commit_summary(self.raw)) }
+        unsafe { crate::opt_bytes(self, raw::git_commit_summary(self.raw)) }
+    }
+
+    /// Get the long "body" of the git commit message.
+    ///
+    /// The returned message is the body of the commit, comprising everything
+    /// but the first paragraph of the message. Leading and trailing whitespaces
+    /// are trimmed.
+    ///
+    /// `None` may be returned if an error occurs or if the summary is not valid
+    /// utf-8.
+    pub fn body(&self) -> Option<&str> {
+        self.body_bytes().and_then(|s| str::from_utf8(s).ok())
+    }
+
+    /// Get the long "body" of the git commit message.
+    ///
+    /// The returned message is the body of the commit, comprising everything
+    /// but the first paragraph of the message. Leading and trailing whitespaces
+    /// are trimmed.
+    ///
+    /// `None` may be returned if an error occurs.
+    pub fn body_bytes(&self) -> Option<&[u8]> {
+        unsafe { crate::opt_bytes(self, raw::git_commit_body(self.raw)) }
     }
 
     /// Get the commit time (i.e. committer time) of a commit.
@@ -140,39 +175,73 @@ impl<'repo> Commit<'repo> {
     /// committer's preferred time zone.
     pub fn time(&self) -> Time {
         unsafe {
-            Time::new(raw::git_commit_time(&*self.raw) as i64,
-                      raw::git_commit_time_offset(&*self.raw) as i32)
+            Time::new(
+                raw::git_commit_time(&*self.raw) as i64,
+                raw::git_commit_time_offset(&*self.raw) as i32,
+            )
         }
     }
 
     /// Creates a new iterator over the parents of this commit.
     pub fn parents<'a>(&'a self) -> Parents<'a, 'repo> {
-        let max = unsafe { raw::git_commit_parentcount(&*self.raw) as usize };
-        Parents { range: 0..max, commit: self }
+        Parents {
+            range: 0..self.parent_count(),
+            commit: self,
+        }
     }
 
     /// Creates a new iterator over the parents of this commit.
-    pub fn parent_ids(&self) -> ParentIds {
-        let max = unsafe { raw::git_commit_parentcount(&*self.raw) as usize };
-        ParentIds { range: 0..max, commit: self }
+    pub fn parent_ids(&self) -> ParentIds<'_> {
+        ParentIds {
+            range: 0..self.parent_count(),
+            commit: self,
+        }
     }
 
     /// Get the author of this commit.
-    pub fn author(&self) -> Signature {
+    pub fn author(&self) -> Signature<'_> {
         unsafe {
             let ptr = raw::git_commit_author(&*self.raw);
             signature::from_raw_const(self, ptr)
         }
     }
 
+    /// Get the author of this commit, using the mailmap to map names and email
+    /// addresses to canonical real names and email addresses.
+    pub fn author_with_mailmap(&self, mailmap: &Mailmap) -> Result<Signature<'static>, Error> {
+        let mut ret = ptr::null_mut();
+        unsafe {
+            try_call!(raw::git_commit_author_with_mailmap(
+                &mut ret,
+                &*self.raw,
+                &*mailmap.raw()
+            ));
+            Ok(Binding::from_raw(ret))
+        }
+    }
+
     /// Get the committer of this commit.
-    pub fn committer(&self) -> Signature {
+    pub fn committer(&self) -> Signature<'_> {
         unsafe {
             let ptr = raw::git_commit_committer(&*self.raw);
             signature::from_raw_const(self, ptr)
         }
     }
 
+    /// Get the committer of this commit, using the mailmap to map names and email
+    /// addresses to canonical real names and email addresses.
+    pub fn committer_with_mailmap(&self, mailmap: &Mailmap) -> Result<Signature<'static>, Error> {
+        let mut ret = ptr::null_mut();
+        unsafe {
+            try_call!(raw::git_commit_committer_with_mailmap(
+                &mut ret,
+                &*self.raw,
+                &*mailmap.raw()
+            ));
+            Ok(Binding::from_raw(ret))
+        }
+    }
+
     /// Amend this existing commit with all non-`None` values
     ///
     /// This creates a new commit that is exactly the same as the old commit,
@@ -182,38 +251,54 @@ impl<'repo> Commit<'repo> {
     /// For information about `update_ref`, see [`Repository::commit`].
     ///
     /// [`Repository::commit`]: struct.Repository.html#method.commit
-    pub fn amend(&self,
-                 update_ref: Option<&str>,
-                 author: Option<&Signature>,
-                 committer: Option<&Signature>,
-                 message_encoding: Option<&str>,
-                 message: Option<&str>,
-                 tree: Option<&Tree<'repo>>) -> Result<Oid, Error> {
-        let mut raw = raw::git_oid { id: [0; raw::GIT_OID_RAWSZ] };
-        let update_ref = try!(::opt_cstr(update_ref));
-        let encoding = try!(::opt_cstr(message_encoding));
-        let message = try!(::opt_cstr(message));
+    pub fn amend(
+        &self,
+        update_ref: Option<&str>,
+        author: Option<&Signature<'_>>,
+        committer: Option<&Signature<'_>>,
+        message_encoding: Option<&str>,
+        message: Option<&str>,
+        tree: Option<&Tree<'repo>>,
+    ) -> Result<Oid, Error> {
+        let mut raw = raw::git_oid {
+            id: [0; raw::GIT_OID_RAWSZ],
+        };
+        let update_ref = crate::opt_cstr(update_ref)?;
+        let encoding = crate::opt_cstr(message_encoding)?;
+        let message = crate::opt_cstr(message)?;
         unsafe {
-            try_call!(raw::git_commit_amend(&mut raw,
-                                            self.raw(),
-                                            update_ref,
-                                            author.map(|s| s.raw()),
-                                            committer.map(|s| s.raw()),
-                                            encoding,
-                                            message,
-                                            tree.map(|t| t.raw())));
+            try_call!(raw::git_commit_amend(
+                &mut raw,
+                self.raw(),
+                update_ref,
+                author.map(|s| s.raw()),
+                committer.map(|s| s.raw()),
+                encoding,
+                message,
+                tree.map(|t| t.raw())
+            ));
             Ok(Binding::from_raw(&raw as *const _))
         }
     }
 
+    /// Get the number of parents of this commit.
+    ///
+    /// Use the `parents` iterator to return an iterator over all parents.
+    pub fn parent_count(&self) -> usize {
+        unsafe { raw::git_commit_parentcount(&*self.raw) as usize }
+    }
+
     /// Get the specified parent of the commit.
     ///
     /// Use the `parents` iterator to return an iterator over all parents.
     pub fn parent(&self, i: usize) -> Result<Commit<'repo>, Error> {
         unsafe {
             let mut raw = ptr::null_mut();
-            try_call!(raw::git_commit_parent(&mut raw, &*self.raw,
-                                             i as libc::c_uint));
+            try_call!(raw::git_commit_parent(
+                &mut raw,
+                &*self.raw,
+                i as libc::c_uint
+            ));
             Ok(Binding::from_raw(raw))
         }
     }
@@ -237,17 +322,13 @@ impl<'repo> Commit<'repo> {
 
     /// Casts this Commit to be usable as an `Object`
     pub fn as_object(&self) -> &Object<'repo> {
-        unsafe {
-            &*(self as *const _ as *const Object<'repo>)
-        }
+        unsafe { &*(self as *const _ as *const Object<'repo>) }
     }
 
     /// Consumes Commit to be returned as an `Object`
     pub fn into_object(self) -> Object<'repo> {
-        assert_eq!(mem::size_of_val(&self), mem::size_of::<Object>());
-        unsafe {
-            mem::transmute(self)
-        }
+        assert_eq!(mem::size_of_val(&self), mem::size_of::<Object<'_>>());
+        unsafe { mem::transmute(self) }
     }
 }
 
@@ -255,51 +336,67 @@ impl<'repo> Binding for Commit<'repo> {
     type Raw = *mut raw::git_commit;
     unsafe fn from_raw(raw: *mut raw::git_commit) -> Commit<'repo> {
         Commit {
-            raw: raw,
+            raw,
             _marker: marker::PhantomData,
         }
     }
-    fn raw(&self) -> *mut raw::git_commit { self.raw }
+    fn raw(&self) -> *mut raw::git_commit {
+        self.raw
+    }
 }
 
-impl<'repo> ::std::fmt::Debug for Commit<'repo> {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
-               let mut ds = f.debug_struct("Commit");
-               ds.field("id", &self.id());
-               if let Some(summary) = self.summary() {
-                       ds.field("summary", &summary);
-               }
-               ds.finish()
+impl<'repo> std::fmt::Debug for Commit<'repo> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        let mut ds = f.debug_struct("Commit");
+        ds.field("id", &self.id());
+        if let Some(summary) = self.summary() {
+            ds.field("summary", &summary);
+        }
+        ds.finish()
     }
 }
 
+/// Aborts iteration when a commit cannot be found
 impl<'repo, 'commit> Iterator for Parents<'commit, 'repo> {
     type Item = Commit<'repo>;
     fn next(&mut self) -> Option<Commit<'repo>> {
-        self.range.next().map(|i| self.commit.parent(i).unwrap())
+        self.range.next().and_then(|i| self.commit.parent(i).ok())
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.range.size_hint()
     }
-    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
 }
 
+/// Aborts iteration when a commit cannot be found
 impl<'repo, 'commit> DoubleEndedIterator for Parents<'commit, 'repo> {
     fn next_back(&mut self) -> Option<Commit<'repo>> {
-        self.range.next_back().map(|i| self.commit.parent(i).unwrap())
+        self.range
+            .next_back()
+            .and_then(|i| self.commit.parent(i).ok())
     }
 }
 
 impl<'repo, 'commit> ExactSizeIterator for Parents<'commit, 'repo> {}
 
+/// Aborts iteration when a commit cannot be found
 impl<'commit> Iterator for ParentIds<'commit> {
     type Item = Oid;
     fn next(&mut self) -> Option<Oid> {
-        self.range.next().map(|i| self.commit.parent_id(i).unwrap())
+        self.range
+            .next()
+            .and_then(|i| self.commit.parent_id(i).ok())
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.range.size_hint()
     }
-    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
 }
 
+/// Aborts iteration when a commit cannot be found
 impl<'commit> DoubleEndedIterator for ParentIds<'commit> {
     fn next_back(&mut self) -> Option<Oid> {
-        self.range.next_back().map(|i| self.commit.parent_id(i).unwrap())
+        self.range
+            .next_back()
+            .and_then(|i| self.commit.parent_id(i).ok())
     }
 }
 
@@ -321,20 +418,27 @@ impl<'repo> Drop for Commit<'repo> {
 mod tests {
     #[test]
     fn smoke() {
-        let (_td, repo) = ::test::repo_init();
+        let (_td, repo) = crate::test::repo_init();
         let head = repo.head().unwrap();
         let target = head.target().unwrap();
         let commit = repo.find_commit(target).unwrap();
-        assert_eq!(commit.message(), Some("initial"));
+        assert_eq!(commit.message(), Some("initial\n\nbody"));
+        assert_eq!(commit.body(), Some("body"));
         assert_eq!(commit.id(), target);
         commit.message_raw().unwrap();
         commit.raw_header().unwrap();
         commit.message_encoding();
         commit.summary().unwrap();
+        commit.body().unwrap();
         commit.tree_id();
         commit.tree().unwrap();
         assert_eq!(commit.parents().count(), 0);
 
+        let tree_header_bytes = commit.header_field_bytes("tree").unwrap();
+        assert_eq!(
+            crate::Oid::from_str(tree_header_bytes.as_str().unwrap()).unwrap(),
+            commit.tree_id()
+        );
         assert_eq!(commit.author().name(), Some("name"));
         assert_eq!(commit.author().email(), Some("email"));
         assert_eq!(commit.committer().name(), Some("name"));
@@ -342,18 +446,23 @@ mod tests {
 
         let sig = repo.signature().unwrap();
         let tree = repo.find_tree(commit.tree_id()).unwrap();
-        let id = repo.commit(Some("HEAD"), &sig, &sig, "bar", &tree,
-                             &[&commit]).unwrap();
+        let id = repo
+            .commit(Some("HEAD"), &sig, &sig, "bar", &tree, &[&commit])
+            .unwrap();
         let head = repo.find_commit(id).unwrap();
 
-        let new_head = head.amend(Some("HEAD"), None, None, None,
-                                  Some("new message"), None).unwrap();
+        let new_head = head
+            .amend(Some("HEAD"), None, None, None, Some("new message"), None)
+            .unwrap();
         let new_head = repo.find_commit(new_head).unwrap();
         assert_eq!(new_head.message(), Some("new message"));
         new_head.into_object();
 
         repo.find_object(target, None).unwrap().as_commit().unwrap();
-        repo.find_object(target, None).unwrap().into_commit().ok().unwrap();
+        repo.find_object(target, None)
+            .unwrap()
+            .into_commit()
+            .ok()
+            .unwrap();
     }
 }
-