Commit c030ada7 by nulltoken

refs: make git_reference_normalize_name() accept refspec pattern

parent d75074f4
...@@ -414,8 +414,6 @@ enum { ...@@ -414,8 +414,6 @@ enum {
* Once normalized, if the reference name is valid, it will be * Once normalized, if the reference name is valid, it will be
* returned in the user allocated buffer. * returned in the user allocated buffer.
* *
* TODO: Implement handling of GIT_REF_FORMAT_REFSPEC_PATTERN
*
* @param buffer_out The user allocated buffer where the * @param buffer_out The user allocated buffer where the
* normalized name will be stored. * normalized name will be stored.
* *
......
...@@ -1098,7 +1098,7 @@ int git_reference_lookup_resolved( ...@@ -1098,7 +1098,7 @@ int git_reference_lookup_resolved(
scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char)); scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));
GITERR_CHECK_ALLOC(scan->name); GITERR_CHECK_ALLOC(scan->name);
if ((result = git_reference__normalize_name( if ((result = git_reference__normalize_name_lax(
scan->name, scan->name,
GIT_REFNAME_MAX, GIT_REFNAME_MAX,
name)) < 0) { name)) < 0) {
...@@ -1200,7 +1200,7 @@ int git_reference_create_symbolic( ...@@ -1200,7 +1200,7 @@ int git_reference_create_symbolic(
char normalized[GIT_REFNAME_MAX]; char normalized[GIT_REFNAME_MAX];
git_reference *ref = NULL; git_reference *ref = NULL;
if (git_reference__normalize_name( if (git_reference__normalize_name_lax(
normalized, normalized,
sizeof(normalized), sizeof(normalized),
name) < 0) name) < 0)
...@@ -1322,7 +1322,7 @@ int git_reference_set_target(git_reference *ref, const char *target) ...@@ -1322,7 +1322,7 @@ int git_reference_set_target(git_reference *ref, const char *target)
return -1; return -1;
} }
if (git_reference__normalize_name( if (git_reference__normalize_name_lax(
normalized, normalized,
sizeof(normalized), sizeof(normalized),
target)) target))
...@@ -1584,106 +1584,148 @@ static int is_valid_ref_char(char ch) ...@@ -1584,106 +1584,148 @@ static int is_valid_ref_char(char ch)
} }
} }
int git_reference_normalize_name( static int ensure_segment_validity(const char *name)
char *buffer_out,
size_t buffer_size,
const char *name,
unsigned int flags)
{ {
const char *name_end, *buffer_out_start; const char *current = name;
const char *current; char prev = '\0';
int contains_a_slash = 0;
assert(name && buffer_out); if (*current == '.')
return -1; /* Refname starts with "." */
if (flags & GIT_REF_FORMAT_REFSPEC_PATTERN) { for (current = name; ; current++) {
giterr_set(GITERR_INVALID, "Unimplemented"); if (*current == '\0' || *current == '/')
return -1; break;
}
buffer_out_start = buffer_out; if (!is_valid_ref_char(*current))
current = name; return -1; /* Illegal character in refname */
name_end = name + strlen(name);
/* Terminating null byte */ if (prev == '.' && *current == '.')
buffer_size--; return -1; /* Refname contains ".." */
/* A refname can not be empty */ if (prev == '@' && *current == '{')
if (name_end == name) return -1; /* Refname contains "@{" */
goto invalid_name;
/* A refname can not end with a dot or a slash */ prev = *current;
if (*(name_end - 1) == '.' || *(name_end - 1) == '/') }
goto invalid_name;
while (current < name_end && buffer_size > 0) { return current - name;
if (!is_valid_ref_char(*current)) }
goto invalid_name;
int git_reference__normalize_name(
git_buf *buf,
const char *name,
unsigned int flags)
{
// Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
if (buffer_out > buffer_out_start) { char *current;
char prev = *(buffer_out - 1); int segment_len, segments_count = 0, error = -1;
assert(name && buf);
current = (char *)name;
git_buf_clear(buf);
while (true) {
segment_len = ensure_segment_validity(current);
if (segment_len < 0) {
if ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
current[0] == '*' &&
(current[1] == '\0' || current[1] == '/')) {
/* Accept one wildcard as a full refname component. */
flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
segment_len = 1;
} else
goto cleanup;
}
/* A refname can not start with a dot nor contain a double dot */ if (segment_len > 0) {
if (*current == '.' && ((prev == '.') || (prev == '/'))) int cur_len = git_buf_len(buf);
goto invalid_name;
/* '@{' is forbidden within a refname */ git_buf_joinpath(buf, git_buf_cstr(buf), current);
if (*current == '{' && prev == '@') git_buf_truncate(buf,
goto invalid_name; cur_len + segment_len + (segments_count ? 1 : 0));
/* Prevent multiple slashes from being added to the output */ segments_count++;
if (*current == '/' && prev == '/') {
current++;
continue;
}
}
if (*current == '/') { if (git_buf_oom(buf))
if (buffer_out > buffer_out_start) goto cleanup;
contains_a_slash = 1;
else {
current++;
continue;
}
} }
*buffer_out++ = *current++; if (current[segment_len] == '\0')
buffer_size--; break;
}
if (current < name_end) { current += segment_len + 1;
giterr_set(
GITERR_REFERENCE,
"The provided buffer is too short to hold the normalization of '%s'", name);
return GIT_EBUFS;
} }
/* A refname can not be empty */
if (git_buf_len(buf) == 0)
goto cleanup;
/* A refname can not end with "." */
if (current[segment_len - 1] == '.')
goto cleanup;
/* A refname can not end with "/" */
if (current[segment_len - 1] == '/')
goto cleanup;
/* A refname can not end with ".lock" */
if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
goto cleanup;
/* Object id refname have to contain at least one slash, except /* Object id refname have to contain at least one slash, except
* for HEAD in a detached state or MERGE_HEAD if we're in the * for HEAD in a detached state or MERGE_HEAD if we're in the
* middle of a merge */ * middle of a merge */
if (!(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL) && if (!(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL) &&
!contains_a_slash && segments_count < 2 &&
strcmp(name, GIT_HEAD_FILE) != 0 && strcmp(name, GIT_HEAD_FILE) != 0 &&
strcmp(name, GIT_MERGE_HEAD_FILE) != 0 && strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
strcmp(name, GIT_FETCH_HEAD_FILE) != 0) strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
goto invalid_name; return -1;
/* A refname can not end with ".lock" */ error = 0;
if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
goto invalid_name;
*buffer_out = '\0'; cleanup:
if (error)
giterr_set(
GITERR_REFERENCE,
"The given reference name '%s' is not valid", name);
return 0; return error;
}
invalid_name: int git_reference_normalize_name(
giterr_set( char *buffer_out,
size_t buffer_size,
const char *name,
unsigned int flags)
{
git_buf buf = GIT_BUF_INIT;
int error;
if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
goto cleanup;
if (git_buf_len(&buf) > buffer_size - 1) {
giterr_set(
GITERR_REFERENCE, GITERR_REFERENCE,
"The given reference name '%s' is not valid", name); "The provided buffer is too short to hold the normalization of '%s'", name);
return -1; error = GIT_EBUFS;
goto cleanup;
}
git_buf_copy_cstr(buffer_out, buffer_size, &buf);
error = 0;
cleanup:
git_buf_free(&buf);
return error;
} }
int git_reference__normalize_name( int git_reference__normalize_name_lax(
char *buffer_out, char *buffer_out,
size_t out_size, size_t out_size,
const char *name) const char *name)
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "git2/oid.h" #include "git2/oid.h"
#include "git2/refs.h" #include "git2/refs.h"
#include "strmap.h" #include "strmap.h"
#include "buffer.h"
#define GIT_REFS_DIR "refs/" #define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
...@@ -52,8 +53,9 @@ typedef struct { ...@@ -52,8 +53,9 @@ typedef struct {
void git_repository__refcache_free(git_refcache *refs); void git_repository__refcache_free(git_refcache *refs);
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name); int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name); int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name); int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name);
/** /**
......
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
#include "reflog.h" #include "reflog.h"
// Helpers // Helpers
static void ensure_refname_normalized(unsigned int flags, static void ensure_refname_normalized(
const char *input_refname, unsigned int flags,
const char *expected_refname) const char *input_refname,
const char *expected_refname)
{ {
char buffer_out[GIT_REFNAME_MAX]; char buffer_out[GIT_REFNAME_MAX];
...@@ -115,7 +116,7 @@ void test_refs_normalize__symbolic(void) ...@@ -115,7 +116,7 @@ void test_refs_normalize__symbolic(void)
* See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739 */ * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739 */
void test_refs_normalize__jgit_suite(void) void test_refs_normalize__jgit_suite(void)
{ {
// tests borrowed from JGit // tests borrowed from JGit
/* EmptyString */ /* EmptyString */
ensure_refname_invalid( ensure_refname_invalid(
...@@ -314,3 +315,57 @@ void test_refs_normalize__buffer_has_to_be_big_enough_to_hold_the_normalized_ver ...@@ -314,3 +315,57 @@ void test_refs_normalize__buffer_has_to_be_big_enough_to_hold_the_normalized_ver
cl_git_fail(git_reference_normalize_name( cl_git_fail(git_reference_normalize_name(
buffer_out, 20, "//refs//heads/long///name", GIT_REF_FORMAT_NORMAL)); buffer_out, 20, "//refs//heads/long///name", GIT_REF_FORMAT_NORMAL));
} }
#define ONE_LEVEL_AND_REFSPEC \
GIT_REF_FORMAT_ALLOW_ONELEVEL \
| GIT_REF_FORMAT_REFSPEC_PATTERN
void test_refs_normalize__refspec_pattern(void)
{
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/*foo/bar");
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/foo*/bar");
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/f*o/bar");
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "foo");
ensure_refname_normalized(
ONE_LEVEL_AND_REFSPEC, "foo", "foo");
ensure_refname_normalized(
GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/bar", "foo/bar");
ensure_refname_normalized(
ONE_LEVEL_AND_REFSPEC, "foo/bar", "foo/bar");
ensure_refname_normalized(
GIT_REF_FORMAT_REFSPEC_PATTERN, "*/foo", "*/foo");
ensure_refname_normalized(
ONE_LEVEL_AND_REFSPEC, "*/foo", "*/foo");
ensure_refname_normalized(
GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/*/bar", "foo/*/bar");
ensure_refname_normalized(
ONE_LEVEL_AND_REFSPEC, "foo/*/bar", "foo/*/bar");
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "*");
ensure_refname_normalized(
ONE_LEVEL_AND_REFSPEC, "*", "*");
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/*/*");
ensure_refname_invalid(
ONE_LEVEL_AND_REFSPEC, "foo/*/*");
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "*/foo/*");
ensure_refname_invalid(
ONE_LEVEL_AND_REFSPEC, "*/foo/*");
ensure_refname_invalid(
GIT_REF_FORMAT_REFSPEC_PATTERN, "*/*/foo");
ensure_refname_invalid(
ONE_LEVEL_AND_REFSPEC, "*/*/foo");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment