@@ -8,9 +8,14 @@ use std::str;
88use crate :: object:: CastOrPanic ;
99use crate :: util:: { c_cmp_to_ordering, Binding } ;
1010use crate :: {
11- raw, Blob , Commit , Error , Object , ObjectType , Oid , ReferenceType , Repository , Tag , Tree ,
11+ raw, Blob , Commit , Error , Object , ObjectType , Oid , ReferenceFormat , ReferenceType , Repository ,
12+ Tag , Tree ,
1213} ;
1314
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+
1419struct Refdb < ' repo > ( & ' repo Repository ) ;
1520
1621/// A structure to represent a git [reference][1].
@@ -34,12 +39,120 @@ pub struct ReferenceNames<'repo, 'references> {
3439
3540impl < ' repo > Reference < ' repo > {
3641 /// 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/master"));
52+ ///
53+ /// // But:
54+ /// assert!(!Reference::is_valid_name("master"));
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
3762 pub fn is_valid_name ( refname : & str ) -> bool {
3863 crate :: init ( ) ;
3964 let refname = CString :: new ( refname) . unwrap ( ) ;
4065 unsafe { raw:: git_reference_is_valid_name ( refname. as_ptr ( ) ) == 1 }
4166 }
4267
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, "master" 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+ /// "master",
125+ /// ReferenceFormat::ALLOW_ONELEVEL | ReferenceFormat::REFSPEC_SHORTHAND
126+ /// )
127+ /// .unwrap(),
128+ /// "master".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
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+
43156 /// Get access to the underlying raw pointer.
44157 pub fn raw ( & self ) -> * mut raw:: git_reference {
45158 self . raw
0 commit comments