check_refname_component(): return 0 for zero-length components
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index d40ab63e913a1e34131a84ae5e53eea720a8f53a..1f6890bf486fcb3b9022ab55f35aa63edbe56bab 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -51,7 +51,7 @@ static int check_refname_component(const char *refname, int flags)
                last = ch;
        }
        if (cp == refname)
-               return -1; /* Component has zero length. */
+               return 0; /* Component has zero length. */
        if (refname[0] == '.') {
                if (!(flags & REFNAME_DOT_COMPONENT))
                        return -1; /* Component starts with '.'. */
@@ -74,7 +74,7 @@ int check_refname_format(const char *refname, int flags)
        while (1) {
                /* We are at the start of a path component. */
                component_len = check_refname_component(refname, flags);
-               if (component_len < 0) {
+               if (component_len <= 0) {
                        if ((flags & REFNAME_REFSPEC_PATTERN) &&
                                        refname[0] == '*' &&
                                        (refname[1] == '\0' || refname[1] == '/')) {
@@ -145,6 +145,11 @@ static struct ref_entry *create_ref_entry(const char *refname,
        return ref;
 }
 
+static void free_ref_entry(struct ref_entry *entry)
+{
+       free(entry);
+}
+
 /* Add a ref_entry to the end of the ref_array (unsorted). */
 static void add_ref(struct ref_array *refs, struct ref_entry *ref)
 {
@@ -156,7 +161,7 @@ static void clear_ref_array(struct ref_array *array)
 {
        int i;
        for (i = 0; i < array->nr; i++)
-               free(array->refs[i]);
+               free_ref_entry(array->refs[i]);
        free(array->refs);
        array->sorted = array->nr = array->alloc = 0;
        array->refs = NULL;
@@ -235,7 +240,7 @@ static void sort_ref_array(struct ref_array *array)
        i = 0;
        for (j = 1; j < array->nr; j++) {
                if (is_dup_ref(array->refs[i], array->refs[j])) {
-                       free(array->refs[j]);
+                       free_ref_entry(array->refs[j]);
                        continue;
                }
                array->refs[++i] = array->refs[j];
@@ -342,41 +347,53 @@ static int do_for_each_ref_in_arrays(struct ref_array *array1,
  */
 static int names_conflict(const char *refname1, const char *refname2)
 {
-       int len1 = strlen(refname1);
-       int len2 = strlen(refname2);
-       int cmplen;
-       const char *lead;
+       for (; *refname1 && *refname1 == *refname2; refname1++, refname2++)
+               ;
+       return (*refname1 == '\0' && *refname2 == '/')
+               || (*refname1 == '/' && *refname2 == '\0');
+}
 
-       if (len1 < len2) {
-               cmplen = len1;
-               lead = refname2;
-       } else {
-               cmplen = len2;
-               lead = refname1;
+struct name_conflict_cb {
+       const char *refname;
+       const char *oldrefname;
+       const char *conflicting_refname;
+};
+
+static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,
+                           int flags, void *cb_data)
+{
+       struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
+       if (data->oldrefname && !strcmp(data->oldrefname, existingrefname))
+               return 0;
+       if (names_conflict(data->refname, existingrefname)) {
+               data->conflicting_refname = existingrefname;
+               return 1;
        }
-       return !strncmp(refname1, refname2, cmplen) && lead[cmplen] == '/';
+       return 0;
 }
 
 /*
  * Return true iff a reference named refname could be created without
- * conflicting with the name of an existing reference.  If oldrefname
- * is non-NULL, ignore potential conflicts with oldrefname (e.g.,
- * because oldrefname is scheduled for deletion in the same
+ * conflicting with the name of an existing reference in array.  If
+ * oldrefname is non-NULL, ignore potential conflicts with oldrefname
+ * (e.g., because oldrefname is scheduled for deletion in the same
  * operation).
  */
 static int is_refname_available(const char *refname, const char *oldrefname,
                                struct ref_array *array)
 {
-       int i;
-       for (i = 0; i < array->nr; i++) {
-               struct ref_entry *entry = array->refs[i];
-               if (oldrefname && !strcmp(oldrefname, entry->name))
-                       continue;
-               if (names_conflict(refname, entry->name)) {
-                       error("'%s' exists; cannot create '%s'",
-                             entry->name, refname);
-                       return 0;
-               }
+       struct name_conflict_cb data;
+       data.refname = refname;
+       data.oldrefname = oldrefname;
+       data.conflicting_refname = NULL;
+
+       sort_ref_array(array);
+       if (do_for_each_ref_in_array(array, 0, "", name_conflict_fn,
+                                    0, DO_FOR_EACH_INCLUDE_BROKEN,
+                                    &data)) {
+               error("'%s' exists; cannot create '%s'",
+                     data.conflicting_refname, refname);
+               return 0;
        }
        return 1;
 }