]> git.proxmox.com Git - cargo.git/blob - vendor/git2-0.6.8/src/reference.rs
New upstream version 0.23.0
[cargo.git] / vendor / git2-0.6.8 / src / reference.rs
1 use std::cmp::Ordering;
2 use std::ffi::CString;
3 use std::marker;
4 use std::mem;
5 use std::ptr;
6 use std::str;
7
8 use {raw, Error, Oid, Repository, Object, ObjectType};
9 use util::Binding;
10
11 struct Refdb<'repo>(&'repo Repository);
12
13 /// A structure to represent a git [reference][1].
14 ///
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>>,
19 }
20
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>>,
25 }
26
27 /// An iterator over the names of references in a repository.
28 pub struct ReferenceNames<'repo: 'references, 'references> {
29 inner: &'references mut References<'repo>,
30 }
31
32 impl<'repo> Reference<'repo> {
33 /// Ensure the reference name is well-formed.
34 pub fn is_valid_name(refname: &str) -> bool {
35 ::init();
36 let refname = CString::new(refname).unwrap();
37 unsafe { raw::git_reference_is_valid_name(refname.as_ptr()) == 1 }
38 }
39
40 /// Get access to the underlying raw pointer.
41 pub fn raw(&self) -> *mut raw::git_reference { self.raw }
42
43 /// Delete an existing reference.
44 ///
45 /// This method works for both direct and symbolic references. The reference
46 /// will be immediately removed on disk.
47 ///
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)); }
52 Ok(())
53 }
54
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 }
58 }
59
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 }
63 }
64
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 }
68 }
69
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 }
73 }
74
75 /// Get the full name of a reference.
76 ///
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() }
79
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() }
83 }
84
85 /// Get the full shorthand of a reference.
86 ///
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.
89 ///
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()
93 }
94
95 /// Get the full shorthand of a reference.
96 pub fn shorthand_bytes(&self) -> &[u8] {
97 unsafe {
98 ::opt_bytes(self, raw::git_reference_shorthand(&*self.raw)).unwrap()
99 }
100 }
101
102 /// Get the OID pointed to by a direct reference.
103 ///
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> {
107 unsafe {
108 Binding::from_raw_opt(raw::git_reference_target(&*self.raw))
109 }
110 }
111
112 /// Return the peeled OID target of this reference.
113 ///
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> {
117 unsafe {
118 Binding::from_raw_opt(raw::git_reference_target_peel(&*self.raw))
119 }
120 }
121
122 /// Get full name to the reference pointed to by a symbolic reference.
123 ///
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())
128 }
129
130 /// Get full name to the reference pointed to by a symbolic reference.
131 ///
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)) }
135 }
136
137 /// Resolve a symbolic reference to a direct reference.
138 ///
139 /// This method iteratively peels a symbolic reference until it resolves to
140 /// a direct reference to an OID.
141 ///
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();
146 unsafe {
147 try_call!(raw::git_reference_resolve(&mut raw, &*self.raw));
148 Ok(Binding::from_raw(raw))
149 }
150 }
151
152 /// Peel a reference to an object
153 ///
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();
158 unsafe {
159 try_call!(raw::git_reference_peel(&mut raw, self.raw, kind));
160 Ok(Binding::from_raw(raw))
161 }
162 }
163
164 /// Rename an existing reference.
165 ///
166 /// This method works for both direct and symbolic references.
167 ///
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));
175 unsafe {
176 try_call!(raw::git_reference_rename(&mut raw, self.raw, new_name,
177 force, msg));
178 Ok(Binding::from_raw(raw))
179 }
180 }
181
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.
185 ///
186 /// The new reference will be written to disk, overwriting the given
187 /// reference.
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));
192 unsafe {
193 try_call!(raw::git_reference_set_target(&mut raw, self.raw,
194 id.raw(), msg));
195 Ok(Binding::from_raw(raw))
196 }
197 }
198
199 }
200
201 impl<'repo> PartialOrd for Reference<'repo> {
202 fn partial_cmp(&self, other: &Reference<'repo>) -> Option<Ordering> {
203 Some(self.cmp(other))
204 }
205 }
206
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,
213 }
214 }
215 }
216
217 impl<'repo> PartialEq for Reference<'repo> {
218 fn eq(&self, other: &Reference<'repo>) -> bool {
219 self.cmp(other) == Ordering::Equal
220 }
221 }
222
223 impl<'repo> Eq for Reference<'repo> {}
224
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 }
229 }
230 fn raw(&self) -> *mut raw::git_reference { self.raw }
231 }
232
233 impl<'repo> Drop for Reference<'repo> {
234 fn drop(&mut self) {
235 unsafe { raw::git_reference_free(self.raw) }
236 }
237 }
238
239 impl<'repo> References<'repo> {
240 /// Consumes a `References` iterator to create an iterator over just the
241 /// name of some references.
242 ///
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.
245 ///
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 }
249 }
250 }
251
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 }
257 }
258 fn raw(&self) -> *mut raw::git_reference_iterator { self.raw }
259 }
260
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();
265 unsafe {
266 try_call_iter!(raw::git_reference_next(&mut out, self.raw));
267 Some(Ok(Binding::from_raw(out)))
268 }
269 }
270 }
271
272 impl<'repo> Drop for References<'repo> {
273 fn drop(&mut self) {
274 unsafe { raw::git_reference_iterator_free(self.raw) }
275 }
276 }
277
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();
282 unsafe {
283 try_call_iter!(raw::git_reference_next_name(&mut out,
284 self.inner.raw));
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)))
288 }
289 }
290 }
291
292 #[cfg(test)]
293 mod tests {
294 use {Reference, ObjectType};
295
296 #[test]
297 fn smoke() {
298 assert!(Reference::is_valid_name("refs/foo"));
299 assert!(!Reference::is_valid_name("foo"));
300 }
301
302 #[test]
303 fn smoke2() {
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());
310
311 assert!(head == repo.head().unwrap());
312 assert_eq!(head.name(), Some("refs/heads/master"));
313
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());
317
318 assert!(head.symbolic_target().is_none());
319 assert!(head.target_peel().is_none());
320
321 assert_eq!(head.shorthand(), Some("master"));
322 assert!(head.resolve().unwrap() == head);
323
324 let mut tag1 = repo.reference("refs/tags/tag1",
325 head.target().unwrap(),
326 false, "test").unwrap();
327 assert!(tag1.is_tag());
328
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());
332
333 tag1.delete().unwrap();
334
335 let mut sym1 = repo.reference_symbolic("refs/tags/tag1",
336 "refs/heads/master", false,
337 "test").unwrap();
338 sym1.delete().unwrap();
339
340 {
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);
349 }
350
351 let mut head = head.rename("refs/foo", true, "test").unwrap();
352 head.delete().unwrap();
353
354 }
355 }