Thanks to visit codestin.com
Credit goes to docs.rs

Skip to main content

git2/
reference.rs

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