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 crate::object::CastOrPanic;
9 use crate::util::{c_cmp_to_ordering, Binding};
10 use crate::{
11     raw, Blob, Commit, Error, Object, ObjectType, Oid, ReferenceFormat, ReferenceType, Repository,
12     Tag, Tree,
13 };
14 
15 // Not in the public header files (yet?), but a hard limit used by libgit2
16 // internally
17 const GIT_REFNAME_MAX: usize = 1024;
18 
19 struct Refdb<'repo>(&'repo Repository);
20 
21 /// A structure to represent a git [reference][1].
22 ///
23 /// [1]: http://git-scm.com/book/en/Git-Internals-Git-References
24 pub struct Reference<'repo> {
25     raw: *mut raw::git_reference,
26     _marker: marker::PhantomData<Refdb<'repo>>,
27 }
28 
29 /// An iterator over the references in a repository.
30 pub struct References<'repo> {
31     raw: *mut raw::git_reference_iterator,
32     _marker: marker::PhantomData<Refdb<'repo>>,
33 }
34 
35 /// An iterator over the names of references in a repository.
36 pub struct ReferenceNames<'repo, 'references> {
37     inner: &'references mut References<'repo>,
38 }
39 
40 impl<'repo> Reference<'repo> {
41     /// Ensure the reference name is well-formed.
42     ///
43     /// Validation is performed as if [`ReferenceFormat::ALLOW_ONELEVEL`]
44     /// was given to [`Reference::normalize_name`]. No normalization is
45     /// performed, however.
46     ///
47     /// ```rust
48     /// use git2::Reference;
49     ///
50     /// assert!(Reference::is_valid_name("HEAD"));
51     /// assert!(Reference::is_valid_name("refs/heads/main"));
52     ///
53     /// // But:
54     /// assert!(!Reference::is_valid_name("main"));
55     /// assert!(!Reference::is_valid_name("refs/heads/*"));
56     /// assert!(!Reference::is_valid_name("foo//bar"));
57     /// ```
58     ///
59     /// [`ReferenceFormat::ALLOW_ONELEVEL`]:
60     ///     struct.ReferenceFormat#associatedconstant.ALLOW_ONELEVEL
61     /// [`Reference::normalize_name`]: struct.Reference#method.normalize_name
is_valid_name(refname: &str) -> bool62     pub fn is_valid_name(refname: &str) -> bool {
63         crate::init();
64         let refname = CString::new(refname).unwrap();
65         unsafe { raw::git_reference_is_valid_name(refname.as_ptr()) == 1 }
66     }
67 
68     /// Normalize reference name and check validity.
69     ///
70     /// This will normalize the reference name by collapsing runs of adjacent
71     /// slashes between name components into a single slash. It also validates
72     /// the name according to the following rules:
73     ///
74     /// 1. If [`ReferenceFormat::ALLOW_ONELEVEL`] is given, the name may
75     ///    contain only capital letters and underscores, and must begin and end
76     ///    with a letter. (e.g. "HEAD", "ORIG_HEAD").
77     /// 2. The flag [`ReferenceFormat::REFSPEC_SHORTHAND`] has an effect
78     ///    only when combined with [`ReferenceFormat::ALLOW_ONELEVEL`]. If
79     ///    it is given, "shorthand" branch names (i.e. those not prefixed by
80     ///    `refs/`, but consisting of a single word without `/` separators)
81     ///    become valid. For example, "main" would be accepted.
82     /// 3. If [`ReferenceFormat::REFSPEC_PATTERN`] is given, the name may
83     ///    contain a single `*` in place of a full pathname component (e.g.
84     ///    `foo/*/bar`, `foo/bar*`).
85     /// 4. Names prefixed with "refs/" can be almost anything. You must avoid
86     ///    the characters '~', '^', ':', '\\', '?', '[', and '*', and the
87     ///    sequences ".." and "@{" which have special meaning to revparse.
88     ///
89     /// If the reference passes validation, it is returned in normalized form,
90     /// otherwise an [`Error`] with [`ErrorCode::InvalidSpec`] is returned.
91     ///
92     /// ```rust
93     /// use git2::{Reference, ReferenceFormat};
94     ///
95     /// assert_eq!(
96     ///     Reference::normalize_name(
97     ///         "foo//bar",
98     ///         ReferenceFormat::NORMAL
99     ///     )
100     ///     .unwrap(),
101     ///     "foo/bar".to_owned()
102     /// );
103     ///
104     /// assert_eq!(
105     ///     Reference::normalize_name(
106     ///         "HEAD",
107     ///         ReferenceFormat::ALLOW_ONELEVEL
108     ///     )
109     ///     .unwrap(),
110     ///     "HEAD".to_owned()
111     /// );
112     ///
113     /// assert_eq!(
114     ///     Reference::normalize_name(
115     ///         "refs/heads/*",
116     ///         ReferenceFormat::REFSPEC_PATTERN
117     ///     )
118     ///     .unwrap(),
119     ///     "refs/heads/*".to_owned()
120     /// );
121     ///
122     /// assert_eq!(
123     ///     Reference::normalize_name(
124     ///         "main",
125     ///         ReferenceFormat::ALLOW_ONELEVEL | ReferenceFormat::REFSPEC_SHORTHAND
126     ///     )
127     ///     .unwrap(),
128     ///     "main".to_owned()
129     /// );
130     /// ```
131     ///
132     /// [`ReferenceFormat::ALLOW_ONELEVEL`]:
133     ///     struct.ReferenceFormat#associatedconstant.ALLOW_ONELEVEL
134     /// [`ReferenceFormat::REFSPEC_SHORTHAND`]:
135     ///     struct.ReferenceFormat#associatedconstant.REFSPEC_SHORTHAND
136     /// [`ReferenceFormat::REFSPEC_PATTERN`]:
137     ///     struct.ReferenceFormat#associatedconstant.REFSPEC_PATTERN
138     /// [`Error`]: struct.Error
139     /// [`ErrorCode::InvalidSpec`]: enum.ErrorCode#variant.InvalidSpec
normalize_name(refname: &str, flags: ReferenceFormat) -> Result<String, Error>140     pub fn normalize_name(refname: &str, flags: ReferenceFormat) -> Result<String, Error> {
141         crate::init();
142         let mut dst = [0u8; GIT_REFNAME_MAX];
143         let refname = CString::new(refname)?;
144         unsafe {
145             try_call!(raw::git_reference_normalize_name(
146                 dst.as_mut_ptr() as *mut libc::c_char,
147                 dst.len() as libc::size_t,
148                 refname,
149                 flags.bits()
150             ));
151             let s = &dst[..dst.iter().position(|&a| a == 0).unwrap()];
152             Ok(str::from_utf8(s).unwrap().to_owned())
153         }
154     }
155 
156     /// Get access to the underlying raw pointer.
raw(&self) -> *mut raw::git_reference157     pub fn raw(&self) -> *mut raw::git_reference {
158         self.raw
159     }
160 
161     /// Delete an existing reference.
162     ///
163     /// This method works for both direct and symbolic references. The reference
164     /// will be immediately removed on disk.
165     ///
166     /// This function will return an error if the reference has changed from the
167     /// time it was looked up.
delete(&mut self) -> Result<(), Error>168     pub fn delete(&mut self) -> Result<(), Error> {
169         unsafe {
170             try_call!(raw::git_reference_delete(self.raw));
171         }
172         Ok(())
173     }
174 
175     /// Check if a reference is a local branch.
is_branch(&self) -> bool176     pub fn is_branch(&self) -> bool {
177         unsafe { raw::git_reference_is_branch(&*self.raw) == 1 }
178     }
179 
180     /// Check if a reference is a note.
is_note(&self) -> bool181     pub fn is_note(&self) -> bool {
182         unsafe { raw::git_reference_is_note(&*self.raw) == 1 }
183     }
184 
185     /// Check if a reference is a remote tracking branch
is_remote(&self) -> bool186     pub fn is_remote(&self) -> bool {
187         unsafe { raw::git_reference_is_remote(&*self.raw) == 1 }
188     }
189 
190     /// Check if a reference is a tag
is_tag(&self) -> bool191     pub fn is_tag(&self) -> bool {
192         unsafe { raw::git_reference_is_tag(&*self.raw) == 1 }
193     }
194 
195     /// Get the reference type of a reference.
196     ///
197     /// If the type is unknown, then `None` is returned.
kind(&self) -> Option<ReferenceType>198     pub fn kind(&self) -> Option<ReferenceType> {
199         ReferenceType::from_raw(unsafe { raw::git_reference_type(&*self.raw) })
200     }
201 
202     /// Get the full name of a reference.
203     ///
204     /// Returns `None` if the name is not valid utf-8.
name(&self) -> Option<&str>205     pub fn name(&self) -> Option<&str> {
206         str::from_utf8(self.name_bytes()).ok()
207     }
208 
209     /// Get the full name of a reference.
name_bytes(&self) -> &[u8]210     pub fn name_bytes(&self) -> &[u8] {
211         unsafe { crate::opt_bytes(self, raw::git_reference_name(&*self.raw)).unwrap() }
212     }
213 
214     /// Get the full shorthand of a reference.
215     ///
216     /// This will transform the reference name into a name "human-readable"
217     /// version. If no shortname is appropriate, it will return the full name.
218     ///
219     /// Returns `None` if the shorthand is not valid utf-8.
shorthand(&self) -> Option<&str>220     pub fn shorthand(&self) -> Option<&str> {
221         str::from_utf8(self.shorthand_bytes()).ok()
222     }
223 
224     /// Get the full shorthand of a reference.
shorthand_bytes(&self) -> &[u8]225     pub fn shorthand_bytes(&self) -> &[u8] {
226         unsafe { crate::opt_bytes(self, raw::git_reference_shorthand(&*self.raw)).unwrap() }
227     }
228 
229     /// Get the OID pointed to by a direct reference.
230     ///
231     /// Only available if the reference is direct (i.e. an object id reference,
232     /// not a symbolic one).
target(&self) -> Option<Oid>233     pub fn target(&self) -> Option<Oid> {
234         unsafe { Binding::from_raw_opt(raw::git_reference_target(&*self.raw)) }
235     }
236 
237     /// Return the peeled OID target of this reference.
238     ///
239     /// This peeled OID only applies to direct references that point to a hard
240     /// Tag object: it is the result of peeling such Tag.
target_peel(&self) -> Option<Oid>241     pub fn target_peel(&self) -> Option<Oid> {
242         unsafe { Binding::from_raw_opt(raw::git_reference_target_peel(&*self.raw)) }
243     }
244 
245     /// Get full name to the reference pointed to by a symbolic reference.
246     ///
247     /// May return `None` if the reference is either not symbolic or not a
248     /// valid utf-8 string.
symbolic_target(&self) -> Option<&str>249     pub fn symbolic_target(&self) -> Option<&str> {
250         self.symbolic_target_bytes()
251             .and_then(|s| str::from_utf8(s).ok())
252     }
253 
254     /// Get full name to the reference pointed to by a symbolic reference.
255     ///
256     /// Only available if the reference is symbolic.
symbolic_target_bytes(&self) -> Option<&[u8]>257     pub fn symbolic_target_bytes(&self) -> Option<&[u8]> {
258         unsafe { crate::opt_bytes(self, raw::git_reference_symbolic_target(&*self.raw)) }
259     }
260 
261     /// Resolve a symbolic reference to a direct reference.
262     ///
263     /// This method iteratively peels a symbolic reference until it resolves to
264     /// a direct reference to an OID.
265     ///
266     /// If a direct reference is passed as an argument, a copy of that
267     /// reference is returned.
resolve(&self) -> Result<Reference<'repo>, Error>268     pub fn resolve(&self) -> Result<Reference<'repo>, Error> {
269         let mut raw = ptr::null_mut();
270         unsafe {
271             try_call!(raw::git_reference_resolve(&mut raw, &*self.raw));
272             Ok(Binding::from_raw(raw))
273         }
274     }
275 
276     /// Peel a reference to an object
277     ///
278     /// This method recursively peels the reference until it reaches
279     /// an object of the specified type.
peel(&self, kind: ObjectType) -> Result<Object<'repo>, Error>280     pub fn peel(&self, kind: ObjectType) -> Result<Object<'repo>, Error> {
281         let mut raw = ptr::null_mut();
282         unsafe {
283             try_call!(raw::git_reference_peel(&mut raw, self.raw, kind));
284             Ok(Binding::from_raw(raw))
285         }
286     }
287 
288     /// Peel a reference to a blob
289     ///
290     /// This method recursively peels the reference until it reaches
291     /// a blob.
peel_to_blob(&self) -> Result<Blob<'repo>, Error>292     pub fn peel_to_blob(&self) -> Result<Blob<'repo>, Error> {
293         Ok(self.peel(ObjectType::Blob)?.cast_or_panic(ObjectType::Blob))
294     }
295 
296     /// Peel a reference to a commit
297     ///
298     /// This method recursively peels the reference until it reaches
299     /// a commit.
peel_to_commit(&self) -> Result<Commit<'repo>, Error>300     pub fn peel_to_commit(&self) -> Result<Commit<'repo>, Error> {
301         Ok(self
302             .peel(ObjectType::Commit)?
303             .cast_or_panic(ObjectType::Commit))
304     }
305 
306     /// Peel a reference to a tree
307     ///
308     /// This method recursively peels the reference until it reaches
309     /// a tree.
peel_to_tree(&self) -> Result<Tree<'repo>, Error>310     pub fn peel_to_tree(&self) -> Result<Tree<'repo>, Error> {
311         Ok(self.peel(ObjectType::Tree)?.cast_or_panic(ObjectType::Tree))
312     }
313 
314     /// Peel a reference to a tag
315     ///
316     /// This method recursively peels the reference until it reaches
317     /// a tag.
peel_to_tag(&self) -> Result<Tag<'repo>, Error>318     pub fn peel_to_tag(&self) -> Result<Tag<'repo>, Error> {
319         Ok(self.peel(ObjectType::Tag)?.cast_or_panic(ObjectType::Tag))
320     }
321 
322     /// Rename an existing reference.
323     ///
324     /// This method works for both direct and symbolic references.
325     ///
326     /// If the force flag is not enabled, and there's already a reference with
327     /// the given name, the renaming will fail.
rename( &mut self, new_name: &str, force: bool, msg: &str, ) -> Result<Reference<'repo>, Error>328     pub fn rename(
329         &mut self,
330         new_name: &str,
331         force: bool,
332         msg: &str,
333     ) -> Result<Reference<'repo>, Error> {
334         let mut raw = ptr::null_mut();
335         let new_name = CString::new(new_name)?;
336         let msg = CString::new(msg)?;
337         unsafe {
338             try_call!(raw::git_reference_rename(
339                 &mut raw, self.raw, new_name, force, msg
340             ));
341             Ok(Binding::from_raw(raw))
342         }
343     }
344 
345     /// Conditionally create a new reference with the same name as the given
346     /// reference but a different OID target. The reference must be a direct
347     /// reference, otherwise this will fail.
348     ///
349     /// The new reference will be written to disk, overwriting the given
350     /// reference.
set_target(&mut self, id: Oid, reflog_msg: &str) -> Result<Reference<'repo>, Error>351     pub fn set_target(&mut self, id: Oid, reflog_msg: &str) -> Result<Reference<'repo>, Error> {
352         let mut raw = ptr::null_mut();
353         let msg = CString::new(reflog_msg)?;
354         unsafe {
355             try_call!(raw::git_reference_set_target(
356                 &mut raw,
357                 self.raw,
358                 id.raw(),
359                 msg
360             ));
361             Ok(Binding::from_raw(raw))
362         }
363     }
364 }
365 
366 impl<'repo> PartialOrd for Reference<'repo> {
partial_cmp(&self, other: &Reference<'repo>) -> Option<Ordering>367     fn partial_cmp(&self, other: &Reference<'repo>) -> Option<Ordering> {
368         Some(self.cmp(other))
369     }
370 }
371 
372 impl<'repo> Ord for Reference<'repo> {
cmp(&self, other: &Reference<'repo>) -> Ordering373     fn cmp(&self, other: &Reference<'repo>) -> Ordering {
374         c_cmp_to_ordering(unsafe { raw::git_reference_cmp(&*self.raw, &*other.raw) })
375     }
376 }
377 
378 impl<'repo> PartialEq for Reference<'repo> {
eq(&self, other: &Reference<'repo>) -> bool379     fn eq(&self, other: &Reference<'repo>) -> bool {
380         self.cmp(other) == Ordering::Equal
381     }
382 }
383 
384 impl<'repo> Eq for Reference<'repo> {}
385 
386 impl<'repo> Binding for Reference<'repo> {
387     type Raw = *mut raw::git_reference;
from_raw(raw: *mut raw::git_reference) -> Reference<'repo>388     unsafe fn from_raw(raw: *mut raw::git_reference) -> Reference<'repo> {
389         Reference {
390             raw: raw,
391             _marker: marker::PhantomData,
392         }
393     }
raw(&self) -> *mut raw::git_reference394     fn raw(&self) -> *mut raw::git_reference {
395         self.raw
396     }
397 }
398 
399 impl<'repo> Drop for Reference<'repo> {
drop(&mut self)400     fn drop(&mut self) {
401         unsafe { raw::git_reference_free(self.raw) }
402     }
403 }
404 
405 impl<'repo> References<'repo> {
406     /// Consumes a `References` iterator to create an iterator over just the
407     /// name of some references.
408     ///
409     /// This is more efficient if only the names are desired of references as
410     /// the references themselves don't have to be allocated and deallocated.
411     ///
412     /// The returned iterator will yield strings as opposed to a `Reference`.
names<'a>(&'a mut self) -> ReferenceNames<'repo, 'a>413     pub fn names<'a>(&'a mut self) -> ReferenceNames<'repo, 'a> {
414         ReferenceNames { inner: self }
415     }
416 }
417 
418 impl<'repo> Binding for References<'repo> {
419     type Raw = *mut raw::git_reference_iterator;
from_raw(raw: *mut raw::git_reference_iterator) -> References<'repo>420     unsafe fn from_raw(raw: *mut raw::git_reference_iterator) -> References<'repo> {
421         References {
422             raw: raw,
423             _marker: marker::PhantomData,
424         }
425     }
raw(&self) -> *mut raw::git_reference_iterator426     fn raw(&self) -> *mut raw::git_reference_iterator {
427         self.raw
428     }
429 }
430 
431 impl<'repo> Iterator for References<'repo> {
432     type Item = Result<Reference<'repo>, Error>;
next(&mut self) -> Option<Result<Reference<'repo>, Error>>433     fn next(&mut self) -> Option<Result<Reference<'repo>, Error>> {
434         let mut out = ptr::null_mut();
435         unsafe {
436             try_call_iter!(raw::git_reference_next(&mut out, self.raw));
437             Some(Ok(Binding::from_raw(out)))
438         }
439     }
440 }
441 
442 impl<'repo> Drop for References<'repo> {
drop(&mut self)443     fn drop(&mut self) {
444         unsafe { raw::git_reference_iterator_free(self.raw) }
445     }
446 }
447 
448 impl<'repo, 'references> Iterator for ReferenceNames<'repo, 'references> {
449     type Item = Result<&'references str, Error>;
next(&mut self) -> Option<Result<&'references str, Error>>450     fn next(&mut self) -> Option<Result<&'references str, Error>> {
451         let mut out = ptr::null();
452         unsafe {
453             try_call_iter!(raw::git_reference_next_name(&mut out, self.inner.raw));
454             let bytes = crate::opt_bytes(self, out).unwrap();
455             let s = str::from_utf8(bytes).unwrap();
456             Some(Ok(mem::transmute::<&str, &'references str>(s)))
457         }
458     }
459 }
460 
461 #[cfg(test)]
462 mod tests {
463     use crate::{ObjectType, Reference, ReferenceType};
464 
465     #[test]
smoke()466     fn smoke() {
467         assert!(Reference::is_valid_name("refs/foo"));
468         assert!(!Reference::is_valid_name("foo"));
469     }
470 
471     #[test]
smoke2()472     fn smoke2() {
473         let (_td, repo) = crate::test::repo_init();
474         let mut head = repo.head().unwrap();
475         assert!(head.is_branch());
476         assert!(!head.is_remote());
477         assert!(!head.is_tag());
478         assert!(!head.is_note());
479 
480         // HEAD is a symbolic reference but git_repository_head resolves it
481         // so it is a GIT_REFERENCE_DIRECT.
482         assert_eq!(head.kind().unwrap(), ReferenceType::Direct);
483 
484         assert!(head == repo.head().unwrap());
485         assert_eq!(head.name(), Some("refs/heads/main"));
486 
487         assert!(head == repo.find_reference("refs/heads/main").unwrap());
488         assert_eq!(
489             repo.refname_to_id("refs/heads/main").unwrap(),
490             head.target().unwrap()
491         );
492 
493         assert!(head.symbolic_target().is_none());
494         assert!(head.target_peel().is_none());
495 
496         assert_eq!(head.shorthand(), Some("main"));
497         assert!(head.resolve().unwrap() == head);
498 
499         let mut tag1 = repo
500             .reference("refs/tags/tag1", head.target().unwrap(), false, "test")
501             .unwrap();
502         assert!(tag1.is_tag());
503         assert_eq!(tag1.kind().unwrap(), ReferenceType::Direct);
504 
505         let peeled_commit = tag1.peel(ObjectType::Commit).unwrap();
506         assert_eq!(ObjectType::Commit, peeled_commit.kind().unwrap());
507         assert_eq!(tag1.target().unwrap(), peeled_commit.id());
508 
509         tag1.delete().unwrap();
510 
511         let mut sym1 = repo
512             .reference_symbolic("refs/tags/tag1", "refs/heads/main", false, "test")
513             .unwrap();
514         assert_eq!(sym1.kind().unwrap(), ReferenceType::Symbolic);
515         sym1.delete().unwrap();
516 
517         {
518             assert!(repo.references().unwrap().count() == 1);
519             assert!(repo.references().unwrap().next().unwrap().unwrap() == head);
520             let mut names = repo.references().unwrap();
521             let mut names = names.names();
522             assert_eq!(names.next().unwrap().unwrap(), "refs/heads/main");
523             assert!(names.next().is_none());
524             assert!(repo.references_glob("foo").unwrap().count() == 0);
525             assert!(repo.references_glob("refs/heads/*").unwrap().count() == 1);
526         }
527 
528         let mut head = head.rename("refs/foo", true, "test").unwrap();
529         head.delete().unwrap();
530     }
531 }
532