1 use std
::cmp
::Ordering
;
8 use crate::object
::CastOrPanic
;
9 use crate::util
::{c_cmp_to_ordering, Binding}
;
11 raw
, Blob
, Commit
, Error
, Object
, ObjectType
, Oid
, ReferenceFormat
, ReferenceType
, Repository
,
15 // Not in the public header files (yet?), but a hard limit used by libgit2
17 const GIT_REFNAME_MAX
: usize = 1024;
19 struct Refdb
<'repo
>(&'repo Repository
);
21 /// A structure to represent a git [reference][1].
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
>>,
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
>>,
35 /// An iterator over the names of references in a repository.
36 pub struct ReferenceNames
<'repo
, 'references
> {
37 inner
: &'references
mut References
<'repo
>,
40 impl<'repo
> Reference
<'repo
> {
41 /// Ensure the reference name is well-formed.
43 /// Validation is performed as if [`ReferenceFormat::ALLOW_ONELEVEL`]
44 /// was given to [`Reference::normalize_name`]. No normalization is
45 /// performed, however.
48 /// use git2::Reference;
50 /// assert!(Reference::is_valid_name("HEAD"));
51 /// assert!(Reference::is_valid_name("refs/heads/main"));
54 /// assert!(!Reference::is_valid_name("main"));
55 /// assert!(!Reference::is_valid_name("refs/heads/*"));
56 /// assert!(!Reference::is_valid_name("foo//bar"));
59 /// [`ReferenceFormat::ALLOW_ONELEVEL`]:
60 /// struct.ReferenceFormat#associatedconstant.ALLOW_ONELEVEL
61 /// [`Reference::normalize_name`]: struct.Reference#method.normalize_name
62 pub fn is_valid_name(refname
: &str) -> bool
{
64 let refname
= CString
::new(refname
).unwrap();
65 unsafe { raw::git_reference_is_valid_name(refname.as_ptr()) == 1 }
68 /// Normalize reference name and check validity.
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:
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.
89 /// If the reference passes validation, it is returned in normalized form,
90 /// otherwise an [`Error`] with [`ErrorCode::InvalidSpec`] is returned.
93 /// use git2::{Reference, ReferenceFormat};
96 /// Reference::normalize_name(
98 /// ReferenceFormat::NORMAL
101 /// "foo/bar".to_owned()
105 /// Reference::normalize_name(
107 /// ReferenceFormat::ALLOW_ONELEVEL
110 /// "HEAD".to_owned()
114 /// Reference::normalize_name(
116 /// ReferenceFormat::REFSPEC_PATTERN
119 /// "refs/heads/*".to_owned()
123 /// Reference::normalize_name(
125 /// ReferenceFormat::ALLOW_ONELEVEL | ReferenceFormat::REFSPEC_SHORTHAND
128 /// "main".to_owned()
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
> {
142 let mut dst
= [0u8; GIT_REFNAME_MAX
];
143 let refname
= CString
::new(refname
)?
;
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
,
151 let s
= &dst
[..dst
.iter().position(|&a
| a
== 0).unwrap()];
152 Ok(str::from_utf8(s
).unwrap().to_owned())
156 /// Get access to the underlying raw pointer.
157 pub fn raw(&self) -> *mut raw
::git_reference
{
161 /// Delete an existing reference.
163 /// This method works for both direct and symbolic references. The reference
164 /// will be immediately removed on disk.
166 /// This function will return an error if the reference has changed from the
167 /// time it was looked up.
168 pub fn delete(&mut self) -> Result
<(), Error
> {
170 try_call
!(raw
::git_reference_delete(self.raw
));
175 /// Check if a reference is a local branch.
176 pub fn is_branch(&self) -> bool
{
177 unsafe { raw::git_reference_is_branch(&*self.raw) == 1 }
180 /// Check if a reference is a note.
181 pub fn is_note(&self) -> bool
{
182 unsafe { raw::git_reference_is_note(&*self.raw) == 1 }
185 /// Check if a reference is a remote tracking branch
186 pub fn is_remote(&self) -> bool
{
187 unsafe { raw::git_reference_is_remote(&*self.raw) == 1 }
190 /// Check if a reference is a tag
191 pub fn is_tag(&self) -> bool
{
192 unsafe { raw::git_reference_is_tag(&*self.raw) == 1 }
195 /// Get the reference type of a reference.
197 /// If the type is unknown, then `None` is returned.
198 pub fn kind(&self) -> Option
<ReferenceType
> {
199 ReferenceType
::from_raw(unsafe { raw::git_reference_type(&*self.raw) }
)
202 /// Get the full name of a reference.
204 /// Returns `None` if the name is not valid utf-8.
205 pub fn name(&self) -> Option
<&str> {
206 str::from_utf8(self.name_bytes()).ok()
209 /// Get the full name of a reference.
210 pub fn name_bytes(&self) -> &[u8] {
211 unsafe { crate::opt_bytes(self, raw::git_reference_name(&*self.raw)).unwrap() }
214 /// Get the full shorthand of a reference.
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.
219 /// Returns `None` if the shorthand is not valid utf-8.
220 pub fn shorthand(&self) -> Option
<&str> {
221 str::from_utf8(self.shorthand_bytes()).ok()
224 /// Get the full shorthand of a reference.
225 pub fn shorthand_bytes(&self) -> &[u8] {
226 unsafe { crate::opt_bytes(self, raw::git_reference_shorthand(&*self.raw)).unwrap() }
229 /// Get the OID pointed to by a direct reference.
231 /// Only available if the reference is direct (i.e. an object id reference,
232 /// not a symbolic one).
233 pub fn target(&self) -> Option
<Oid
> {
234 unsafe { Binding::from_raw_opt(raw::git_reference_target(&*self.raw)) }
237 /// Return the peeled OID target of this reference.
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.
241 pub fn target_peel(&self) -> Option
<Oid
> {
242 unsafe { Binding::from_raw_opt(raw::git_reference_target_peel(&*self.raw)) }
245 /// Get full name to the reference pointed to by a symbolic reference.
247 /// May return `None` if the reference is either not symbolic or not a
248 /// valid utf-8 string.
249 pub fn symbolic_target(&self) -> Option
<&str> {
250 self.symbolic_target_bytes()
251 .and_then(|s
| str::from_utf8(s
).ok())
254 /// Get full name to the reference pointed to by a symbolic reference.
256 /// Only available if the reference is symbolic.
257 pub fn symbolic_target_bytes(&self) -> Option
<&[u8]> {
258 unsafe { crate::opt_bytes(self, raw::git_reference_symbolic_target(&*self.raw)) }
261 /// Resolve a symbolic reference to a direct reference.
263 /// This method iteratively peels a symbolic reference until it resolves to
264 /// a direct reference to an OID.
266 /// If a direct reference is passed as an argument, a copy of that
267 /// reference is returned.
268 pub fn resolve(&self) -> Result
<Reference
<'repo
>, Error
> {
269 let mut raw
= ptr
::null_mut();
271 try_call
!(raw
::git_reference_resolve(&mut raw
, &*self.raw
));
272 Ok(Binding
::from_raw(raw
))
276 /// Peel a reference to an object
278 /// This method recursively peels the reference until it reaches
279 /// an object of the specified type.
280 pub fn peel(&self, kind
: ObjectType
) -> Result
<Object
<'repo
>, Error
> {
281 let mut raw
= ptr
::null_mut();
283 try_call
!(raw
::git_reference_peel(&mut raw
, self.raw
, kind
));
284 Ok(Binding
::from_raw(raw
))
288 /// Peel a reference to a blob
290 /// This method recursively peels the reference until it reaches
292 pub fn peel_to_blob(&self) -> Result
<Blob
<'repo
>, Error
> {
293 Ok(self.peel(ObjectType
::Blob
)?
.cast_or_panic(ObjectType
::Blob
))
296 /// Peel a reference to a commit
298 /// This method recursively peels the reference until it reaches
300 pub fn peel_to_commit(&self) -> Result
<Commit
<'repo
>, Error
> {
302 .peel(ObjectType
::Commit
)?
303 .cast_or_panic(ObjectType
::Commit
))
306 /// Peel a reference to a tree
308 /// This method recursively peels the reference until it reaches
310 pub fn peel_to_tree(&self) -> Result
<Tree
<'repo
>, Error
> {
311 Ok(self.peel(ObjectType
::Tree
)?
.cast_or_panic(ObjectType
::Tree
))
314 /// Peel a reference to a tag
316 /// This method recursively peels the reference until it reaches
318 pub fn peel_to_tag(&self) -> Result
<Tag
<'repo
>, Error
> {
319 Ok(self.peel(ObjectType
::Tag
)?
.cast_or_panic(ObjectType
::Tag
))
322 /// Rename an existing reference.
324 /// This method works for both direct and symbolic references.
326 /// If the force flag is not enabled, and there's already a reference with
327 /// the given name, the renaming will fail.
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
)?
;
338 try_call
!(raw
::git_reference_rename(
339 &mut raw
, self.raw
, new_name
, force
, msg
341 Ok(Binding
::from_raw(raw
))
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.
349 /// The new reference will be written to disk, overwriting the given
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
)?
;
355 try_call
!(raw
::git_reference_set_target(
361 Ok(Binding
::from_raw(raw
))
366 impl<'repo
> PartialOrd
for Reference
<'repo
> {
367 fn partial_cmp(&self, other
: &Reference
<'repo
>) -> Option
<Ordering
> {
368 Some(self.cmp(other
))
372 impl<'repo
> Ord
for Reference
<'repo
> {
373 fn cmp(&self, other
: &Reference
<'repo
>) -> Ordering
{
374 c_cmp_to_ordering(unsafe { raw::git_reference_cmp(&*self.raw, &*other.raw) }
)
378 impl<'repo
> PartialEq
for Reference
<'repo
> {
379 fn eq(&self, other
: &Reference
<'repo
>) -> bool
{
380 self.cmp(other
) == Ordering
::Equal
384 impl<'repo
> Eq
for Reference
<'repo
> {}
386 impl<'repo
> Binding
for Reference
<'repo
> {
387 type Raw
= *mut raw
::git_reference
;
388 unsafe fn from_raw(raw
: *mut raw
::git_reference
) -> Reference
<'repo
> {
391 _marker
: marker
::PhantomData
,
394 fn raw(&self) -> *mut raw
::git_reference
{
399 impl<'repo
> Drop
for Reference
<'repo
> {
401 unsafe { raw::git_reference_free(self.raw) }
405 impl<'repo
> References
<'repo
> {
406 /// Consumes a `References` iterator to create an iterator over just the
407 /// name of some references.
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.
412 /// The returned iterator will yield strings as opposed to a `Reference`.
413 pub fn names
<'a
>(&'a
mut self) -> ReferenceNames
<'repo
, 'a
> {
414 ReferenceNames { inner: self }
418 impl<'repo
> Binding
for References
<'repo
> {
419 type Raw
= *mut raw
::git_reference_iterator
;
420 unsafe fn from_raw(raw
: *mut raw
::git_reference_iterator
) -> References
<'repo
> {
423 _marker
: marker
::PhantomData
,
426 fn raw(&self) -> *mut raw
::git_reference_iterator
{
431 impl<'repo
> Iterator
for References
<'repo
> {
432 type Item
= Result
<Reference
<'repo
>, Error
>;
433 fn next(&mut self) -> Option
<Result
<Reference
<'repo
>, Error
>> {
434 let mut out
= ptr
::null_mut();
436 try_call_iter
!(raw
::git_reference_next(&mut out
, self.raw
));
437 Some(Ok(Binding
::from_raw(out
)))
442 impl<'repo
> Drop
for References
<'repo
> {
444 unsafe { raw::git_reference_iterator_free(self.raw) }
448 impl<'repo
, 'references
> Iterator
for ReferenceNames
<'repo
, 'references
> {
449 type Item
= Result
<&'references
str, Error
>;
450 fn next(&mut self) -> Option
<Result
<&'references
str, Error
>> {
451 let mut out
= ptr
::null();
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
)))
463 use crate::{ObjectType, Reference, ReferenceType}
;
467 assert
!(Reference
::is_valid_name("refs/foo"));
468 assert
!(!Reference
::is_valid_name("foo"));
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());
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
);
484 assert
!(head
== repo
.head().unwrap());
485 assert_eq
!(head
.name(), Some("refs/heads/main"));
487 assert
!(head
== repo
.find_reference("refs/heads/main").unwrap());
489 repo
.refname_to_id("refs/heads/main").unwrap(),
490 head
.target().unwrap()
493 assert
!(head
.symbolic_target().is_none());
494 assert
!(head
.target_peel().is_none());
496 assert_eq
!(head
.shorthand(), Some("main"));
497 assert
!(head
.resolve().unwrap() == head
);
500 .reference("refs/tags/tag1", head
.target().unwrap(), false, "test")
502 assert
!(tag1
.is_tag());
503 assert_eq
!(tag1
.kind().unwrap(), ReferenceType
::Direct
);
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());
509 tag1
.delete().unwrap();
512 .reference_symbolic("refs/tags/tag1", "refs/heads/main", false, "test")
514 assert_eq
!(sym1
.kind().unwrap(), ReferenceType
::Symbolic
);
515 sym1
.delete().unwrap();
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);
528 let mut head
= head
.rename("refs/foo", true, "test").unwrap();
529 head
.delete().unwrap();