8 use {raw, signature, Oid, Error, Signature, Tree, Time, Object}
;
11 /// A structure to represent a git [commit][1]
13 /// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
14 pub struct Commit
<'repo
> {
15 raw
: *mut raw
::git_commit
,
16 _marker
: marker
::PhantomData
<Object
<'repo
>>,
19 /// An iterator over the parent commits of a commit.
20 pub struct Parents
<'commit
, 'repo
: 'commit
> {
22 commit
: &'commit Commit
<'repo
>,
25 /// An iterator over the parent commits' ids of a commit.
26 pub struct ParentIds
<'commit
> {
28 commit
: &'commit Commit
<'commit
>,
31 impl<'repo
> Commit
<'repo
> {
32 /// Get the id (SHA1) of a repository commit
33 pub fn id(&self) -> Oid
{
34 unsafe { Binding::from_raw(raw::git_commit_id(&*self.raw)) }
37 /// Get the id of the tree pointed to by this commit.
39 /// No attempts are made to fetch an object from the ODB.
40 pub fn tree_id(&self) -> Oid
{
41 unsafe { Binding::from_raw(raw::git_commit_tree_id(&*self.raw)) }
44 /// Get the tree pointed to by a commit.
45 pub fn tree(&self) -> Result
<Tree
<'repo
>, Error
> {
46 let mut ret
= ptr
::null_mut();
48 try_call
!(raw
::git_commit_tree(&mut ret
, &*self.raw
));
49 Ok(Binding
::from_raw(ret
))
53 /// Get access to the underlying raw pointer.
54 pub fn raw(&self) -> *mut raw
::git_commit { self.raw }
56 /// Get the full message of a commit.
58 /// The returned message will be slightly prettified by removing any
59 /// potential leading newlines.
61 /// `None` will be returned if the message is not valid utf-8
62 pub fn message(&self) -> Option
<&str> {
63 str::from_utf8(self.message_bytes()).ok()
66 /// Get the full message of a commit as a byte slice.
68 /// The returned message will be slightly prettified by removing any
69 /// potential leading newlines.
70 pub fn message_bytes(&self) -> &[u8] {
72 ::opt_bytes(self, raw
::git_commit_message(&*self.raw
)).unwrap()
76 /// Get the encoding for the message of a commit, as a string representing a
77 /// standard encoding name.
79 /// `None` will be returned if the encoding is not known
80 pub fn message_encoding(&self) -> Option
<&str> {
82 ::opt_bytes(self, raw
::git_commit_message(&*self.raw
))
84 bytes
.map(|b
| str::from_utf8(b
).unwrap())
87 /// Get the full raw message of a commit.
89 /// `None` will be returned if the message is not valid utf-8
90 pub fn message_raw(&self) -> Option
<&str> {
91 str::from_utf8(self.message_raw_bytes()).ok()
94 /// Get the full raw message of a commit.
95 pub fn message_raw_bytes(&self) -> &[u8] {
97 ::opt_bytes(self, raw
::git_commit_message_raw(&*self.raw
)).unwrap()
101 /// Get the full raw text of the commit header.
103 /// `None` will be returned if the message is not valid utf-8
104 pub fn raw_header(&self) -> Option
<&str> {
105 str::from_utf8(self.raw_header_bytes()).ok()
108 /// Get the full raw text of the commit header.
109 pub fn raw_header_bytes(&self) -> &[u8] {
111 ::opt_bytes(self, raw
::git_commit_raw_header(&*self.raw
)).unwrap()
115 /// Get the short "summary" of the git commit message.
117 /// The returned message is the summary of the commit, comprising the first
118 /// paragraph of the message with whitespace trimmed and squashed.
120 /// `None` may be returned if an error occurs or if the summary is not valid
122 pub fn summary(&mut self) -> Option
<&str> {
123 self.summary_bytes().and_then(|s
| str::from_utf8(s
).ok())
126 /// Get the short "summary" of the git commit message.
128 /// The returned message is the summary of the commit, comprising the first
129 /// paragraph of the message with whitespace trimmed and squashed.
131 /// `None` may be returned if an error occurs
132 pub fn summary_bytes(&mut self) -> Option
<&[u8]> {
133 unsafe { ::opt_bytes(self, raw::git_commit_summary(self.raw)) }
136 /// Get the commit time (i.e. committer time) of a commit.
138 /// The first element of the tuple is the time, in seconds, since the epoch.
139 /// The second element is the offset, in minutes, of the time zone of the
140 /// committer's preferred time zone.
141 pub fn time(&self) -> Time
{
143 Time
::new(raw
::git_commit_time(&*self.raw
) as i64,
144 raw
::git_commit_time_offset(&*self.raw
) as i32)
148 /// Creates a new iterator over the parents of this commit.
149 pub fn parents
<'a
>(&'a
self) -> Parents
<'a
, 'repo
> {
150 let max
= unsafe { raw::git_commit_parentcount(&*self.raw) as usize }
;
151 Parents { range: 0..max, commit: self }
154 /// Creates a new iterator over the parents of this commit.
155 pub fn parent_ids(&self) -> ParentIds
{
156 let max
= unsafe { raw::git_commit_parentcount(&*self.raw) as usize }
;
157 ParentIds { range: 0..max, commit: self }
160 /// Get the author of this commit.
161 pub fn author(&self) -> Signature
{
163 let ptr
= raw
::git_commit_author(&*self.raw
);
164 signature
::from_raw_const(self, ptr
)
168 /// Get the committer of this commit.
169 pub fn committer(&self) -> Signature
{
171 let ptr
= raw
::git_commit_committer(&*self.raw
);
172 signature
::from_raw_const(self, ptr
)
176 /// Amend this existing commit with all non-`None` values
178 /// This creates a new commit that is exactly the same as the old commit,
179 /// except that any non-`None` values will be updated. The new commit has
180 /// the same parents as the old commit.
182 /// For information about `update_ref`, see [`Repository::commit`].
184 /// [`Repository::commit`]: struct.Repository.html#method.commit
186 update_ref
: Option
<&str>,
187 author
: Option
<&Signature
>,
188 committer
: Option
<&Signature
>,
189 message_encoding
: Option
<&str>,
190 message
: Option
<&str>,
191 tree
: Option
<&Tree
<'repo
>>) -> Result
<Oid
, Error
> {
192 let mut raw
= raw
::git_oid { id: [0; raw::GIT_OID_RAWSZ] }
;
193 let update_ref
= try
!(::opt_cstr(update_ref
));
194 let encoding
= try
!(::opt_cstr(message_encoding
));
195 let message
= try
!(::opt_cstr(message
));
197 try_call
!(raw
::git_commit_amend(&mut raw
,
200 author
.map(|s
| s
.raw()),
201 committer
.map(|s
| s
.raw()),
204 tree
.map(|t
| t
.raw())));
205 Ok(Binding
::from_raw(&raw
as *const _
))
209 /// Get the specified parent of the commit.
211 /// Use the `parents` iterator to return an iterator over all parents.
212 pub fn parent(&self, i
: usize) -> Result
<Commit
<'repo
>, Error
> {
214 let mut raw
= ptr
::null_mut();
215 try_call
!(raw
::git_commit_parent(&mut raw
, &*self.raw
,
217 Ok(Binding
::from_raw(raw
))
221 /// Get the specified parent id of the commit.
223 /// This is different from `parent`, which will attemptstempt to load the
224 /// parent commit from the ODB.
226 /// Use the `parent_ids` iterator to return an iterator over all parents.
227 pub fn parent_id(&self, i
: usize) -> Result
<Oid
, Error
> {
229 let id
= raw
::git_commit_parent_id(self.raw
, i
as libc
::c_uint
);
231 Err(Error
::from_str("parent index out of bounds"))
233 Ok(Binding
::from_raw(id
))
238 /// Casts this Commit to be usable as an `Object`
239 pub fn as_object(&self) -> &Object
<'repo
> {
241 &*(self as *const _
as *const Object
<'repo
>)
245 /// Consumes Commit to be returned as an `Object`
246 pub fn into_object(self) -> Object
<'repo
> {
247 assert_eq
!(mem
::size_of_val(&self), mem
::size_of
::<Object
>());
254 impl<'repo
> Binding
for Commit
<'repo
> {
255 type Raw
= *mut raw
::git_commit
;
256 unsafe fn from_raw(raw
: *mut raw
::git_commit
) -> Commit
<'repo
> {
259 _marker
: marker
::PhantomData
,
262 fn raw(&self) -> *mut raw
::git_commit { self.raw }
266 impl<'repo
, 'commit
> Iterator
for Parents
<'commit
, 'repo
> {
267 type Item
= Commit
<'repo
>;
268 fn next(&mut self) -> Option
<Commit
<'repo
>> {
269 self.range
.next().map(|i
| self.commit
.parent(i
).unwrap())
271 fn size_hint(&self) -> (usize, Option
<usize>) { self.range.size_hint() }
274 impl<'repo
, 'commit
> DoubleEndedIterator
for Parents
<'commit
, 'repo
> {
275 fn next_back(&mut self) -> Option
<Commit
<'repo
>> {
276 self.range
.next_back().map(|i
| self.commit
.parent(i
).unwrap())
280 impl<'repo
, 'commit
> ExactSizeIterator
for Parents
<'commit
, 'repo
> {}
282 impl<'commit
> Iterator
for ParentIds
<'commit
> {
284 fn next(&mut self) -> Option
<Oid
> {
285 self.range
.next().map(|i
| self.commit
.parent_id(i
).unwrap())
287 fn size_hint(&self) -> (usize, Option
<usize>) { self.range.size_hint() }
290 impl<'commit
> DoubleEndedIterator
for ParentIds
<'commit
> {
291 fn next_back(&mut self) -> Option
<Oid
> {
292 self.range
.next_back().map(|i
| self.commit
.parent_id(i
).unwrap())
296 impl<'commit
> ExactSizeIterator
for ParentIds
<'commit
> {}
298 impl<'repo
> Drop
for Commit
<'repo
> {
300 unsafe { raw::git_commit_free(self.raw) }
308 let (_td
, repo
) = ::test
::repo_init();
309 let head
= repo
.head().unwrap();
310 let target
= head
.target().unwrap();
311 let mut commit
= repo
.find_commit(target
).unwrap();
312 assert_eq
!(commit
.message(), Some("initial"));
313 assert_eq
!(commit
.id(), target
);
314 commit
.message_raw().unwrap();
315 commit
.raw_header().unwrap();
316 commit
.message_encoding();
317 commit
.summary().unwrap();
319 commit
.tree().unwrap();
320 assert_eq
!(commit
.parents().count(), 0);
322 assert_eq
!(commit
.author().name(), Some("name"));
323 assert_eq
!(commit
.author().email(), Some("email"));
324 assert_eq
!(commit
.committer().name(), Some("name"));
325 assert_eq
!(commit
.committer().email(), Some("email"));
327 let sig
= repo
.signature().unwrap();
328 let tree
= repo
.find_tree(commit
.tree_id()).unwrap();
329 let id
= repo
.commit(Some("HEAD"), &sig
, &sig
, "bar", &tree
,
330 &[&commit
]).unwrap();
331 let head
= repo
.find_commit(id
).unwrap();
333 let new_head
= head
.amend(Some("HEAD"), None
, None
, None
,
334 Some("new message"), None
).unwrap();
335 let new_head
= repo
.find_commit(new_head
).unwrap();
336 assert_eq
!(new_head
.message(), Some("new message"));
337 new_head
.into_object();
339 repo
.find_object(target
, None
).unwrap().as_commit().unwrap();
340 repo
.find_object(target
, None
).unwrap().into_commit().ok().unwrap();