@@ -8,9 +8,14 @@ use std::str;
8
8
use crate :: object:: CastOrPanic ;
9
9
use crate :: util:: { c_cmp_to_ordering, Binding } ;
10
10
use 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 ,
12
13
} ;
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
+
14
19
struct Refdb < ' repo > ( & ' repo Repository ) ;
15
20
16
21
/// A structure to represent a git [reference][1].
@@ -34,12 +39,120 @@ pub struct ReferenceNames<'repo, 'references> {
34
39
35
40
impl < ' repo > Reference < ' repo > {
36
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/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
37
62
pub fn is_valid_name ( refname : & str ) -> bool {
38
63
crate :: init ( ) ;
39
64
let refname = CString :: new ( refname) . unwrap ( ) ;
40
65
unsafe { raw:: git_reference_is_valid_name ( refname. as_ptr ( ) ) == 1 }
41
66
}
42
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, "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
+
43
156
/// Get access to the underlying raw pointer.
44
157
pub fn raw ( & self ) -> * mut raw:: git_reference {
45
158
self . raw
0 commit comments