Merge branch 'sb/refs-code-cleanup'
authorJunio C Hamano <gitster@pobox.com>
Fri, 1 Nov 2013 14:38:58 +0000 (07:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 1 Nov 2013 14:38:58 +0000 (07:38 -0700)
* sb/refs-code-cleanup:
cache: remove unused function 'have_git_dir'
refs: remove unused function invalidate_ref_cache

1  2 
cache.h
refs.c
refs.h
diff --combined cache.h
index 9aa46e70c3faf7eb678eeef7b825ae52fb4bc0bf,9cda46e07b7a318af6aed42857251bf9b4cee771..ce377e1354a4d0fd719b50edb32124110956467f
+++ b/cache.h
@@@ -101,9 -101,9 +101,9 @@@ unsigned long git_deflate_bound(git_zst
  
  #define CACHE_SIGNATURE 0x44495243    /* "DIRC" */
  struct cache_header {
 -      unsigned int hdr_signature;
 -      unsigned int hdr_version;
 -      unsigned int hdr_entries;
 +      uint32_t hdr_signature;
 +      uint32_t hdr_version;
 +      uint32_t hdr_entries;
  };
  
  #define INDEX_FORMAT_LB 2
   * check it for equality in the 32 bits we save.
   */
  struct cache_time {
 -      unsigned int sec;
 -      unsigned int nsec;
 +      uint32_t sec;
 +      uint32_t nsec;
  };
  
  struct stat_data {
@@@ -189,8 -189,6 +189,8 @@@ struct cache_entry 
  #error "CE_EXTENDED_FLAGS out of range"
  #endif
  
 +struct pathspec;
 +
  /*
   * Copy the sha1 and stat state of a cache entry from one to
   * another. But we never change the name, or the hash state!
@@@ -314,8 -312,6 +314,8 @@@ extern void free_name_hash(struct index
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
 +#define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
 +#define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
  #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
@@@ -369,9 -365,6 +369,9 @@@ static inline enum object_type object_t
  #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
  #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
  #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
 +#define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
 +#define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
 +#define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
  
  /*
   * This environment variable is expected to contain a boolean indicating
@@@ -398,7 -391,6 +398,6 @@@ extern int is_bare_repository(void)
  extern int is_inside_git_dir(void);
  extern char *git_work_tree_cfg;
  extern int is_inside_work_tree(void);
- extern int have_git_dir(void);
  extern const char *get_git_dir(void);
  extern int is_git_directory(const char *path);
  extern char *get_object_directory(void);
@@@ -419,7 -411,6 +418,7 @@@ extern void setup_work_tree(void)
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
  extern char *prefix_path(const char *prefix, int len, const char *path);
 +extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix,
@@@ -457,7 -448,7 +456,7 @@@ extern void sanitize_stdfds(void)
  
  /* Initialize and use the cache information */
  extern int read_index(struct index_state *);
 -extern int read_index_preload(struct index_state *, const char **pathspec);
 +extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
@@@ -465,8 -456,6 +464,8 @@@ extern int write_index(struct index_sta
  extern int discard_index(struct index_state *);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
 +extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
 +extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
@@@ -501,8 -490,28 +500,8 @@@ extern void *read_blob_data_from_index(
  extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
 -#define PATHSPEC_ONESTAR 1    /* the pathspec pattern satisfies GFNM_ONESTAR */
 -
 -struct pathspec {
 -      const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
 -      int nr;
 -      unsigned int has_wildcard:1;
 -      unsigned int recursive:1;
 -      int max_depth;
 -      struct pathspec_item {
 -              const char *match;
 -              int len;
 -              int nowildcard_len;
 -              int flags;
 -      } *items;
 -};
 -
 -extern int init_pathspec(struct pathspec *, const char **);
 -extern void free_pathspec(struct pathspec *);
  extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
  
 -extern int limit_pathspec_to_literal(void);
 -
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@@ -530,7 -539,7 +529,7 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
 +extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  
  struct lock_file {
        struct lock_file *next;
@@@ -751,9 -760,7 +750,9 @@@ int is_directory(const char *)
  const char *real_path(const char *path);
  const char *real_path_if_valid(const char *path);
  const char *absolute_path(const char *path);
 +const char *remove_leading_path(const char *in, const char *prefix);
  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 +int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
  int normalize_path_copy(char *dst, const char *src);
  int longest_ancestor_length(const char *path, struct string_list *prefixes);
  char *strip_path_suffix(const char *path, const char *suffix);
@@@ -885,7 -892,7 +884,7 @@@ extern char *resolve_refdup(const char 
  
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
 -extern int interpret_branch_name(const char *str, struct strbuf *);
 +extern int interpret_branch_name(const char *str, int len, struct strbuf *);
  extern int get_sha1_mb(const char *str, unsigned char *sha1);
  
  extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
@@@ -958,15 -965,6 +957,15 @@@ struct ident_split 
   */
  extern int split_ident_line(struct ident_split *, const char *, int);
  
 +/*
 + * Compare split idents for equality or strict ordering. Note that we
 + * compare only the ident part of the line, ignoring any timestamp.
 + *
 + * Because there are two fields, we must choose one as the primary key; we
 + * currently arbitrarily pick the email.
 + */
 +extern int ident_cmp(const struct ident_split *, const struct ident_split *);
 +
  struct checkout {
        const char *base_dir;
        int base_dir_len;
                 refresh_cache:1;
  };
  
 +#define TEMPORARY_FILENAME_LENGTH 25
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
  
  struct cache_def {
@@@ -1040,6 -1037,68 +1039,6 @@@ struct pack_entry 
        struct packed_git *p;
  };
  
 -struct ref {
 -      struct ref *next;
 -      unsigned char old_sha1[20];
 -      unsigned char new_sha1[20];
 -      char *symref;
 -      unsigned int
 -              force:1,
 -              forced_update:1,
 -              deletion:1,
 -              matched:1;
 -
 -      /*
 -       * Order is important here, as we write to FETCH_HEAD
 -       * in numeric order. And the default NOT_FOR_MERGE
 -       * should be 0, so that xcalloc'd structures get it
 -       * by default.
 -       */
 -      enum {
 -              FETCH_HEAD_MERGE = -1,
 -              FETCH_HEAD_NOT_FOR_MERGE = 0,
 -              FETCH_HEAD_IGNORE = 1
 -      } fetch_head_status;
 -
 -      enum {
 -              REF_STATUS_NONE = 0,
 -              REF_STATUS_OK,
 -              REF_STATUS_REJECT_NONFASTFORWARD,
 -              REF_STATUS_REJECT_ALREADY_EXISTS,
 -              REF_STATUS_REJECT_NODELETE,
 -              REF_STATUS_REJECT_FETCH_FIRST,
 -              REF_STATUS_REJECT_NEEDS_FORCE,
 -              REF_STATUS_UPTODATE,
 -              REF_STATUS_REMOTE_REJECT,
 -              REF_STATUS_EXPECTING_REPORT
 -      } status;
 -      char *remote_status;
 -      struct ref *peer_ref; /* when renaming */
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -#define REF_NORMAL    (1u << 0)
 -#define REF_HEADS     (1u << 1)
 -#define REF_TAGS      (1u << 2)
 -
 -extern struct ref *find_ref_by_name(const struct ref *list, const char *name);
 -
 -#define CONNECT_VERBOSE       (1u << 0)
 -extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
 -extern int finish_connect(struct child_process *conn);
 -extern int git_connection_is_socket(struct child_process *conn);
 -struct extra_have_objects {
 -      int nr, alloc;
 -      unsigned char (*array)[20];
 -};
 -extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 -                                   struct ref **list, unsigned int flags,
 -                                   struct extra_have_objects *);
 -extern int server_supports(const char *feature);
 -extern int parse_feature_request(const char *features, const char *feature);
 -extern const char *server_feature_value(const char *feature, int *len_ret);
 -extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
 -
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
  /* A hook for count-objects to report invalid files in pack directory */
@@@ -1130,7 -1189,6 +1129,7 @@@ extern int git_config_with_options(conf
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
 +extern int64_t git_config_int64(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
  extern int git_config_bool_or_int(const char *, const char *, int *);
  extern int git_config_bool(const char *, const char *);
@@@ -1246,7 -1304,7 +1245,7 @@@ void packet_trace_identity(const char *
   * return 0 if success, 1 - if addition of a file failed and
   * ADD_FILES_IGNORE_ERRORS was specified in flags
   */
 -int add_files_to_cache(const char *prefix, const char **pathspec, int flags);
 +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
@@@ -1280,7 -1338,7 +1279,7 @@@ extern int ws_blank_line(const char *li
  #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
 -int report_path_error(const char *ps_matched, const char **pathspec, const char *prefix);
 +int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
  char *alias_lookup(const char *alias);
diff --combined refs.c
index 0c0e963532a6dcf2f9c062b371d40a2cd10571c3,d20834054ba1ddb32c00aceb4896098dae0844f6..5e5a3824b992132aaac8d86698feebcb773e13c6
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -72,10 -72,6 +72,10 @@@ int check_refname_format(const char *re
  {
        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);
@@@ -947,13 -943,6 +947,6 @@@ static struct ref_cache *get_ref_cache(
        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
  
@@@ -1955,7 -1944,7 +1948,7 @@@ static int remove_empty_directories(con
  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;
@@@ -2125,12 -2114,11 +2118,12 @@@ struct ref_lock *lock_ref_sha1(const ch
  }
  
  struct ref_lock *lock_any_ref_for_update(const char *refname,
 -                                       const unsigned char *old_sha1, int flags)
 +                                       const unsigned char *old_sha1,
 +                                       int flags, int *type_p)
  {
        if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
                return NULL;
 -      return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
 +      return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
  }
  
  /*
@@@ -2418,82 -2406,60 +2411,82 @@@ static int curate_packed_ref_fn(struct 
        return 0;
  }
  
 -static int repack_without_ref(const char *refname)
 +static int repack_without_refs(const char **refnames, int n)
  {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
        struct string_list_item *ref_to_delete;
 +      int i, removed = 0;
 +
 +      /* Look for a packed ref */
 +      for (i = 0; i < n; i++)
 +              if (get_packed_ref(refnames[i]))
 +                      break;
  
 -      if (!get_packed_ref(refname))
 -              return 0; /* refname does not exist in packed refs */
 +      /* Avoid locking if we have nothing to do */
 +      if (i == n)
 +              return 0; /* no refname exists in packed refs */
  
        if (lock_packed_refs(0)) {
                unable_to_lock_error(git_path("packed-refs"), errno);
 -              return error("cannot delete '%s' from packed refs", refname);
 +              return error("cannot delete '%s' from packed refs", refnames[i]);
        }
        packed = get_packed_refs(&ref_cache);
  
 -      /* Remove refname from the cache: */
 -      if (remove_entry(packed, refname) == -1) {
 +      /* Remove refnames from the cache */
 +      for (i = 0; i < n; i++)
 +              if (remove_entry(packed, refnames[i]) != -1)
 +                      removed = 1;
 +      if (!removed) {
                /*
 -               * The packed entry disappeared while we were
 +               * All packed entries disappeared while we were
                 * acquiring the lock.
                 */
                rollback_packed_refs();
                return 0;
        }
  
 -      /* Remove any other accumulated cruft: */
 +      /* Remove any other accumulated cruft */
        do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete);
        for_each_string_list_item(ref_to_delete, &refs_to_delete) {
                if (remove_entry(packed, ref_to_delete->string) == -1)
                        die("internal error");
        }
  
 -      /* Write what remains: */
 +      /* Write what remains */
        return commit_packed_refs();
  }
  
 -int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 +static int repack_without_ref(const char *refname)
  {
 -      struct ref_lock *lock;
 -      int err, i = 0, ret = 0, flag = 0;
 +      return repack_without_refs(&refname, 1);
 +}
  
 -      lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
 -      if (!lock)
 -              return 1;
 +static int delete_ref_loose(struct ref_lock *lock, int flag)
 +{
        if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
                /* loose */
 -              i = strlen(lock->lk->filename) - 5; /* .lock */
 +              int err, i = strlen(lock->lk->filename) - 5; /* .lock */
 +
                lock->lk->filename[i] = 0;
                err = unlink_or_warn(lock->lk->filename);
 -              if (err && errno != ENOENT)
 -                      ret = 1;
 -
                lock->lk->filename[i] = '.';
 +              if (err && errno != ENOENT)
 +                      return 1;
        }
 +      return 0;
 +}
 +
 +int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 +{
 +      struct ref_lock *lock;
 +      int ret = 0, flag = 0;
 +
 +      lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
 +      if (!lock)
 +              return 1;
 +      ret |= delete_ref_loose(lock, flag);
 +
        /* removing the loose one could have resurrected an earlier
         * packed one.  Also, if it was not loose we need to repack
         * without it.
@@@ -3196,13 -3162,12 +3189,13 @@@ int for_each_reflog(each_ref_fn fn, voi
        return retval;
  }
  
 -int update_ref(const char *action, const char *refname,
 -              const unsigned char *sha1, const unsigned char *oldval,
 -              int flags, enum action_on_err onerr)
 +static struct ref_lock *update_ref_lock(const char *refname,
 +                                      const unsigned char *oldval,
 +                                      int flags, int *type_p,
 +                                      enum action_on_err onerr)
  {
 -      static struct ref_lock *lock;
 -      lock = lock_any_ref_for_update(refname, oldval, flags);
 +      struct ref_lock *lock;
 +      lock = lock_any_ref_for_update(refname, oldval, flags, type_p);
        if (!lock) {
                const char *str = "Cannot lock the ref '%s'.";
                switch (onerr) {
                case DIE_ON_ERR: die(str, refname); break;
                case QUIET_ON_ERR: break;
                }
 -              return 1;
        }
 +      return lock;
 +}
 +
 +static int update_ref_write(const char *action, const char *refname,
 +                          const unsigned char *sha1, struct ref_lock *lock,
 +                          enum action_on_err onerr)
 +{
        if (write_ref_sha1(lock, sha1, action) < 0) {
                const char *str = "Cannot update the ref '%s'.";
                switch (onerr) {
        return 0;
  }
  
 -struct ref *find_ref_by_name(const struct ref *list, const char *name)
 +int update_ref(const char *action, const char *refname,
 +             const unsigned char *sha1, const unsigned char *oldval,
 +             int flags, enum action_on_err onerr)
  {
 -      for ( ; list; list = list->next)
 -              if (!strcmp(list->name, name))
 -                      return (struct ref *)list;
 -      return NULL;
 +      struct ref_lock *lock;
 +      lock = update_ref_lock(refname, oldval, flags, NULL, 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;
  }
  
  /*
@@@ -3376,7 -3232,7 +3369,7 @@@ char *shorten_unambiguous_ref(const cha
                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]);
  
diff --combined refs.h
index b113377c4806423af2cad13f1b38284b5f5e5f2f,861170d691e83d299ad3586bd430e3938b056db0..87a1a79ad659f3520a22ae14bac1d5082cf2c27b
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -10,20 -10,6 +10,20 @@@ 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 */
 +};
 +
  /*
   * Bit values set in the flags argument passed to each_ref_fn():
   */
@@@ -151,7 -137,7 +151,7 @@@ extern struct ref_lock *lock_ref_sha1(c
  #define REF_NODEREF   0x01
  extern struct ref_lock *lock_any_ref_for_update(const char *refname,
                                                const unsigned char *old_sha1,
 -                                              int flags);
 +                                              int flags, int *type_p);
  
  /** Close the file descriptor owned by a lock and return the status */
  extern int close_ref(struct ref_lock *lock);
@@@ -165,14 -151,6 +165,6 @@@ extern void unlock_ref(struct ref_lock 
  /** Writes sha1 into the ref specified by the lock. **/
  extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
  
- /*
-  * Invalidate the reference cache for the specified submodule.  Use
-  * submodule=NULL to invalidate the cache for the main module.  This
-  * function must be called if references are changed via a mechanism
-  * other than the refs API.
-  */
- extern void invalidate_ref_cache(const char *submodule);
  /** Setup reflog before using. **/
  int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
  
@@@ -228,12 -206,6 +220,12 @@@ int update_ref(const char *action, cons
                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 *);