diff --git a/libgit2-sys/lib.rs b/libgit2-sys/lib.rs index a113a29526..226d6d6ea0 100644 --- a/libgit2-sys/lib.rs +++ b/libgit2-sys/lib.rs @@ -2247,6 +2247,7 @@ extern "C" { ) -> c_int; pub fn git_remote_get_refspec(remote: *const git_remote, n: size_t) -> *const git_refspec; pub fn git_remote_is_valid_name(remote_name: *const c_char) -> c_int; + pub fn git_remote_name_is_valid(valid: *mut c_int, remote_name: *const c_char) -> c_int; pub fn git_remote_list(out: *mut git_strarray, repo: *mut git_repository) -> c_int; pub fn git_remote_rename( problems: *mut git_strarray, @@ -2398,6 +2399,7 @@ extern "C" { pub fn git_reference_is_remote(r: *const git_reference) -> c_int; pub fn git_reference_is_tag(r: *const git_reference) -> c_int; pub fn git_reference_is_valid_name(name: *const c_char) -> c_int; + pub fn git_reference_name_is_valid(valid: *mut c_int, refname: *const c_char) -> c_int; pub fn git_reference_lookup( out: *mut *mut git_reference, repo: *mut git_repository, @@ -3209,6 +3211,7 @@ extern "C" { pub fn git_tag_target(target_out: *mut *mut git_object, tag: *const git_tag) -> c_int; pub fn git_tag_target_id(tag: *const git_tag) -> *const git_oid; pub fn git_tag_target_type(tag: *const git_tag) -> git_object_t; + pub fn git_tag_name_is_valid(valid: *mut c_int, tag_name: *const c_char) -> c_int; // checkout pub fn git_checkout_head(repo: *mut git_repository, opts: *const git_checkout_options) diff --git a/src/reference.rs b/src/reference.rs index 479252a5cc..2af92edde4 100644 --- a/src/reference.rs +++ b/src/reference.rs @@ -8,8 +8,8 @@ use std::str; use crate::object::CastOrPanic; use crate::util::{c_cmp_to_ordering, Binding}; use crate::{ - raw, Blob, Commit, Error, Object, ObjectType, Oid, ReferenceFormat, ReferenceType, Repository, - Tag, Tree, + call, raw, Blob, Commit, Error, Object, ObjectType, Oid, ReferenceFormat, ReferenceType, + Repository, Tag, Tree, }; // Not in the public header files (yet?), but a hard limit used by libgit2 @@ -62,7 +62,15 @@ impl<'repo> Reference<'repo> { pub fn is_valid_name(refname: &str) -> bool { crate::init(); let refname = CString::new(refname).unwrap(); - unsafe { raw::git_reference_is_valid_name(refname.as_ptr()) == 1 } + let mut valid: libc::c_int = 0; + unsafe { + call::c_try(raw::git_reference_name_is_valid( + &mut valid, + refname.as_ptr(), + )) + .unwrap(); + } + valid == 1 } /// Normalize reference name and check validity. @@ -463,13 +471,23 @@ mod tests { use crate::{ObjectType, Reference, ReferenceType}; #[test] - fn smoke() { + fn is_valid_name() { assert!(Reference::is_valid_name("refs/foo")); assert!(!Reference::is_valid_name("foo")); + assert!(Reference::is_valid_name("FOO_BAR")); + + assert!(!Reference::is_valid_name("foo")); + assert!(!Reference::is_valid_name("_FOO_BAR")); } #[test] - fn smoke2() { + #[should_panic] + fn is_valid_name_for_invalid_ref() { + Reference::is_valid_name("ab\012"); + } + + #[test] + fn smoke() { let (_td, repo) = crate::test::repo_init(); let mut head = repo.head().unwrap(); assert!(head.is_branch()); diff --git a/src/remote.rs b/src/remote.rs index d2a73858f7..7418d1159d 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -10,7 +10,7 @@ use std::{ffi::CString, os::raw::c_char}; use crate::string_array::StringArray; use crate::util::Binding; -use crate::{raw, Buf, Direction, Error, FetchPrune, Oid, ProxyOptions, Refspec}; +use crate::{call, raw, Buf, Direction, Error, FetchPrune, Oid, ProxyOptions, Refspec}; use crate::{AutotagOption, Progress, RemoteCallbacks, Repository}; /// A structure representing a [remote][1] of a git repository. @@ -92,7 +92,15 @@ impl<'repo> Remote<'repo> { pub fn is_valid_name(remote_name: &str) -> bool { crate::init(); let remote_name = CString::new(remote_name).unwrap(); - unsafe { raw::git_remote_is_valid_name(remote_name.as_ptr()) == 1 } + let mut valid: libc::c_int = 0; + unsafe { + call::c_try(raw::git_remote_name_is_valid( + &mut valid, + remote_name.as_ptr(), + )) + .unwrap(); + } + valid == 1 } /// Create a detached remote @@ -851,11 +859,17 @@ mod tests { } #[test] - fn is_valid() { + fn is_valid_name() { assert!(Remote::is_valid_name("foobar")); assert!(!Remote::is_valid_name("\x01")); } + #[test] + #[should_panic] + fn is_valid_name_for_invalid_remote() { + Remote::is_valid_name("ab\012"); + } + #[test] fn transfer_cb() { let (td, _repo) = crate::test::repo_init(); diff --git a/src/tag.rs b/src/tag.rs index 8cd328fdd2..6986c7c160 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -1,10 +1,11 @@ +use std::ffi::CString; use std::marker; use std::mem; use std::ptr; use std::str; use crate::util::Binding; -use crate::{raw, signature, Error, Object, ObjectType, Oid, Signature}; +use crate::{call, raw, signature, Error, Object, ObjectType, Oid, Signature}; /// A structure to represent a git [tag][1] /// @@ -15,6 +16,19 @@ pub struct Tag<'repo> { } impl<'repo> Tag<'repo> { + /// Determine whether a tag name is valid, meaning that (when prefixed with refs/tags/) that + /// it is a valid reference name, and that any additional tag name restrictions are imposed + /// (eg, it cannot start with a -). + pub fn is_valid_name(tag_name: &str) -> bool { + crate::init(); + let tag_name = CString::new(tag_name).unwrap(); + let mut valid: libc::c_int = 0; + unsafe { + call::c_try(raw::git_tag_name_is_valid(&mut valid, tag_name.as_ptr())).unwrap(); + } + valid == 1 + } + /// Get the id (SHA1) of a repository tag pub fn id(&self) -> Oid { unsafe { Binding::from_raw(raw::git_tag_id(&*self.raw)) } @@ -141,6 +155,30 @@ impl<'repo> Drop for Tag<'repo> { #[cfg(test)] mod tests { + use crate::Tag; + + // Reference -- https://git-scm.com/docs/git-check-ref-format + #[test] + fn name_is_valid() { + assert_eq!(Tag::is_valid_name("blah_blah"), true); + assert_eq!(Tag::is_valid_name("v1.2.3"), true); + assert_eq!(Tag::is_valid_name("my/tag"), true); + assert_eq!(Tag::is_valid_name("@"), true); + + assert_eq!(Tag::is_valid_name("-foo"), false); + assert_eq!(Tag::is_valid_name("foo:bar"), false); + assert_eq!(Tag::is_valid_name("foo^bar"), false); + assert_eq!(Tag::is_valid_name("foo."), false); + assert_eq!(Tag::is_valid_name("@{"), false); + assert_eq!(Tag::is_valid_name("as\\cd"), false); + } + + #[test] + #[should_panic] + fn is_valid_name_for_invalid_tag() { + Tag::is_valid_name("ab\012"); + } + #[test] fn smoke() { let (_td, repo) = crate::test::repo_init();