1 use std
::cmp
::Ordering
;
8 use {raw, Error, Oid, Repository, Object, ObjectType}
;
11 struct Refdb
<'repo
>(&'repo Repository
);
13 /// A structure to represent a git [reference][1].
15 /// [1]: http://git-scm.com/book/en/Git-Internals-Git-References
16 pub struct Reference
<'repo
> {
17 raw
: *mut raw
::git_reference
,
18 _marker
: marker
::PhantomData
<Refdb
<'repo
>>,
21 /// An iterator over the references in a repository.
22 pub struct References
<'repo
> {
23 raw
: *mut raw
::git_reference_iterator
,
24 _marker
: marker
::PhantomData
<Refdb
<'repo
>>,
27 /// An iterator over the names of references in a repository.
28 pub struct ReferenceNames
<'repo
: 'references
, 'references
> {
29 inner
: &'references
mut References
<'repo
>,
32 impl<'repo
> Reference
<'repo
> {
33 /// Ensure the reference name is well-formed.
34 pub fn is_valid_name(refname
: &str) -> bool
{
36 let refname
= CString
::new(refname
).unwrap();
37 unsafe { raw::git_reference_is_valid_name(refname.as_ptr()) == 1 }
40 /// Get access to the underlying raw pointer.
41 pub fn raw(&self) -> *mut raw
::git_reference { self.raw }
43 /// Delete an existing reference.
45 /// This method works for both direct and symbolic references. The reference
46 /// will be immediately removed on disk.
48 /// This function will return an error if the reference has changed from the
49 /// time it was looked up.
50 pub fn delete(&mut self) -> Result
<(), Error
> {
51 unsafe { try_call!(raw::git_reference_delete(self.raw)); }
55 /// Check if a reference is a local branch.
56 pub fn is_branch(&self) -> bool
{
57 unsafe { raw::git_reference_is_branch(&*self.raw) == 1 }
60 /// Check if a reference is a note.
61 pub fn is_note(&self) -> bool
{
62 unsafe { raw::git_reference_is_note(&*self.raw) == 1 }
65 /// Check if a reference is a remote tracking branch
66 pub fn is_remote(&self) -> bool
{
67 unsafe { raw::git_reference_is_remote(&*self.raw) == 1 }
70 /// Check if a reference is a tag
71 pub fn is_tag(&self) -> bool
{
72 unsafe { raw::git_reference_is_tag(&*self.raw) == 1 }
75 /// Get the full name of a reference.
77 /// Returns `None` if the name is not valid utf-8.
78 pub fn name(&self) -> Option
<&str> { str::from_utf8(self.name_bytes()).ok() }
80 /// Get the full name of a reference.
81 pub fn name_bytes(&self) -> &[u8] {
82 unsafe { ::opt_bytes(self, raw::git_reference_name(&*self.raw)).unwrap() }
85 /// Get the full shorthand of a reference.
87 /// This will transform the reference name into a name "human-readable"
88 /// version. If no shortname is appropriate, it will return the full name.
90 /// Returns `None` if the shorthand is not valid utf-8.
91 pub fn shorthand(&self) -> Option
<&str> {
92 str::from_utf8(self.shorthand_bytes()).ok()
95 /// Get the full shorthand of a reference.
96 pub fn shorthand_bytes(&self) -> &[u8] {
98 ::opt_bytes(self, raw
::git_reference_shorthand(&*self.raw
)).unwrap()
102 /// Get the OID pointed to by a direct reference.
104 /// Only available if the reference is direct (i.e. an object id reference,
105 /// not a symbolic one).
106 pub fn target(&self) -> Option
<Oid
> {
108 Binding
::from_raw_opt(raw
::git_reference_target(&*self.raw
))
112 /// Return the peeled OID target of this reference.
114 /// This peeled OID only applies to direct references that point to a hard
115 /// Tag object: it is the result of peeling such Tag.
116 pub fn target_peel(&self) -> Option
<Oid
> {
118 Binding
::from_raw_opt(raw
::git_reference_target_peel(&*self.raw
))
122 /// Get full name to the reference pointed to by a symbolic reference.
124 /// May return `None` if the reference is either not symbolic or not a
125 /// valid utf-8 string.
126 pub fn symbolic_target(&self) -> Option
<&str> {
127 self.symbolic_target_bytes().and_then(|s
| str::from_utf8(s
).ok())
130 /// Get full name to the reference pointed to by a symbolic reference.
132 /// Only available if the reference is symbolic.
133 pub fn symbolic_target_bytes(&self) -> Option
<&[u8]> {
134 unsafe { ::opt_bytes(self, raw::git_reference_symbolic_target(&*self.raw)) }
137 /// Resolve a symbolic reference to a direct reference.
139 /// This method iteratively peels a symbolic reference until it resolves to
140 /// a direct reference to an OID.
142 /// If a direct reference is passed as an argument, a copy of that
143 /// reference is returned.
144 pub fn resolve(&self) -> Result
<Reference
<'repo
>, Error
> {
145 let mut raw
= ptr
::null_mut();
147 try_call
!(raw
::git_reference_resolve(&mut raw
, &*self.raw
));
148 Ok(Binding
::from_raw(raw
))
152 /// Peel a reference to an object
154 /// This method recursively peels the reference until it reaches
155 /// an object of the specified type.
156 pub fn peel(&self, kind
: ObjectType
) -> Result
<Object
<'repo
>, Error
> {
157 let mut raw
= ptr
::null_mut();
159 try_call
!(raw
::git_reference_peel(&mut raw
, self.raw
, kind
));
160 Ok(Binding
::from_raw(raw
))
164 /// Rename an existing reference.
166 /// This method works for both direct and symbolic references.
168 /// If the force flag is not enabled, and there's already a reference with
169 /// the given name, the renaming will fail.
170 pub fn rename(&mut self, new_name
: &str, force
: bool
,
171 msg
: &str) -> Result
<Reference
<'repo
>, Error
> {
172 let mut raw
= ptr
::null_mut();
173 let new_name
= try
!(CString
::new(new_name
));
174 let msg
= try
!(CString
::new(msg
));
176 try_call
!(raw
::git_reference_rename(&mut raw
, self.raw
, new_name
,
178 Ok(Binding
::from_raw(raw
))
182 /// Conditionally create a new reference with the same name as the given
183 /// reference but a different OID target. The reference must be a direct
184 /// reference, otherwise this will fail.
186 /// The new reference will be written to disk, overwriting the given
188 pub fn set_target(&mut self, id
: Oid
, reflog_msg
: &str)
189 -> Result
<Reference
<'repo
>, Error
> {
190 let mut raw
= ptr
::null_mut();
191 let msg
= try
!(CString
::new(reflog_msg
));
193 try_call
!(raw
::git_reference_set_target(&mut raw
, self.raw
,
195 Ok(Binding
::from_raw(raw
))
201 impl<'repo
> PartialOrd
for Reference
<'repo
> {
202 fn partial_cmp(&self, other
: &Reference
<'repo
>) -> Option
<Ordering
> {
203 Some(self.cmp(other
))
207 impl<'repo
> Ord
for Reference
<'repo
> {
208 fn cmp(&self, other
: &Reference
<'repo
>) -> Ordering
{
209 match unsafe { raw::git_reference_cmp(&*self.raw, &*other.raw) }
{
210 0 => Ordering
::Equal
,
211 n
if n
< 0 => Ordering
::Less
,
212 _
=> Ordering
::Greater
,
217 impl<'repo
> PartialEq
for Reference
<'repo
> {
218 fn eq(&self, other
: &Reference
<'repo
>) -> bool
{
219 self.cmp(other
) == Ordering
::Equal
223 impl<'repo
> Eq
for Reference
<'repo
> {}
225 impl<'repo
> Binding
for Reference
<'repo
> {
226 type Raw
= *mut raw
::git_reference
;
227 unsafe fn from_raw(raw
: *mut raw
::git_reference
) -> Reference
<'repo
> {
228 Reference { raw: raw, _marker: marker::PhantomData }
230 fn raw(&self) -> *mut raw
::git_reference { self.raw }
233 impl<'repo
> Drop
for Reference
<'repo
> {
235 unsafe { raw::git_reference_free(self.raw) }
239 impl<'repo
> References
<'repo
> {
240 /// Consumes a `References` iterator to create an iterator over just the
241 /// name of some references.
243 /// This is more efficient if only the names are desired of references as
244 /// the references themselves don't have to be allocated and deallocated.
246 /// The returned iterator will yield strings as opposed to a `Reference`.
247 pub fn names
<'a
>(&'a
mut self) -> ReferenceNames
<'repo
, 'a
> {
248 ReferenceNames { inner: self }
252 impl<'repo
> Binding
for References
<'repo
> {
253 type Raw
= *mut raw
::git_reference_iterator
;
254 unsafe fn from_raw(raw
: *mut raw
::git_reference_iterator
)
255 -> References
<'repo
> {
256 References { raw: raw, _marker: marker::PhantomData }
258 fn raw(&self) -> *mut raw
::git_reference_iterator { self.raw }
261 impl<'repo
> Iterator
for References
<'repo
> {
262 type Item
= Result
<Reference
<'repo
>, Error
>;
263 fn next(&mut self) -> Option
<Result
<Reference
<'repo
>, Error
>> {
264 let mut out
= ptr
::null_mut();
266 try_call_iter
!(raw
::git_reference_next(&mut out
, self.raw
));
267 Some(Ok(Binding
::from_raw(out
)))
272 impl<'repo
> Drop
for References
<'repo
> {
274 unsafe { raw::git_reference_iterator_free(self.raw) }
278 impl<'repo
, 'references
> Iterator
for ReferenceNames
<'repo
, 'references
> {
279 type Item
= Result
<&'references
str, Error
>;
280 fn next(&mut self) -> Option
<Result
<&'references
str, Error
>> {
281 let mut out
= ptr
::null();
283 try_call_iter
!(raw
::git_reference_next_name(&mut out
,
285 let bytes
= ::opt_bytes(self, out
).unwrap();
286 let s
= str::from_utf8(bytes
).unwrap();
287 Some(Ok(mem
::transmute
::<&str, &'references
str>(s
)))
294 use {Reference, ObjectType}
;
298 assert
!(Reference
::is_valid_name("refs/foo"));
299 assert
!(!Reference
::is_valid_name("foo"));
304 let (_td
, repo
) = ::test
::repo_init();
305 let mut head
= repo
.head().unwrap();
306 assert
!(head
.is_branch());
307 assert
!(!head
.is_remote());
308 assert
!(!head
.is_tag());
309 assert
!(!head
.is_note());
311 assert
!(head
== repo
.head().unwrap());
312 assert_eq
!(head
.name(), Some("refs/heads/master"));
314 assert
!(head
== repo
.find_reference("refs/heads/master").unwrap());
315 assert_eq
!(repo
.refname_to_id("refs/heads/master").unwrap(),
316 head
.target().unwrap());
318 assert
!(head
.symbolic_target().is_none());
319 assert
!(head
.target_peel().is_none());
321 assert_eq
!(head
.shorthand(), Some("master"));
322 assert
!(head
.resolve().unwrap() == head
);
324 let mut tag1
= repo
.reference("refs/tags/tag1",
325 head
.target().unwrap(),
326 false, "test").unwrap();
327 assert
!(tag1
.is_tag());
329 let peeled_commit
= tag1
.peel(ObjectType
::Commit
).unwrap();
330 assert_eq
!(ObjectType
::Commit
, peeled_commit
.kind().unwrap());
331 assert_eq
!(tag1
.target().unwrap(), peeled_commit
.id());
333 tag1
.delete().unwrap();
335 let mut sym1
= repo
.reference_symbolic("refs/tags/tag1",
336 "refs/heads/master", false,
338 sym1
.delete().unwrap();
341 assert
!(repo
.references().unwrap().count() == 1);
342 assert
!(repo
.references().unwrap().next().unwrap().unwrap() == head
);
343 let mut names
= repo
.references().unwrap();
344 let mut names
= names
.names();
345 assert_eq
!(names
.next().unwrap().unwrap(), "refs/heads/master");
346 assert
!(names
.next().is_none());
347 assert
!(repo
.references_glob("foo").unwrap().count() == 0);
348 assert
!(repo
.references_glob("refs/heads/*").unwrap().count() == 1);
351 let mut head
= head
.rename("refs/foo", true, "test").unwrap();
352 head
.delete().unwrap();