]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'bk/refs-multi-update'
authorJunio C Hamano <gitster@pobox.com>
Fri, 20 Sep 2013 19:36:12 +0000 (12:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 20 Sep 2013 19:36:12 +0000 (12:36 -0700)
Give "update-refs" a "--stdin" option to read multiple update
requests and perform them in an all-or-none fashion.

* bk/refs-multi-update:
  update-ref: add test cases covering --stdin signature
  update-ref: support multiple simultaneous updates
  refs: add update_refs for multiple simultaneous updates
  refs: add function to repack without multiple refs
  refs: factor delete_ref loose ref step into a helper
  refs: factor update_ref steps into helpers
  refs: report ref type from lock_any_ref_for_update
  reset: rename update_refs to reset_refs

1  2 
branch.c
builtin/commit.c
builtin/fetch.c
builtin/receive-pack.c
builtin/reflog.c
builtin/replace.c
builtin/reset.c
builtin/tag.c
builtin/update-ref.c
fast-import.c
refs.c

diff --cc branch.c
Simple merge
Simple merge
diff --cc builtin/fetch.c
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc builtin/reset.c
index 088ccffba07a4227150290492f22dc3bbac56fdc,789ee489b772f147f254afbf7d3b0c9eecf42c7b..1a5344877212e675d685055a631aba2ac0a4c661
@@@ -220,13 -216,10 +220,13 @@@ static void parse_args(struct pathspec 
                }
        }
        *rev_ret = rev;
 -      return argv[0] ? get_pathspec(prefix, argv) : NULL;
 +      parse_pathspec(pathspec, 0,
 +                     PATHSPEC_PREFER_FULL |
 +                     (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
 +                     prefix, argv);
  }
  
- static int update_refs(const char *rev, const unsigned char *sha1)
+ static int reset_refs(const char *rev, const unsigned char *sha1)
  {
        int update_ref_status;
        struct strbuf msg = STRBUF_INIT;
@@@ -351,10 -347,10 +351,10 @@@ int cmd_reset(int argc, const char **ar
                        die(_("Could not write new index file."));
        }
  
 -      if (!pathspec && !unborn) {
 +      if (!pathspec.nr && !unborn) {
                /* Any resets without paths update HEAD to the head being
                 * switched to, saving the previous head in ORIG_HEAD before. */
-               update_ref_status = update_refs(rev, sha1);
+               update_ref_status = reset_refs(rev, sha1);
  
                if (reset_type == HARD && !update_ref_status && !quiet)
                        print_new_head_line(lookup_commit_reference(sha1));
diff --cc builtin/tag.c
Simple merge
index 7484d36a65ba1f722f2dda33473bf509f30727c9,894f16bc59248f4c642228781a3b12bc1a82e54f..702e90db2a82a0eba2ff233b9ec46690da5afc3e
@@@ -13,12 -249,14 +249,14 @@@ int cmd_update_ref(int argc, const cha
  {
        const char *refname, *oldval, *msg = NULL;
        unsigned char sha1[20], oldsha1[20];
-       int delete = 0, no_deref = 0, flags = 0;
+       int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0;
        struct option options[] = {
                OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
 -              OPT_BOOLEAN('d', NULL, &delete, N_("delete the reference")),
 -              OPT_BOOLEAN('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
 -              OPT_BOOLEAN( 0 , "no-deref", &no_deref,
 +              OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
 +              OPT_BOOL( 0 , "no-deref", &no_deref,
                                        N_("update <refname> not the one it points to")),
 -              OPT_BOOLEAN( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
++              OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
++              OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
                OPT_END(),
        };
  
diff --cc fast-import.c
Simple merge
diff --cc refs.c
index d78860c46d95ea9785c83c08824355275393ed99,46177ad256c7991df327ea0d2fba31eb10bbdaab..6383813627ece441aa780c49f71feeaa41145c5c
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -3196,6 -3226,125 +3226,117 @@@ static int update_ref_write(const char 
        return 0;
  }
  
 -struct ref *find_ref_by_name(const struct ref *list, const char *name)
 -{
 -      for ( ; list; list = list->next)
 -              if (!strcmp(list->name, name))
 -                      return (struct ref *)list;
 -      return NULL;
 -}
 -
+ int update_ref(const char *action, const char *refname,
+              const unsigned char *sha1, const unsigned char *oldval,
+              int flags, enum action_on_err onerr)
+ {
+       struct ref_lock *lock;
+       lock = update_ref_lock(refname, oldval, flags, 0, onerr);
+       if (!lock)
+               return 1;
+       return update_ref_write(action, refname, sha1, lock, onerr);
+ }
+ static int ref_update_compare(const void *r1, const void *r2)
+ {
+       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;
+ }
  /*
   * generate a format suitable for scanf from a ref_rev_parse_rules
   * rule, that is replace the "%.*s" spec with a "%s" spec