Merge branch 'sb/refs-code-cleanup'
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 92d801c00a0878760f2d6aa8d127c4f228813733..5e5a3824b992132aaac8d86698feebcb773e13c6 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -72,6 +72,10 @@ int check_refname_format(const char *refname, int flags)
 {
        int component_len, component_count = 0;
 
+       if (!strcmp(refname, "@"))
+               /* Refname is a single character '@'. */
+               return -1;
+
        while (1) {
                /* We are at the start of a path component. */
                component_len = check_refname_component(refname, flags);
@@ -943,13 +947,6 @@ static struct ref_cache *get_ref_cache(const char *submodule)
        return refs;
 }
 
-void invalidate_ref_cache(const char *submodule)
-{
-       struct ref_cache *refs = get_ref_cache(submodule);
-       clear_packed_ref_cache(refs);
-       clear_loose_ref_cache(refs);
-}
-
 /* The length of a peeled reference line in packed-refs, including EOL: */
 #define PEELED_LINE_LENGTH 42
 
@@ -1951,7 +1948,7 @@ static int remove_empty_directories(const char *file)
 static char *substitute_branch_name(const char **string, int *len)
 {
        struct strbuf buf = STRBUF_INIT;
-       int ret = interpret_branch_name(*string, &buf);
+       int ret = interpret_branch_name(*string, *len, &buf);
 
        if (ret == *len) {
                size_t size;
@@ -3231,18 +3228,110 @@ int update_ref(const char *action, const char *refname,
               int flags, enum action_on_err onerr)
 {
        struct ref_lock *lock;
-       lock = update_ref_lock(refname, oldval, flags, 0, onerr);
+       lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
        if (!lock)
                return 1;
        return update_ref_write(action, refname, sha1, lock, onerr);
 }
 
-struct ref *find_ref_by_name(const struct ref *list, const char *name)
+static int ref_update_compare(const void *r1, const void *r2)
 {
-       for ( ; list; list = list->next)
-               if (!strcmp(list->name, name))
-                       return (struct ref *)list;
-       return NULL;
+       const struct ref_update * const *u1 = r1;
+       const struct ref_update * const *u2 = r2;
+       return strcmp((*u1)->ref_name, (*u2)->ref_name);
+}
+
+static int ref_update_reject_duplicates(struct ref_update **updates, int n,
+                                       enum action_on_err onerr)
+{
+       int i;
+       for (i = 1; i < n; i++)
+               if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) {
+                       const char *str =
+                               "Multiple updates for ref '%s' not allowed.";
+                       switch (onerr) {
+                       case MSG_ON_ERR:
+                               error(str, updates[i]->ref_name); break;
+                       case DIE_ON_ERR:
+                               die(str, updates[i]->ref_name); break;
+                       case QUIET_ON_ERR:
+                               break;
+                       }
+                       return 1;
+               }
+       return 0;
+}
+
+int update_refs(const char *action, const struct ref_update **updates_orig,
+               int n, enum action_on_err onerr)
+{
+       int ret = 0, delnum = 0, i;
+       struct ref_update **updates;
+       int *types;
+       struct ref_lock **locks;
+       const char **delnames;
+
+       if (!updates_orig || !n)
+               return 0;
+
+       /* Allocate work space */
+       updates = xmalloc(sizeof(*updates) * n);
+       types = xmalloc(sizeof(*types) * n);
+       locks = xcalloc(n, sizeof(*locks));
+       delnames = xmalloc(sizeof(*delnames) * n);
+
+       /* Copy, sort, and reject duplicate refs */
+       memcpy(updates, updates_orig, sizeof(*updates) * n);
+       qsort(updates, n, sizeof(*updates), ref_update_compare);
+       ret = ref_update_reject_duplicates(updates, n, onerr);
+       if (ret)
+               goto cleanup;
+
+       /* Acquire all locks while verifying old values */
+       for (i = 0; i < n; i++) {
+               locks[i] = update_ref_lock(updates[i]->ref_name,
+                                          (updates[i]->have_old ?
+                                           updates[i]->old_sha1 : NULL),
+                                          updates[i]->flags,
+                                          &types[i], onerr);
+               if (!locks[i]) {
+                       ret = 1;
+                       goto cleanup;
+               }
+       }
+
+       /* Perform updates first so live commits remain referenced */
+       for (i = 0; i < n; i++)
+               if (!is_null_sha1(updates[i]->new_sha1)) {
+                       ret = update_ref_write(action,
+                                              updates[i]->ref_name,
+                                              updates[i]->new_sha1,
+                                              locks[i], onerr);
+                       locks[i] = NULL; /* freed by update_ref_write */
+                       if (ret)
+                               goto cleanup;
+               }
+
+       /* Perform deletes now that updates are safely completed */
+       for (i = 0; i < n; i++)
+               if (locks[i]) {
+                       delnames[delnum++] = locks[i]->ref_name;
+                       ret |= delete_ref_loose(locks[i], types[i]);
+               }
+       ret |= repack_without_refs(delnames, delnum);
+       for (i = 0; i < delnum; i++)
+               unlink_or_warn(git_path("logs/%s", delnames[i]));
+       clear_loose_ref_cache(&ref_cache);
+
+cleanup:
+       for (i = 0; i < n; i++)
+               if (locks[i])
+                       unlock_ref(locks[i]);
+       free(updates);
+       free(types);
+       free(locks);
+       free(delnames);
+       return ret;
 }
 
 /*
@@ -3280,7 +3369,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                size_t total_len = 0;
 
                /* the rule list is NULL terminated, count them first */
-               for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
+               for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)
                        /* no +1 because strlen("%s") < strlen("%.*s") */
                        total_len += strlen(ref_rev_parse_rules[nr_rules]);