2 use std
::cmp
::Ordering
;
11 use {raw, Oid, Repository, Error, Object, ObjectType}
;
12 use util
::{Binding, IntoCString}
;
14 /// A structure to represent a git [tree][1]
16 /// [1]: http://git-scm.com/book/en/Git-Internals-Git-Objects
17 pub struct Tree
<'repo
> {
18 raw
: *mut raw
::git_tree
,
19 _marker
: marker
::PhantomData
<Object
<'repo
>>,
22 /// A structure representing an entry inside of a tree. An entry is borrowed
24 pub struct TreeEntry
<'tree
> {
25 raw
: *mut raw
::git_tree_entry
,
27 _marker
: marker
::PhantomData
<&'tree raw
::git_tree_entry
>,
30 /// An iterator over the entries in a tree.
31 pub struct TreeIter
<'tree
> {
33 tree
: &'tree Tree
<'tree
>,
36 impl<'repo
> Tree
<'repo
> {
37 /// Get the id (SHA1) of a repository object
38 pub fn id(&self) -> Oid
{
39 unsafe { Binding::from_raw(raw::git_tree_id(&*self.raw)) }
42 /// Get the number of entries listed in this tree.
43 pub fn len(&self) -> usize {
44 unsafe { raw::git_tree_entrycount(&*self.raw) as usize }
47 /// Return `true` if there is not entry
48 pub fn is_empty(&self) -> bool
{
52 /// Returns an iterator over the entries in this tree.
53 pub fn iter(&self) -> TreeIter
{
54 TreeIter { range: 0..self.len(), tree: self }
57 /// Lookup a tree entry by SHA value.
58 pub fn get_id(&self, id
: Oid
) -> Option
<TreeEntry
> {
60 let ptr
= raw
::git_tree_entry_byid(&*self.raw(), &*id
.raw());
64 Some(entry_from_raw_const(ptr
))
69 /// Lookup a tree entry by its position in the tree
70 pub fn get(&self, n
: usize) -> Option
<TreeEntry
> {
72 let ptr
= raw
::git_tree_entry_byindex(&*self.raw(),
77 Some(entry_from_raw_const(ptr
))
82 /// Lookup a tree entry by its filename
83 pub fn get_name(&self, filename
: &str) -> Option
<TreeEntry
> {
84 let filename
= CString
::new(filename
).unwrap();
86 let ptr
= call
!(raw
::git_tree_entry_byname(&*self.raw(), filename
));
90 Some(entry_from_raw_const(ptr
))
95 /// Retrieve a tree entry contained in a tree or in any of its subtrees,
96 /// given its relative path.
97 pub fn get_path(&self, path
: &Path
) -> Result
<TreeEntry
<'
static>, Error
> {
98 let path
= try
!(path
.into_c_string());
99 let mut ret
= ptr
::null_mut();
101 try_call
!(raw
::git_tree_entry_bypath(&mut ret
, &*self.raw(), path
));
102 Ok(Binding
::from_raw(ret
))
106 /// Casts this Tree to be usable as an `Object`
107 pub fn as_object(&self) -> &Object
<'repo
> {
109 &*(self as *const _
as *const Object
<'repo
>)
113 /// Consumes Commit to be returned as an `Object`
114 pub fn into_object(self) -> Object
<'repo
> {
115 assert_eq
!(mem
::size_of_val(&self), mem
::size_of
::<Object
>());
122 impl<'repo
> Binding
for Tree
<'repo
> {
123 type Raw
= *mut raw
::git_tree
;
125 unsafe fn from_raw(raw
: *mut raw
::git_tree
) -> Tree
<'repo
> {
126 Tree { raw: raw, _marker: marker::PhantomData }
128 fn raw(&self) -> *mut raw
::git_tree { self.raw }
131 impl<'repo
> Drop
for Tree
<'repo
> {
133 unsafe { raw::git_tree_free(self.raw) }
137 impl<'repo
, 'iter
> IntoIterator
for &'iter Tree
<'repo
> {
138 type Item
= TreeEntry
<'iter
>;
139 type IntoIter
= TreeIter
<'iter
>;
140 fn into_iter(self) -> Self::IntoIter
{
145 /// Create a new tree entry from the raw pointer provided.
147 /// The lifetime of the entry is tied to the tree provided and the function
148 /// is unsafe because the validity of the pointer cannot be guaranteed.
149 pub unsafe fn entry_from_raw_const
<'tree
>(raw
: *const raw
::git_tree_entry
)
150 -> TreeEntry
<'tree
> {
152 raw
: raw
as *mut raw
::git_tree_entry
,
154 _marker
: marker
::PhantomData
,
158 impl<'tree
> TreeEntry
<'tree
> {
159 /// Get the id of the object pointed by the entry
160 pub fn id(&self) -> Oid
{
161 unsafe { Binding::from_raw(raw::git_tree_entry_id(&*self.raw)) }
164 /// Get the filename of a tree entry
166 /// Returns `None` if the name is not valid utf-8
167 pub fn name(&self) -> Option
<&str> {
168 str::from_utf8(self.name_bytes()).ok()
171 /// Get the filename of a tree entry
172 pub fn name_bytes(&self) -> &[u8] {
174 ::opt_bytes(self, raw
::git_tree_entry_name(&*self.raw())).unwrap()
178 /// Convert a tree entry to the object it points to.
179 pub fn to_object
<'a
>(&self, repo
: &'a Repository
)
180 -> Result
<Object
<'a
>, Error
> {
181 let mut ret
= ptr
::null_mut();
183 try_call
!(raw
::git_tree_entry_to_object(&mut ret
, repo
.raw(),
185 Ok(Binding
::from_raw(ret
))
189 /// Get the type of the object pointed by the entry
190 pub fn kind(&self) -> Option
<ObjectType
> {
191 ObjectType
::from_raw(unsafe { raw::git_tree_entry_type(&*self.raw) }
)
194 /// Get the UNIX file attributes of a tree entry
195 pub fn filemode(&self) -> i32 {
196 unsafe { raw::git_tree_entry_filemode(&*self.raw) as i32 }
199 /// Get the raw UNIX file attributes of a tree entry
200 pub fn filemode_raw(&self) -> i32 {
201 unsafe { raw::git_tree_entry_filemode_raw(&*self.raw) as i32 }
204 /// Convert this entry of any lifetime into an owned signature with a static
207 /// This will use the `Clone::clone` implementation under the hood.
208 pub fn to_owned(&self) -> TreeEntry
<'
static> {
210 let me
= mem
::transmute
::<&TreeEntry
<'tree
>, &TreeEntry
<'
static>>(self);
216 impl<'a
> Binding
for TreeEntry
<'a
> {
217 type Raw
= *mut raw
::git_tree_entry
;
218 unsafe fn from_raw(raw
: *mut raw
::git_tree_entry
) -> TreeEntry
<'a
> {
222 _marker
: marker
::PhantomData
,
225 fn raw(&self) -> *mut raw
::git_tree_entry { self.raw }
228 impl<'a
> Clone
for TreeEntry
<'a
> {
229 fn clone(&self) -> TreeEntry
<'a
> {
230 let mut ret
= ptr
::null_mut();
232 assert_eq
!(raw
::git_tree_entry_dup(&mut ret
, &*self.raw()), 0);
233 Binding
::from_raw(ret
)
238 impl<'a
> PartialOrd
for TreeEntry
<'a
> {
239 fn partial_cmp(&self, other
: &TreeEntry
<'a
>) -> Option
<Ordering
> {
240 Some(self.cmp(other
))
243 impl<'a
> Ord
for TreeEntry
<'a
> {
244 fn cmp(&self, other
: &TreeEntry
<'a
>) -> Ordering
{
245 match unsafe { raw::git_tree_entry_cmp(&*self.raw(), &*other.raw()) }
{
246 0 => Ordering
::Equal
,
247 n
if n
< 0 => Ordering
::Less
,
248 _
=> Ordering
::Greater
,
253 impl<'a
> PartialEq
for TreeEntry
<'a
> {
254 fn eq(&self, other
: &TreeEntry
<'a
>) -> bool
{
255 self.cmp(other
) == Ordering
::Equal
258 impl<'a
> Eq
for TreeEntry
<'a
> {}
260 impl<'a
> Drop
for TreeEntry
<'a
> {
263 unsafe { raw::git_tree_entry_free(self.raw) }
268 impl<'tree
> Iterator
for TreeIter
<'tree
> {
269 type Item
= TreeEntry
<'tree
>;
270 fn next(&mut self) -> Option
<TreeEntry
<'tree
>> {
271 self.range
.next().and_then(|i
| self.tree
.get(i
))
273 fn size_hint(&self) -> (usize, Option
<usize>) { self.range.size_hint() }
275 impl<'tree
> DoubleEndedIterator
for TreeIter
<'tree
> {
276 fn next_back(&mut self) -> Option
<TreeEntry
<'tree
>> {
277 self.range
.next_back().and_then(|i
| self.tree
.get(i
))
280 impl<'tree
> ExactSizeIterator
for TreeIter
<'tree
> {}
284 use {Repository,Tree,TreeEntry,ObjectType,Object}
;
285 use tempdir
::TempDir
;
287 use std
::io
::prelude
::*;
290 pub struct TestTreeIter
<'a
> {
291 entries
: Vec
<TreeEntry
<'a
>>,
292 repo
: &'a Repository
,
295 impl<'a
> Iterator
for TestTreeIter
<'a
> {
296 type Item
= TreeEntry
<'a
>;
298 fn next(&mut self) -> Option
<TreeEntry
<'a
> > {
299 if self.entries
.is_empty() {
302 let entry
= self.entries
.remove(0);
305 Some(ObjectType
::Tree
) => {
306 let obj
: Object
<'a
> = entry
.to_object(self.repo
).unwrap();
308 let tree
: &Tree
<'a
> = obj
.as_tree().unwrap();
310 for entry
in tree
.iter() {
311 self.entries
.push(entry
.to_owned());
322 fn tree_iter
<'repo
>(tree
: &Tree
<'repo
>, repo
: &'repo Repository
)
323 -> TestTreeIter
<'repo
> {
324 let mut initial
= vec
![];
326 for entry
in tree
.iter() {
327 initial
.push(entry
.to_owned());
337 fn smoke_tree_iter() {
338 let (td
, repo
) = ::test
::repo_init();
340 setup_repo(&td
, &repo
);
342 let head
= repo
.head().unwrap();
343 let target
= head
.target().unwrap();
344 let commit
= repo
.find_commit(target
).unwrap();
346 let tree
= repo
.find_tree(commit
.tree_id()).unwrap();
347 assert_eq
!(tree
.id(), commit
.tree_id());
348 assert_eq
!(tree
.len(), 1);
350 for entry
in tree_iter(&tree
, &repo
) {
351 println
!("iter entry {:?}", entry
.name());
355 fn setup_repo(td
: &TempDir
, repo
: &Repository
) {
356 let mut index
= repo
.index().unwrap();
357 File
::create(&td
.path().join("foo")).unwrap().write_all(b
"foo").unwrap();
358 index
.add_path(Path
::new("foo")).unwrap();
359 let id
= index
.write_tree().unwrap();
360 let sig
= repo
.signature().unwrap();
361 let tree
= repo
.find_tree(id
).unwrap();
362 let parent
= repo
.find_commit(repo
.head().unwrap().target()
364 repo
.commit(Some("HEAD"), &sig
, &sig
, "another commit",
365 &tree
, &[&parent
]).unwrap();
370 let (td
, repo
) = ::test
::repo_init();
372 setup_repo(&td
, &repo
);
374 let head
= repo
.head().unwrap();
375 let target
= head
.target().unwrap();
376 let commit
= repo
.find_commit(target
).unwrap();
378 let tree
= repo
.find_tree(commit
.tree_id()).unwrap();
379 assert_eq
!(tree
.id(), commit
.tree_id());
380 assert_eq
!(tree
.len(), 1);
382 let e1
= tree
.get(0).unwrap();
383 assert
!(e1
== tree
.get_id(e1
.id()).unwrap());
384 assert
!(e1
== tree
.get_name("foo").unwrap());
385 assert
!(e1
== tree
.get_path(Path
::new("foo")).unwrap());
386 assert_eq
!(e1
.name(), Some("foo"));
387 e1
.to_object(&repo
).unwrap();
391 repo
.find_object(commit
.tree_id(), None
).unwrap().as_tree().unwrap();
392 repo
.find_object(commit
.tree_id(), None
).unwrap().into_tree().ok().unwrap();