Merge branch 'jl/remote-rm-prune'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:17:58 +0000 (12:17 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:17:58 +0000 (12:17 -0700)
"git remote rm" and "git remote prune" can involve removing many
refs at once, which is not a very efficient thing to do when very
many refs exist in the packed-refs file.

* jl/remote-rm-prune:
remote prune: optimize "dangling symref" check/warning
remote: repack packed-refs once when deleting multiple refs
remote rm: delete remote configuration as the last

1  2 
builtin/remote.c
refs.c
refs.h
diff --combined builtin/remote.c
index 9f6202167a64b72420e24895171eea1c3c67ae8e,9b3e368983570be72a5c4b8476f69dc39522d192..c9102e8fe94b41af5136a9cc23db939effae147a
@@@ -282,7 -282,7 +282,7 @@@ static int config_read_branches(const c
                item = string_list_insert(&branch_list, name);
  
                if (!item->util)
 -                      item->util = xcalloc(sizeof(struct branch_info), 1);
 +                      item->util = xcalloc(1, sizeof(struct branch_info));
                info = item->util;
                if (type == REMOTE) {
                        if (info->remote_name)
@@@ -398,7 -398,7 +398,7 @@@ static int get_push_ref_states(const st
  
                item = string_list_append(&states->push,
                                          abbrev_branch(ref->peer_ref->name));
 -              item->util = xcalloc(sizeof(struct push_info), 1);
 +              item->util = xcalloc(1, sizeof(struct push_info));
                info = item->util;
                info->forced = ref->force;
                info->dest = xstrdup(abbrev_branch(ref->name));
@@@ -433,7 -433,7 +433,7 @@@ static int get_push_ref_states_noquery(
        states->push.strdup_strings = 1;
        if (!remote->push_refspec_nr) {
                item = string_list_append(&states->push, _("(matching)"));
 -              info = item->util = xcalloc(sizeof(struct push_info), 1);
 +              info = item->util = xcalloc(1, sizeof(struct push_info));
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
        }
                else
                        item = string_list_append(&states->push, _("(delete)"));
  
 -              info = item->util = xcalloc(sizeof(struct push_info), 1);
 +              info = item->util = xcalloc(1, sizeof(struct push_info));
                info->forced = spec->force;
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(spec->dst ? spec->dst : item->string);
@@@ -749,15 -749,23 +749,23 @@@ static int mv(int argc, const char **ar
  
  static int remove_branches(struct string_list *branches)
  {
+       const char **branch_names;
        int i, result = 0;
+       branch_names = xmalloc(branches->nr * sizeof(*branch_names));
+       for (i = 0; i < branches->nr; i++)
+               branch_names[i] = branches->items[i].string;
+       result |= repack_without_refs(branch_names, branches->nr);
+       free(branch_names);
        for (i = 0; i < branches->nr; i++) {
                struct string_list_item *item = branches->items + i;
                const char *refname = item->string;
-               unsigned char *sha1 = item->util;
  
-               if (delete_ref(refname, sha1, 0))
+               if (delete_ref(refname, NULL, 0))
                        result |= error(_("Could not remove branch %s"), refname);
        }
        return result;
  }
  
@@@ -789,10 -797,6 +797,6 @@@ static int rm(int argc, const char **ar
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
  
-       strbuf_addf(&buf, "remote.%s", remote->name);
-       if (git_config_rename_section(buf.buf, NULL) < 1)
-               return error(_("Could not remove config section '%s'"), buf.buf);
        read_branches();
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
        }
        string_list_clear(&skipped, 0);
  
+       if (!result) {
+               strbuf_addf(&buf, "remote.%s", remote->name);
+               if (git_config_rename_section(buf.buf, NULL) < 1)
+                       return error(_("Could not remove config section '%s'"), buf.buf);
+       }
        return result;
  }
  
@@@ -1303,6 -1313,8 +1313,8 @@@ static int prune_remote(const char *rem
  {
        int result = 0, i;
        struct ref_states states;
+       struct string_list delete_refs_list = STRING_LIST_INIT_NODUP;
+       const char **delete_refs;
        const char *dangling_msg = dry_run
                ? _(" %s will become dangling!")
                : _(" %s has become dangling!");
                       states.remote->url_nr
                       ? states.remote->url[0]
                       : _("(no URL)"));
+               delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
+               for (i = 0; i < states.stale.nr; i++)
+                       delete_refs[i] = states.stale.items[i].util;
+               if (!dry_run)
+                       result |= repack_without_refs(delete_refs, states.stale.nr);
+               free(delete_refs);
        }
  
        for (i = 0; i < states.stale.nr; i++) {
                const char *refname = states.stale.items[i].util;
  
+               string_list_insert(&delete_refs_list, refname);
                if (!dry_run)
                        result |= delete_ref(refname, NULL, 0);
  
                else
                        printf_ln(_(" * [pruned] %s"),
                               abbrev_ref(refname, "refs/remotes/"));
-               warn_dangling_symref(stdout, dangling_msg, refname);
        }
  
+       warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list);
+       string_list_clear(&delete_refs_list, 0);
        free_remote_ref_states(&states);
        return result;
  }
diff --combined refs.c
index 68982637ed89a2d679d5e2a9a86e76e6fb56a7b4,59fb70087a438a763cdbfb2c4e75de36d533aead..ceeeec645c12a0e35e33d2263dca38829668e444
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -1611,6 -1611,7 +1611,7 @@@ int peel_ref(const char *refname, unsig
  struct warn_if_dangling_data {
        FILE *fp;
        const char *refname;
+       const struct string_list *refnames;
        const char *msg_fmt;
  };
  
@@@ -1625,8 -1626,12 +1626,12 @@@ static int warn_if_dangling_symref(cons
                return 0;
  
        resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
-       if (!resolves_to || strcmp(resolves_to, d->refname))
+       if (!resolves_to
+           || (d->refname
+               ? strcmp(resolves_to, d->refname)
+               : !string_list_has_string(d->refnames, resolves_to))) {
                return 0;
+       }
  
        fprintf(d->fp, d->msg_fmt, refname);
        fputc('\n', d->fp);
@@@ -1639,6 -1644,18 +1644,18 @@@ void warn_dangling_symref(FILE *fp, con
  
        data.fp = fp;
        data.refname = refname;
+       data.refnames = NULL;
+       data.msg_fmt = msg_fmt;
+       for_each_rawref(warn_if_dangling_symref, &data);
+ }
+ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
+ {
+       struct warn_if_dangling_data data;
+       data.fp = fp;
+       data.refname = NULL;
+       data.refnames = refnames;
        data.msg_fmt = msg_fmt;
        for_each_rawref(warn_if_dangling_symref, &data);
  }
@@@ -1999,6 -2016,7 +2016,6 @@@ int dwim_log(const char *str, int len, 
  
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
 -              struct stat st;
                unsigned char hash[20];
                char path[PATH_MAX];
                const char *ref, *it;
                ref = resolve_ref_unsafe(path, hash, 1, NULL);
                if (!ref)
                        continue;
 -              if (!stat(git_path("logs/%s", path), &st) &&
 -                  S_ISREG(st.st_mode))
 +              if (reflog_exists(path))
                        it = path;
 -              else if (strcmp(ref, path) &&
 -                       !stat(git_path("logs/%s", ref), &st) &&
 -                       S_ISREG(st.st_mode))
 +              else if (strcmp(ref, path) && reflog_exists(ref))
                        it = ref;
                else
                        continue;
@@@ -2427,7 -2448,7 +2444,7 @@@ static int curate_packed_ref_fn(struct 
        return 0;
  }
  
static int repack_without_refs(const char **refnames, int n)
+ int repack_without_refs(const char **refnames, int n)
  {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
@@@ -3042,19 -3063,6 +3059,19 @@@ int read_ref_at(const char *refname, un
        return 1;
  }
  
 +int reflog_exists(const char *refname)
 +{
 +      struct stat st;
 +
 +      return !lstat(git_path("logs/%s", refname), &st) &&
 +              S_ISREG(st.st_mode);
 +}
 +
 +int delete_reflog(const char *refname)
 +{
 +      return remove_path(git_path("logs/%s", refname));
 +}
 +
  static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
  {
        unsigned char osha1[20], nsha1[20];
@@@ -3252,9 -3260,9 +3269,9 @@@ static struct ref_lock *update_ref_lock
        if (!lock) {
                const char *str = "Cannot lock the ref '%s'.";
                switch (onerr) {
 -              case MSG_ON_ERR: error(str, refname); break;
 -              case DIE_ON_ERR: die(str, refname); break;
 -              case QUIET_ON_ERR: break;
 +              case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
 +              case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
 +              case UPDATE_REFS_QUIET_ON_ERR: break;
                }
        }
        return lock;
@@@ -3267,118 -3275,15 +3284,118 @@@ static int update_ref_write(const char 
        if (write_ref_sha1(lock, sha1, action) < 0) {
                const char *str = "Cannot update the ref '%s'.";
                switch (onerr) {
 -              case MSG_ON_ERR: error(str, refname); break;
 -              case DIE_ON_ERR: die(str, refname); break;
 -              case QUIET_ON_ERR: break;
 +              case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
 +              case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
 +              case UPDATE_REFS_QUIET_ON_ERR: break;
                }
                return 1;
        }
        return 0;
  }
  
 +/**
 + * Information needed for a single ref update.  Set new_sha1 to the
 + * new value or to zero to delete the ref.  To check the old value
 + * while locking the ref, set have_old to 1 and set old_sha1 to the
 + * value or to zero to ensure the ref does not exist before update.
 + */
 +struct ref_update {
 +      unsigned char new_sha1[20];
 +      unsigned char old_sha1[20];
 +      int flags; /* REF_NODEREF? */
 +      int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 +      struct ref_lock *lock;
 +      int type;
 +      const char refname[FLEX_ARRAY];
 +};
 +
 +/*
 + * Data structure for holding a reference transaction, which can
 + * consist of checks and updates to multiple references, carried out
 + * as atomically as possible.  This structure is opaque to callers.
 + */
 +struct ref_transaction {
 +      struct ref_update **updates;
 +      size_t alloc;
 +      size_t nr;
 +};
 +
 +struct ref_transaction *ref_transaction_begin(void)
 +{
 +      return xcalloc(1, sizeof(struct ref_transaction));
 +}
 +
 +static void ref_transaction_free(struct ref_transaction *transaction)
 +{
 +      int i;
 +
 +      for (i = 0; i < transaction->nr; i++)
 +              free(transaction->updates[i]);
 +
 +      free(transaction->updates);
 +      free(transaction);
 +}
 +
 +void ref_transaction_rollback(struct ref_transaction *transaction)
 +{
 +      ref_transaction_free(transaction);
 +}
 +
 +static struct ref_update *add_update(struct ref_transaction *transaction,
 +                                   const char *refname)
 +{
 +      size_t len = strlen(refname);
 +      struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1);
 +
 +      strcpy((char *)update->refname, refname);
 +      ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 +      transaction->updates[transaction->nr++] = update;
 +      return update;
 +}
 +
 +void ref_transaction_update(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1, unsigned char *old_sha1,
 +                          int flags, int have_old)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      hashcpy(update->new_sha1, new_sha1);
 +      update->flags = flags;
 +      update->have_old = have_old;
 +      if (have_old)
 +              hashcpy(update->old_sha1, old_sha1);
 +}
 +
 +void ref_transaction_create(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1,
 +                          int flags)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      assert(!is_null_sha1(new_sha1));
 +      hashcpy(update->new_sha1, new_sha1);
 +      hashclr(update->old_sha1);
 +      update->flags = flags;
 +      update->have_old = 1;
 +}
 +
 +void ref_transaction_delete(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *old_sha1,
 +                          int flags, int have_old)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      update->flags = flags;
 +      update->have_old = have_old;
 +      if (have_old) {
 +              assert(!is_null_sha1(old_sha1));
 +              hashcpy(update->old_sha1, old_sha1);
 +      }
 +}
 +
  int update_ref(const char *action, const char *refname,
               const unsigned char *sha1, const unsigned char *oldval,
               int flags, enum action_on_err onerr)
@@@ -3394,7 -3299,7 +3411,7 @@@ static int ref_update_compare(const voi
  {
        const struct ref_update * const *u1 = r1;
        const struct ref_update * const *u2 = r2;
 -      return strcmp((*u1)->ref_name, (*u2)->ref_name);
 +      return strcmp((*u1)->refname, (*u2)->refname);
  }
  
  static int ref_update_reject_duplicates(struct ref_update **updates, int n,
  {
        int i;
        for (i = 1; i < n; i++)
 -              if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) {
 +              if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
                        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:
 +                      case UPDATE_REFS_MSG_ON_ERR:
 +                              error(str, updates[i]->refname); break;
 +                      case UPDATE_REFS_DIE_ON_ERR:
 +                              die(str, updates[i]->refname); break;
 +                      case UPDATE_REFS_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 ref_transaction_commit(struct ref_transaction *transaction,
 +                         const char *msg, enum action_on_err onerr)
  {
        int ret = 0, delnum = 0, i;
 -      struct ref_update **updates;
 -      int *types;
 -      struct ref_lock **locks;
        const char **delnames;
 +      int n = transaction->nr;
 +      struct ref_update **updates = transaction->updates;
  
 -      if (!updates_orig || !n)
 +      if (!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)
  
        /* 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]) {
 +              struct ref_update *update = updates[i];
 +
 +              update->lock = update_ref_lock(update->refname,
 +                                             (update->have_old ?
 +                                              update->old_sha1 : NULL),
 +                                             update->flags,
 +                                             &update->type, onerr);
 +              if (!update->lock) {
                        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 */
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if (!is_null_sha1(update->new_sha1)) {
 +                      ret = update_ref_write(msg,
 +                                             update->refname,
 +                                             update->new_sha1,
 +                                             update->lock, onerr);
 +                      update->lock = 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]);
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if (update->lock) {
 +                      delnames[delnum++] = update->lock->ref_name;
 +                      ret |= delete_ref_loose(update->lock, update->type);
                }
 +      }
 +
        ret |= repack_without_refs(delnames, delnum);
        for (i = 0; i < delnum; i++)
                unlink_or_warn(git_path("logs/%s", delnames[i]));
  
  cleanup:
        for (i = 0; i < n; i++)
 -              if (locks[i])
 -                      unlock_ref(locks[i]);
 -      free(updates);
 -      free(types);
 -      free(locks);
 +              if (updates[i]->lock)
 +                      unlock_ref(updates[i]->lock);
        free(delnames);
 +      ref_transaction_free(transaction);
        return ret;
  }
  
diff --combined refs.h
index 09ff483c19ec9484359864640da86f9a40418ecc,1440acc06ce59f35d23719adef18774731efe5a4..4e3050d976dcdae2e98a50b75e065ea086b9cb25
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -10,7 -10,19 +10,7 @@@ struct ref_lock 
        int force_write;
  };
  
 -/**
 - * Information needed for a single ref update.  Set new_sha1 to the
 - * new value or to zero to delete the ref.  To check the old value
 - * while locking the ref, set have_old to 1 and set old_sha1 to the
 - * value or to zero to ensure the ref does not exist before update.
 - */
 -struct ref_update {
 -      const char *ref_name;
 -      unsigned char new_sha1[20];
 -      unsigned char old_sha1[20];
 -      int flags; /* REF_NODEREF? */
 -      int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 -};
 +struct ref_transaction;
  
  /*
   * Bit values set in the flags argument passed to each_ref_fn():
@@@ -77,6 -89,7 +77,7 @@@ static inline const char *has_glob_spec
  extern int for_each_rawref(each_ref_fn, void *);
  
  extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
+ extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list* refnames);
  
  /*
   * Lock the packed-refs file for writing.  Flags is passed to
@@@ -120,6 -133,8 +121,8 @@@ extern void rollback_packed_refs(void)
   */
  int pack_refs(unsigned int flags);
  
+ extern int repack_without_refs(const char **refnames, int n);
  extern int ref_exists(const char *);
  
  /*
@@@ -154,19 -169,13 +157,19 @@@ extern void unlock_ref(struct ref_lock 
  extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
  
  /** Setup reflog before using. **/
 -int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
 +int log_ref_setup(const char *refname, char *logfile, int bufsize);
  
  /** Reads log for the value of ref during at_time. **/
  extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
                       unsigned char *sha1, char **msg,
                       unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
  
 +/** Check if a particular reflog exists */
 +extern int reflog_exists(const char *refname);
 +
 +/** Delete a reflog */
 +extern int delete_reflog(const char *refname);
 +
  /* iterate over reflog entries */
  typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
  int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
@@@ -208,80 -217,18 +211,80 @@@ extern int rename_ref(const char *oldre
   */
  extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
  
 -/** lock a ref and then write its file */
 -enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
 +enum action_on_err {
 +      UPDATE_REFS_MSG_ON_ERR,
 +      UPDATE_REFS_DIE_ON_ERR,
 +      UPDATE_REFS_QUIET_ON_ERR
 +};
 +
 +/*
 + * Begin a reference transaction.  The reference transaction must
 + * eventually be commited using ref_transaction_commit() or rolled
 + * back using ref_transaction_rollback().
 + */
 +struct ref_transaction *ref_transaction_begin(void);
 +
 +/*
 + * Roll back a ref_transaction and free all associated data.
 + */
 +void ref_transaction_rollback(struct ref_transaction *transaction);
 +
 +
 +/*
 + * The following functions add a reference check or update to a
 + * ref_transaction.  In all of them, refname is the name of the
 + * reference to be affected.  The functions make internal copies of
 + * refname, so the caller retains ownership of the parameter.  flags
 + * can be REF_NODEREF; it is passed to update_ref_lock().
 + */
 +
 +
 +/*
 + * Add a reference update to transaction.  new_sha1 is the value that
 + * the reference should have after the update, or zeros if it should
 + * be deleted.  If have_old is true, then old_sha1 holds the value
 + * that the reference should have had before the update, or zeros if
 + * it must not have existed beforehand.
 + */
 +void ref_transaction_update(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1, unsigned char *old_sha1,
 +                          int flags, int have_old);
 +
 +/*
 + * Add a reference creation to transaction.  new_sha1 is the value
 + * that the reference should have after the update; it must not be the
 + * null SHA-1.  It is verified that the reference does not exist
 + * already.
 + */
 +void ref_transaction_create(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1,
 +                          int flags);
 +
 +/*
 + * Add a reference deletion to transaction.  If have_old is true, then
 + * old_sha1 holds the value that the reference should have had before
 + * the update (which must not be the null SHA-1).
 + */
 +void ref_transaction_delete(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *old_sha1,
 +                          int flags, int have_old);
 +
 +/*
 + * Commit all of the changes that have been queued in transaction, as
 + * atomically as possible.  Return a nonzero value if there is a
 + * problem.  The ref_transaction is freed by this function.
 + */
 +int ref_transaction_commit(struct ref_transaction *transaction,
 +                         const char *msg, enum action_on_err onerr);
 +
 +/** Lock a ref and then write its file */
  int update_ref(const char *action, const char *refname,
                const unsigned char *sha1, const unsigned char *oldval,
                int flags, enum action_on_err onerr);
  
 -/**
 - * Lock all refs and then perform all modifications.
 - */
 -int update_refs(const char *action, const struct ref_update **updates,
 -              int n, enum action_on_err onerr);
 -
  extern int parse_hide_refs_config(const char *var, const char *value, const char *);
  extern int ref_is_hidden(const char *);