summaryrefslogtreecommitdiff
path: root/src/refs.c
diff options
context:
space:
mode:
authornulltoken <emeric.fermas@gmail.com>2011-02-10 15:08:00 +0100
committerVicent Marti <tanoku@gmail.com>2011-03-03 20:23:48 +0200
commitaa2120e9da45017c6fe0126d6e9b1ee20ff40037 (patch)
treebfd83227689fc95eb5fdfa4958d2066c121ee425 /src/refs.c
parent83403e99bae5fe415f30cc834e66c2e589ca46e0 (diff)
downloadlibgit2-aa2120e9da45017c6fe0126d6e9b1ee20ff40037.tar.gz
Added git_reference__normalize_name() along with tests.
Diffstat (limited to 'src/refs.c')
-rw-r--r--src/refs.c104
1 files changed, 100 insertions, 4 deletions
diff --git a/src/refs.c b/src/refs.c
index b95ec70cf..46589e04d 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -571,12 +571,13 @@ error_cleanup:
int git_repository_lookup_ref(git_reference **ref_out, git_repository *repo, const char *name)
{
int error;
+ char normalized_name[GIT_PATH_MAX];
assert(ref_out && repo && name);
*ref_out = NULL;
- error = check_refname(name);
+ error = git_reference__normalize_name(normalized_name, name, GIT_REF_ANY);
if (error < GIT_SUCCESS)
return error;
@@ -584,7 +585,7 @@ int git_repository_lookup_ref(git_reference **ref_out, git_repository *repo, con
* First, check if the reference is on the local cache;
* references on the cache are assured to be up-to-date
*/
- *ref_out = git_hashtable_lookup(repo->references.cache, name);
+ *ref_out = git_hashtable_lookup(repo->references.cache, normalized_name);
if (*ref_out != NULL)
return GIT_SUCCESS;
@@ -593,7 +594,7 @@ int git_repository_lookup_ref(git_reference **ref_out, git_repository *repo, con
* If the file exists, we parse it and store it on the
* cache.
*/
- error = lookup_loose_ref(ref_out, repo, name);
+ error = lookup_loose_ref(ref_out, repo, normalized_name);
if (error == GIT_SUCCESS)
return GIT_SUCCESS;
@@ -618,7 +619,7 @@ int git_repository_lookup_ref(git_reference **ref_out, git_repository *repo, con
return error;
/* check the cache again -- hopefully the reference will be there */
- *ref_out = git_hashtable_lookup(repo->references.cache, name);
+ *ref_out = git_hashtable_lookup(repo->references.cache, normalized_name);
if (*ref_out != NULL)
return GIT_SUCCESS;
}
@@ -653,4 +654,99 @@ void git_repository__refcache_free(git_refcache *refs)
git_hashtable_free(refs->cache);
}
+static int check_valid_ref_char(char ch)
+{
+ if (ch <= ' ')
+ return GIT_ERROR;
+
+ switch (ch) {
+ case '~':
+ case '^':
+ case ':':
+ case '\\':
+ case '?':
+ case '[':
+ return GIT_ERROR;
+ break;
+
+ default:
+ return GIT_SUCCESS;
+ }
+}
+
+int git_reference__normalize_name(char *buffer_out, const char *name, git_rtype type)
+{
+ int error = GIT_SUCCESS;
+ const char *name_end, *buffer_out_start;
+ char *current;
+ int contains_a_slash = 0;
+
+ assert(name && buffer_out);
+
+ buffer_out_start = buffer_out;
+ current = (char *)name;
+ name_end = name + strlen(name);
+
+ if (type == GIT_REF_INVALID)
+ return GIT_EINVALIDTYPE;
+
+ /* A refname can not be empty */
+ if (name_end == name)
+ return GIT_EINVALIDREFNAME;
+
+ /* A refname can not end with a dot or a slash */
+ if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
+ return GIT_EINVALIDREFNAME;
+
+ while (current < name_end) {
+ if (check_valid_ref_char(*current))
+ return GIT_EINVALIDREFNAME;
+
+ if (buffer_out > buffer_out_start) {
+ char prev = *(buffer_out - 1);
+
+ /* A refname can not start with a dot nor contain a double dot */
+ if (*current == '.' && ((prev == '.') || (prev == '/')))
+ return GIT_EINVALIDREFNAME;
+
+ /* '@{' is forbidden within a refname */
+ if (*current == '{' && prev == '@')
+ return GIT_EINVALIDREFNAME;
+
+ /* Prevent multiple slashes from being added to the output */
+ if (*current == '/' && prev == '/') {
+ current++;
+ continue;
+ }
+ }
+
+ if (*current == '/') {
+ /* Slashes are not authorized in symbolic reference name */
+ if (type == GIT_REF_SYMBOLIC) {
+ return GIT_EINVALIDREFNAME;
+ }
+
+ contains_a_slash = 1;
+ }
+
+ *buffer_out++ = *current++;
+ }
+
+ /* Object id refname have to contain at least one slash */
+ if (type == GIT_REF_OID && !contains_a_slash)
+ return GIT_EINVALIDREFNAME;
+
+ /* A refname can not end with ".lock" */
+ if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
+ return GIT_EINVALIDREFNAME;
+
+ *buffer_out = '\0';
+
+ /* For object id references, name has to start with refs/(heads|tags|remotes) */
+ if (type == GIT_REF_OID && !(!git__prefixcmp(buffer_out_start, GIT_REFS_HEADS_DIR) ||
+ !git__prefixcmp(buffer_out_start, GIT_REFS_TAGS_DIR) || !git__prefixcmp(buffer_out_start, GIT_REFS_REMOTES_DIR)))
+ return GIT_EINVALIDREFNAME;
+
+ return error;
+}