+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]
///
}
/// 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>,
}
/// 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.
///
/// 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
///
/// `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.
/// 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.
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.
///
/// `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.
/// 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,
/// 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))
}
}
/// 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) }
}
}
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())
}
}
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"));
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();
}
}
-