Merge branch 'rs/misc-cppcheck-fixes'
authorJunio C Hamano <gitster@pobox.com>
Mon, 24 Apr 2017 05:07:56 +0000 (22:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 24 Apr 2017 05:07:56 +0000 (22:07 -0700)
Various small fixes.

* rs/misc-cppcheck-fixes:
server-info: avoid calling fclose(3) twice in update_info_file()
files_for_each_reflog_ent_reverse(): close stream and free strbuf on error
am: close stream on error, but not stdin

1  2 
builtin/am.c
refs/files-backend.c
diff --combined builtin/am.c
index f7a7a971fbe762a4fb691e786e86f681f2bbd7c1,172960094c573316de8ea02e1343519fe3ec36ad..805f56cec2f523f8248e2893e10d99cc4e1d1b88
@@@ -762,14 -762,18 +762,18 @@@ static int split_mail_conv(mail_conv_f
                mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
  
                out = fopen(mail, "w");
-               if (!out)
+               if (!out) {
+                       if (in != stdin)
+                               fclose(in);
                        return error_errno(_("could not open '%s' for writing"),
                                           mail);
+               }
  
                ret = fn(out, in, keep_cr);
  
                fclose(out);
-               fclose(in);
+               if (in != stdin)
+                       fclose(in);
  
                if (ret)
                        return error(_("could not parse patch '%s'"), *paths);
@@@ -1049,7 -1053,7 +1053,7 @@@ static void am_setup(struct am_state *s
        } else {
                write_state_text(state, "abort-safety", "");
                if (!state->rebasing)
 -                      delete_ref("ORIG_HEAD", NULL, 0);
 +                      delete_ref(NULL, "ORIG_HEAD", NULL, 0);
        }
  
        /*
@@@ -2172,7 -2176,7 +2176,7 @@@ static void am_abort(struct am_state *s
                                has_curr_head ? &curr_head : NULL, 0,
                                UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
 -              delete_ref(curr_branch, NULL, REF_NODEREF);
 +              delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
  
        free(curr_branch);
        am_destroy(state);
diff --combined refs/files-backend.c
index 4d705b4037e6c1c0c1f2e277e9a5ef5bce695e61,10ba254941e8c4415b1b97950f2f9203840ee114..c9d900fd1287e98ee59421649aee1e34b73e4fa0
@@@ -165,10 -165,6 +165,10 @@@ static struct ref_entry *create_dir_ent
                                          const char *dirname, size_t len,
                                          int incomplete);
  static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
 +static int files_log_ref_write(struct files_ref_store *refs,
 +                             const char *refname, const unsigned char *old_sha1,
 +                             const unsigned char *new_sha1, const char *msg,
 +                             int flags, struct strbuf *err);
  
  static struct ref_dir *get_ref_dir(struct ref_entry *entry)
  {
@@@ -916,12 -912,6 +916,12 @@@ struct packed_ref_cache 
   */
  struct files_ref_store {
        struct ref_store base;
 +      unsigned int store_flags;
 +
 +      char *gitdir;
 +      char *gitcommondir;
 +      char *packed_refs_path;
 +
        struct ref_entry *loose;
        struct packed_ref_cache *packed;
  };
@@@ -977,61 -967,34 +977,61 @@@ static void clear_loose_ref_cache(struc
   * Create a new submodule ref cache and add it to the internal
   * set of caches.
   */
 -static struct ref_store *files_ref_store_create(const char *submodule)
 +static struct ref_store *files_ref_store_create(const char *gitdir,
 +                                              unsigned int flags)
  {
        struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
        struct ref_store *ref_store = (struct ref_store *)refs;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      base_ref_store_init(ref_store, &refs_be_files);
 +      refs->store_flags = flags;
  
 -      base_ref_store_init(ref_store, &refs_be_files, submodule);
 +      refs->gitdir = xstrdup(gitdir);
 +      get_common_dir_noenv(&sb, gitdir);
 +      refs->gitcommondir = strbuf_detach(&sb, NULL);
 +      strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
 +      refs->packed_refs_path = strbuf_detach(&sb, NULL);
  
        return ref_store;
  }
  
 +/*
 + * Die if refs is not the main ref store. caller is used in any
 + * necessary error messages.
 + */
 +static void files_assert_main_repository(struct files_ref_store *refs,
 +                                       const char *caller)
 +{
 +      if (refs->store_flags & REF_STORE_MAIN)
 +              return;
 +
 +      die("BUG: operation %s only allowed for main ref store", caller);
 +}
 +
  /*
   * Downcast ref_store to files_ref_store. Die if ref_store is not a
 - * files_ref_store. If submodule_allowed is not true, then also die if
 - * files_ref_store is for a submodule (i.e., not for the main
 - * repository). caller is used in any necessary error messages.
 + * files_ref_store. required_flags is compared with ref_store's
 + * store_flags to ensure the ref_store has all required capabilities.
 + * "caller" is used in any necessary error messages.
   */
 -static struct files_ref_store *files_downcast(
 -              struct ref_store *ref_store, int submodule_allowed,
 -              const char *caller)
 +static struct files_ref_store *files_downcast(struct ref_store *ref_store,
 +                                            unsigned int required_flags,
 +                                            const char *caller)
  {
 +      struct files_ref_store *refs;
 +
        if (ref_store->be != &refs_be_files)
                die("BUG: ref_store is type \"%s\" not \"files\" in %s",
                    ref_store->be->name, caller);
  
 -      if (!submodule_allowed)
 -              assert_main_repository(ref_store, caller);
 +      refs = (struct files_ref_store *)ref_store;
  
 -      return (struct files_ref_store *)ref_store;
 +      if ((refs->store_flags & required_flags) != required_flags)
 +              die("BUG: operation %s requires abilities 0x%x, but only have 0x%x",
 +                  caller, required_flags, refs->store_flags);
 +
 +      return refs;
  }
  
  /* The length of a peeled reference line in packed-refs, including EOL: */
@@@ -1162,63 -1125,19 +1162,63 @@@ static void read_packed_refs(FILE *f, s
        strbuf_release(&line);
  }
  
 +static const char *files_packed_refs_path(struct files_ref_store *refs)
 +{
 +      return refs->packed_refs_path;
 +}
 +
 +static void files_reflog_path(struct files_ref_store *refs,
 +                            struct strbuf *sb,
 +                            const char *refname)
 +{
 +      if (!refname) {
 +              /*
 +               * FIXME: of course this is wrong in multi worktree
 +               * setting. To be fixed real soon.
 +               */
 +              strbuf_addf(sb, "%s/logs", refs->gitcommondir);
 +              return;
 +      }
 +
 +      switch (ref_type(refname)) {
 +      case REF_TYPE_PER_WORKTREE:
 +      case REF_TYPE_PSEUDOREF:
 +              strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
 +              break;
 +      case REF_TYPE_NORMAL:
 +              strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
 +              break;
 +      default:
 +              die("BUG: unknown ref type %d of ref %s",
 +                  ref_type(refname), refname);
 +      }
 +}
 +
 +static void files_ref_path(struct files_ref_store *refs,
 +                         struct strbuf *sb,
 +                         const char *refname)
 +{
 +      switch (ref_type(refname)) {
 +      case REF_TYPE_PER_WORKTREE:
 +      case REF_TYPE_PSEUDOREF:
 +              strbuf_addf(sb, "%s/%s", refs->gitdir, refname);
 +              break;
 +      case REF_TYPE_NORMAL:
 +              strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
 +              break;
 +      default:
 +              die("BUG: unknown ref type %d of ref %s",
 +                  ref_type(refname), refname);
 +      }
 +}
 +
  /*
   * Get the packed_ref_cache for the specified files_ref_store,
   * creating it if necessary.
   */
  static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
  {
 -      char *packed_refs_file;
 -
 -      if (*refs->base.submodule)
 -              packed_refs_file = git_pathdup_submodule(refs->base.submodule,
 -                                                       "packed-refs");
 -      else
 -              packed_refs_file = git_pathdup("packed-refs");
 +      const char *packed_refs_file = files_packed_refs_path(refs);
  
        if (refs->packed &&
            !stat_validity_check(&refs->packed->validity, packed_refs_file))
                        fclose(f);
                }
        }
 -      free(packed_refs_file);
        return refs->packed;
  }
  
@@@ -1281,10 -1201,19 +1281,10 @@@ static void read_loose_refs(const char 
        struct strbuf refname;
        struct strbuf path = STRBUF_INIT;
        size_t path_baselen;
 -      int err = 0;
  
 -      if (*refs->base.submodule)
 -              err = strbuf_git_path_submodule(&path, refs->base.submodule, "%s", dirname);
 -      else
 -              strbuf_git_path(&path, "%s", dirname);
 +      files_ref_path(refs, &path, dirname);
        path_baselen = path.len;
  
 -      if (err) {
 -              strbuf_release(&path);
 -              return;
 -      }
 -
        d = opendir(path.buf);
        if (!d) {
                strbuf_release(&path);
                                         create_dir_entry(refs, refname.buf,
                                                          refname.len, 1));
                } else {
 -                      int read_ok;
 -
 -                      if (*refs->base.submodule) {
 -                              hashclr(sha1);
 -                              flag = 0;
 -                              read_ok = !resolve_gitlink_ref(refs->base.submodule,
 -                                                             refname.buf, sha1);
 -                      } else {
 -                              read_ok = !read_ref_full(refname.buf,
 -                                                       RESOLVE_REF_READING,
 -                                                       sha1, &flag);
 -                      }
 -
 -                      if (!read_ok) {
 +                      if (!refs_resolve_ref_unsafe(&refs->base,
 +                                                   refname.buf,
 +                                                   RESOLVE_REF_READING,
 +                                                   sha1, &flag)) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
                        } else if (is_null_sha1(sha1)) {
@@@ -1405,7 -1344,7 +1405,7 @@@ static int files_read_raw_ref(struct re
                              struct strbuf *referent, unsigned int *type)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 1, "read_raw_ref");
 +              files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
        struct strbuf sb_contents = STRBUF_INIT;
        struct strbuf sb_path = STRBUF_INIT;
        const char *path;
        *type = 0;
        strbuf_reset(&sb_path);
  
 -      if (*refs->base.submodule)
 -              strbuf_git_path_submodule(&sb_path, refs->base.submodule, "%s", refname);
 -      else
 -              strbuf_git_path(&sb_path, "%s", refname);
 +      files_ref_path(refs, &sb_path, refname);
  
        path = sb_path.buf;
  
@@@ -1598,7 -1540,7 +1598,7 @@@ static int lock_raw_ref(struct files_re
        int ret = TRANSACTION_GENERIC_ERROR;
  
        assert(err);
 -      assert_main_repository(&refs->base, "lock_raw_ref");
 +      files_assert_main_repository(refs, "lock_raw_ref");
  
        *type = 0;
  
        *lock_p = lock = xcalloc(1, sizeof(*lock));
  
        lock->ref_name = xstrdup(refname);
 -      strbuf_git_path(&ref_file, "%s", refname);
 +      files_ref_path(refs, &ref_file, refname);
  
  retry:
        switch (safe_create_leading_directories(ref_file.buf)) {
                 * another reference such as "refs/foo". There is no
                 * reason to expect this error to be transitory.
                 */
 -              if (verify_refname_available(refname, extras, skip, err)) {
 +              if (refs_verify_refname_available(&refs->base, refname,
 +                                                extras, skip, err)) {
                        if (mustexist) {
                                /*
                                 * To the user the relevant error is
@@@ -1823,9 -1764,7 +1823,9 @@@ static enum peel_status peel_entry(stru
  static int files_peel_ref(struct ref_store *ref_store,
                          const char *refname, unsigned char *sha1)
  {
 -      struct files_ref_store *refs = files_downcast(ref_store, 0, "peel_ref");
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,
 +                             "peel_ref");
        int flag;
        unsigned char base[20];
  
                return 0;
        }
  
 -      if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
 +      if (refs_read_ref_full(ref_store, refname,
 +                             RESOLVE_REF_READING, base, &flag))
                return -1;
  
        /*
@@@ -1934,21 -1872,21 +1934,21 @@@ static struct ref_iterator *files_ref_i
                struct ref_store *ref_store,
                const char *prefix, unsigned int flags)
  {
 -      struct files_ref_store *refs =
 -              files_downcast(ref_store, 1, "ref_iterator_begin");
 +      struct files_ref_store *refs;
        struct ref_dir *loose_dir, *packed_dir;
        struct ref_iterator *loose_iter, *packed_iter;
        struct files_ref_iterator *iter;
        struct ref_iterator *ref_iterator;
  
 -      if (!refs)
 -              return empty_ref_iterator_begin();
 -
        if (ref_paranoia < 0)
                ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
        if (ref_paranoia)
                flags |= DO_FOR_EACH_INCLUDE_BROKEN;
  
 +      refs = files_downcast(ref_store,
 +                            REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),
 +                            "ref_iterator_begin");
 +
        iter = xcalloc(1, sizeof(*iter));
        ref_iterator = &iter->base;
        base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);
   * on success. On error, write an error message to err, set errno, and
   * return a negative value.
   */
 -static int verify_lock(struct ref_lock *lock,
 +static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,
                       const unsigned char *old_sha1, int mustexist,
                       struct strbuf *err)
  {
        assert(err);
  
 -      if (read_ref_full(lock->ref_name,
 -                        mustexist ? RESOLVE_REF_READING : 0,
 -                        lock->old_oid.hash, NULL)) {
 +      if (refs_read_ref_full(ref_store, lock->ref_name,
 +                             mustexist ? RESOLVE_REF_READING : 0,
 +                             lock->old_oid.hash, NULL)) {
                if (old_sha1) {
                        int save_errno = errno;
                        strbuf_addf(err, "can't verify ref '%s'", lock->ref_name);
@@@ -2047,13 -1985,6 +2047,13 @@@ static int remove_empty_directories(str
        return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
  }
  
 +static int create_reflock(const char *path, void *cb)
 +{
 +      struct lock_file *lk = cb;
 +
 +      return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0;
 +}
 +
  /*
   * Locks a ref returning the lock on success and NULL on failure.
   * On failure errno is set to something meaningful.
@@@ -2069,11 -2000,13 +2069,11 @@@ static struct ref_lock *lock_ref_sha1_b
        struct strbuf ref_file = STRBUF_INIT;
        struct ref_lock *lock;
        int last_errno = 0;
 -      int lflags = LOCK_NO_DEREF;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
        int resolve_flags = RESOLVE_REF_NO_RECURSE;
 -      int attempts_remaining = 3;
        int resolved;
  
 -      assert_main_repository(&refs->base, "lock_ref_sha1_basic");
 +      files_assert_main_repository(refs, "lock_ref_sha1_basic");
        assert(err);
  
        lock = xcalloc(1, sizeof(struct ref_lock));
        if (flags & REF_DELETING)
                resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
  
 -      strbuf_git_path(&ref_file, "%s", refname);
 -      resolved = !!resolve_ref_unsafe(refname, resolve_flags,
 -                                      lock->old_oid.hash, type);
 +      files_ref_path(refs, &ref_file, refname);
 +      resolved = !!refs_resolve_ref_unsafe(&refs->base,
 +                                           refname, resolve_flags,
 +                                           lock->old_oid.hash, type);
        if (!resolved && errno == EISDIR) {
                /*
                 * we are trying to lock foo but we used to
                                            refname);
                        goto error_return;
                }
 -              resolved = !!resolve_ref_unsafe(refname, resolve_flags,
 -                                              lock->old_oid.hash, type);
 +              resolved = !!refs_resolve_ref_unsafe(&refs->base,
 +                                                   refname, resolve_flags,
 +                                                   lock->old_oid.hash, type);
        }
        if (!resolved) {
                last_errno = errno;
  
        lock->ref_name = xstrdup(refname);
  
 - retry:
 -      switch (safe_create_leading_directories_const(ref_file.buf)) {
 -      case SCLD_OK:
 -              break; /* success */
 -      case SCLD_VANISHED:
 -              if (--attempts_remaining > 0)
 -                      goto retry;
 -              /* fall through */
 -      default:
 +      if (raceproof_create_file(ref_file.buf, create_reflock, lock->lk)) {
                last_errno = errno;
 -              strbuf_addf(err, "unable to create directory for '%s'",
 -                          ref_file.buf);
 +              unable_to_lock_message(ref_file.buf, errno, err);
                goto error_return;
        }
  
 -      if (hold_lock_file_for_update(lock->lk, ref_file.buf, lflags) < 0) {
 -              last_errno = errno;
 -              if (errno == ENOENT && --attempts_remaining > 0)
 -                      /*
 -                       * Maybe somebody just deleted one of the
 -                       * directories leading to ref_file.  Try
 -                       * again:
 -                       */
 -                      goto retry;
 -              else {
 -                      unable_to_lock_message(ref_file.buf, errno, err);
 -                      goto error_return;
 -              }
 -      }
 -      if (verify_lock(lock, old_sha1, mustexist, err)) {
 +      if (verify_lock(&refs->base, lock, old_sha1, mustexist, err)) {
                last_errno = errno;
                goto error_return;
        }
@@@ -2198,7 -2152,7 +2198,7 @@@ static int lock_packed_refs(struct file
        static int timeout_value = 1000;
        struct packed_ref_cache *packed_ref_cache;
  
 -      assert_main_repository(&refs->base, "lock_packed_refs");
 +      files_assert_main_repository(refs, "lock_packed_refs");
  
        if (!timeout_configured) {
                git_config_get_int("core.packedrefstimeout", &timeout_value);
        }
  
        if (hold_lock_file_for_update_timeout(
 -                          &packlock, git_path("packed-refs"),
 +                          &packlock, files_packed_refs_path(refs),
                            flags, timeout_value) < 0)
                return -1;
        /*
@@@ -2236,7 -2190,7 +2236,7 @@@ static int commit_packed_refs(struct fi
        int save_errno = 0;
        FILE *out;
  
 -      assert_main_repository(&refs->base, "commit_packed_refs");
 +      files_assert_main_repository(refs, "commit_packed_refs");
  
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
@@@ -2269,7 -2223,7 +2269,7 @@@ static void rollback_packed_refs(struc
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(refs);
  
 -      assert_main_repository(&refs->base, "rollback_packed_refs");
 +      files_assert_main_repository(refs, "rollback_packed_refs");
  
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
@@@ -2344,28 -2298,15 +2344,28 @@@ static int pack_if_possible_fn(struct r
        return 0;
  }
  
 +enum {
 +      REMOVE_EMPTY_PARENTS_REF = 0x01,
 +      REMOVE_EMPTY_PARENTS_REFLOG = 0x02
 +};
 +
  /*
 - * Remove empty parents, but spare refs/ and immediate subdirs.
 - * Note: munges *name.
 + * Remove empty parent directories associated with the specified
 + * reference and/or its reflog, but spare [logs/]refs/ and immediate
 + * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or
 + * REMOVE_EMPTY_PARENTS_REFLOG.
   */
 -static void try_remove_empty_parents(char *name)
 +static void try_remove_empty_parents(struct files_ref_store *refs,
 +                                   const char *refname,
 +                                   unsigned int flags)
  {
 +      struct strbuf buf = STRBUF_INIT;
 +      struct strbuf sb = STRBUF_INIT;
        char *p, *q;
        int i;
 -      p = name;
 +
 +      strbuf_addstr(&buf, refname);
 +      p = buf.buf;
        for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
                while (*p && *p != '/')
                        p++;
                while (*p == '/')
                        p++;
        }
 -      for (q = p; *q; q++)
 -              ;
 -      while (1) {
 +      q = buf.buf + buf.len;
 +      while (flags & (REMOVE_EMPTY_PARENTS_REF | REMOVE_EMPTY_PARENTS_REFLOG)) {
                while (q > p && *q != '/')
                        q--;
                while (q > p && *(q-1) == '/')
                        q--;
                if (q == p)
                        break;
 -              *q = '\0';
 -              if (rmdir(git_path("%s", name)))
 -                      break;
 +              strbuf_setlen(&buf, q - buf.buf);
 +
 +              strbuf_reset(&sb);
 +              files_ref_path(refs, &sb, buf.buf);
 +              if ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))
 +                      flags &= ~REMOVE_EMPTY_PARENTS_REF;
 +
 +              strbuf_reset(&sb);
 +              files_reflog_path(refs, &sb, buf.buf);
 +              if ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))
 +                      flags &= ~REMOVE_EMPTY_PARENTS_REFLOG;
        }
 +      strbuf_release(&buf);
 +      strbuf_release(&sb);
  }
  
  /* make sure nobody touched the ref, and unlink */
 -static void prune_ref(struct ref_to_prune *r)
 +static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
        if (check_refname_format(r->name, 0))
                return;
  
 -      transaction = ref_transaction_begin(&err);
 +      transaction = ref_store_transaction_begin(&refs->base, &err);
        if (!transaction ||
            ref_transaction_delete(transaction, r->name, r->sha1,
                                   REF_ISPRUNING | REF_NODEREF, NULL, &err) ||
        }
        ref_transaction_free(transaction);
        strbuf_release(&err);
 -      try_remove_empty_parents(r->name);
  }
  
 -static void prune_refs(struct ref_to_prune *r)
 +static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)
  {
        while (r) {
 -              prune_ref(r);
 +              prune_ref(refs, r);
                r = r->next;
        }
  }
  static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 0, "pack_refs");
 +              files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
 +                             "pack_refs");
        struct pack_refs_cb_data cbdata;
  
        memset(&cbdata, 0, sizeof(cbdata));
        if (commit_packed_refs(refs))
                die_errno("unable to overwrite old ref-pack file");
  
 -      prune_refs(cbdata.ref_to_prune);
 +      prune_refs(refs, cbdata.ref_to_prune);
        return 0;
  }
  
@@@ -2465,7 -2397,7 +2465,7 @@@ static int repack_without_refs(struct f
        struct string_list_item *refname;
        int ret, needs_repacking = 0, removed = 0;
  
 -      assert_main_repository(&refs->base, "repack_without_refs");
 +      files_assert_main_repository(refs, "repack_without_refs");
        assert(err);
  
        /* Look for a packed ref */
                return 0; /* no refname exists in packed refs */
  
        if (lock_packed_refs(refs, 0)) {
 -              unable_to_lock_message(git_path("packed-refs"), errno, err);
 +              unable_to_lock_message(files_packed_refs_path(refs), errno, err);
                return -1;
        }
        packed = get_packed_refs(refs);
        return ret;
  }
  
 -static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
 -{
 -      assert(err);
 -
 -      if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
 -              /*
 -               * loose.  The loose file name is the same as the
 -               * lockfile name, minus ".lock":
 -               */
 -              char *loose_filename = get_locked_file_path(lock->lk);
 -              int res = unlink_or_msg(loose_filename, err);
 -              free(loose_filename);
 -              if (res)
 -                      return 1;
 -      }
 -      return 0;
 -}
 -
  static int files_delete_refs(struct ref_store *ref_store,
                             struct string_list *refnames, unsigned int flags)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 0, "delete_refs");
 +              files_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
        struct strbuf err = STRBUF_INIT;
        int i, result = 0;
  
        for (i = 0; i < refnames->nr; i++) {
                const char *refname = refnames->items[i].string;
  
 -              if (delete_ref(refname, NULL, flags))
 +              if (refs_delete_ref(&refs->base, NULL, refname, NULL, flags))
                        result |= error(_("could not remove reference %s"), refname);
        }
  
   * IOW, to avoid cross device rename errors, the temporary renamed log must
   * live into logs/refs.
   */
 -#define TMP_RENAMED_LOG  "logs/refs/.tmp-renamed-log"
 +#define TMP_RENAMED_LOG  "refs/.tmp-renamed-log"
  
 -static int rename_tmp_log(const char *newrefname)
 +struct rename_cb {
 +      const char *tmp_renamed_log;
 +      int true_errno;
 +};
 +
 +static int rename_tmp_log_callback(const char *path, void *cb_data)
  {
 -      int attempts_remaining = 4;
 -      struct strbuf path = STRBUF_INIT;
 -      int ret = -1;
 +      struct rename_cb *cb = cb_data;
  
 - retry:
 -      strbuf_reset(&path);
 -      strbuf_git_path(&path, "logs/%s", newrefname);
 -      switch (safe_create_leading_directories_const(path.buf)) {
 -      case SCLD_OK:
 -              break; /* success */
 -      case SCLD_VANISHED:
 -              if (--attempts_remaining > 0)
 -                      goto retry;
 -              /* fall through */
 -      default:
 -              error("unable to create directory for %s", newrefname);
 -              goto out;
 +      if (rename(cb->tmp_renamed_log, path)) {
 +              /*
 +               * rename(a, b) when b is an existing directory ought
 +               * to result in ISDIR, but Solaris 5.8 gives ENOTDIR.
 +               * Sheesh. Record the true errno for error reporting,
 +               * but report EISDIR to raceproof_create_file() so
 +               * that it knows to retry.
 +               */
 +              cb->true_errno = errno;
 +              if (errno == ENOTDIR)
 +                      errno = EISDIR;
 +              return -1;
 +      } else {
 +              return 0;
        }
 +}
  
 -      if (rename(git_path(TMP_RENAMED_LOG), path.buf)) {
 -              if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
 -                      /*
 -                       * rename(a, b) when b is an existing
 -                       * directory ought to result in ISDIR, but
 -                       * Solaris 5.8 gives ENOTDIR.  Sheesh.
 -                       */
 -                      if (remove_empty_directories(&path)) {
 -                              error("Directory not empty: logs/%s", newrefname);
 -                              goto out;
 -                      }
 -                      goto retry;
 -              } else if (errno == ENOENT && --attempts_remaining > 0) {
 -                      /*
 -                       * Maybe another process just deleted one of
 -                       * the directories in the path to newrefname.
 -                       * Try again from the beginning.
 -                       */
 -                      goto retry;
 -              } else {
 -                      error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
 -                              newrefname, strerror(errno));
 -                      goto out;
 -              }
 +static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
 +{
 +      struct strbuf path = STRBUF_INIT;
 +      struct strbuf tmp = STRBUF_INIT;
 +      struct rename_cb cb;
 +      int ret;
 +
 +      files_reflog_path(refs, &path, newrefname);
 +      files_reflog_path(refs, &tmp, TMP_RENAMED_LOG);
 +      cb.tmp_renamed_log = tmp.buf;
 +      ret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);
 +      if (ret) {
 +              if (errno == EISDIR)
 +                      error("directory not empty: %s", path.buf);
 +              else
 +                      error("unable to move logfile %s to %s: %s",
 +                            tmp.buf, path.buf,
 +                            strerror(cb.true_errno));
        }
 -      ret = 0;
 -out:
 +
        strbuf_release(&path);
 +      strbuf_release(&tmp);
        return ret;
  }
  
@@@ -2615,7 -2566,7 +2615,7 @@@ static int files_verify_refname_availab
                                          struct strbuf *err)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 1, "verify_refname_available");
 +              files_downcast(ref_store, REF_STORE_READ, "verify_refname_available");
        struct ref_dir *packed_refs = get_packed_refs(refs);
        struct ref_dir *loose_refs = get_loose_refs(refs);
  
@@@ -2640,52 -2591,32 +2640,52 @@@ static int files_rename_ref(struct ref_
                            const char *logmsg)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 0, "rename_ref");
 +              files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
        unsigned char sha1[20], orig_sha1[20];
        int flag = 0, logmoved = 0;
        struct ref_lock *lock;
        struct stat loginfo;
 -      int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
 +      struct strbuf sb_oldref = STRBUF_INIT;
 +      struct strbuf sb_newref = STRBUF_INIT;
 +      struct strbuf tmp_renamed_log = STRBUF_INIT;
 +      int log, ret;
        struct strbuf err = STRBUF_INIT;
  
 -      if (log && S_ISLNK(loginfo.st_mode))
 -              return error("reflog for %s is a symlink", oldrefname);
 +      files_reflog_path(refs, &sb_oldref, oldrefname);
 +      files_reflog_path(refs, &sb_newref, newrefname);
 +      files_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);
  
 -      if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
 -                              orig_sha1, &flag))
 -              return error("refname %s not found", oldrefname);
 +      log = !lstat(sb_oldref.buf, &loginfo);
 +      if (log && S_ISLNK(loginfo.st_mode)) {
 +              ret = error("reflog for %s is a symlink", oldrefname);
 +              goto out;
 +      }
  
 -      if (flag & REF_ISSYMREF)
 -              return error("refname %s is a symbolic ref, renaming it is not supported",
 -                      oldrefname);
 -      if (!rename_ref_available(oldrefname, newrefname))
 -              return 1;
 +      if (!refs_resolve_ref_unsafe(&refs->base, oldrefname,
 +                                   RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
 +                              orig_sha1, &flag)) {
 +              ret = error("refname %s not found", oldrefname);
 +              goto out;
 +      }
  
 -      if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
 -              return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
 -                      oldrefname, strerror(errno));
 +      if (flag & REF_ISSYMREF) {
 +              ret = error("refname %s is a symbolic ref, renaming it is not supported",
 +                          oldrefname);
 +              goto out;
 +      }
 +      if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {
 +              ret = 1;
 +              goto out;
 +      }
 +
 +      if (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
 +              ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
 +                          oldrefname, strerror(errno));
 +              goto out;
 +      }
  
 -      if (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {
 +      if (refs_delete_ref(&refs->base, logmsg, oldrefname,
 +                          orig_sha1, REF_NODEREF)) {
                error("unable to delete old %s", oldrefname);
                goto rollback;
        }
         * the safety anyway; we want to delete the reference whatever
         * its current value.
         */
 -      if (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
 -                         sha1, NULL) &&
 -          delete_ref(newrefname, NULL, REF_NODEREF)) {
 -              if (errno==EISDIR) {
 +      if (!refs_read_ref_full(&refs->base, newrefname,
 +                              RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
 +                              sha1, NULL) &&
 +          refs_delete_ref(&refs->base, NULL, newrefname,
 +                          NULL, REF_NODEREF)) {
 +              if (errno == EISDIR) {
                        struct strbuf path = STRBUF_INIT;
                        int result;
  
 -                      strbuf_git_path(&path, "%s", newrefname);
 +                      files_ref_path(refs, &path, newrefname);
                        result = remove_empty_directories(&path);
                        strbuf_release(&path);
  
                }
        }
  
 -      if (log && rename_tmp_log(newrefname))
 +      if (log && rename_tmp_log(refs, newrefname))
                goto rollback;
  
        logmoved = log;
                goto rollback;
        }
  
 -      return 0;
 +      ret = 0;
 +      goto out;
  
   rollback:
        lock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,
        log_all_ref_updates = flag;
  
   rollbacklog:
 -      if (logmoved && rename(git_path("logs/%s", newrefname), git_path("logs/%s", oldrefname)))
 +      if (logmoved && rename(sb_newref.buf, sb_oldref.buf))
                error("unable to restore logfile %s from %s: %s",
                        oldrefname, newrefname, strerror(errno));
        if (!logmoved && log &&
 -          rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldrefname)))
 -              error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
 +          rename(tmp_renamed_log.buf, sb_oldref.buf))
 +              error("unable to restore logfile %s from logs/"TMP_RENAMED_LOG": %s",
                        oldrefname, strerror(errno));
 +      ret = 1;
 + out:
 +      strbuf_release(&sb_newref);
 +      strbuf_release(&sb_oldref);
 +      strbuf_release(&tmp_renamed_log);
  
 -      return 1;
 +      return ret;
  }
  
  static int close_ref(struct ref_lock *lock)
@@@ -2817,93 -2740,66 +2817,93 @@@ static int commit_ref(struct ref_lock *
        return 0;
  }
  
 +static int open_or_create_logfile(const char *path, void *cb)
 +{
 +      int *fd = cb;
 +
 +      *fd = open(path, O_APPEND | O_WRONLY | O_CREAT, 0666);
 +      return (*fd < 0) ? -1 : 0;
 +}
 +
  /*
 - * Create a reflog for a ref.  If force_create = 0, the reflog will
 - * only be created for certain refs (those for which
 - * should_autocreate_reflog returns non-zero.  Otherwise, create it
 - * regardless of the ref name.  Fill in *err and return -1 on failure.
 + * Create a reflog for a ref. If force_create = 0, only create the
 + * reflog for certain refs (those for which should_autocreate_reflog
 + * returns non-zero). Otherwise, create it regardless of the reference
 + * name. If the logfile already existed or was created, return 0 and
 + * set *logfd to the file descriptor opened for appending to the file.
 + * If no logfile exists and we decided not to create one, return 0 and
 + * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and
 + * return -1.
   */
 -static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
 +static int log_ref_setup(struct files_ref_store *refs,
 +                       const char *refname, int force_create,
 +                       int *logfd, struct strbuf *err)
  {
 -      int logfd, oflags = O_APPEND | O_WRONLY;
 +      struct strbuf logfile_sb = STRBUF_INIT;
 +      char *logfile;
  
 -      strbuf_git_path(logfile, "logs/%s", refname);
 -      if (force_create || should_autocreate_reflog(refname)) {
 -              if (safe_create_leading_directories(logfile->buf) < 0) {
 -                      strbuf_addf(err, "unable to create directory for '%s': "
 -                                  "%s", logfile->buf, strerror(errno));
 -                      return -1;
 -              }
 -              oflags |= O_CREAT;
 -      }
 +      files_reflog_path(refs, &logfile_sb, refname);
 +      logfile = strbuf_detach(&logfile_sb, NULL);
  
 -      logfd = open(logfile->buf, oflags, 0666);
 -      if (logfd < 0) {
 -              if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
 -                      return 0;
 +      if (force_create || should_autocreate_reflog(refname)) {
 +              if (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {
 +                      if (errno == ENOENT)
 +                              strbuf_addf(err, "unable to create directory for '%s': "
 +                                          "%s", logfile, strerror(errno));
 +                      else if (errno == EISDIR)
 +                              strbuf_addf(err, "there are still logs under '%s'",
 +                                          logfile);
 +                      else
 +                              strbuf_addf(err, "unable to append to '%s': %s",
 +                                          logfile, strerror(errno));
  
 -              if (errno == EISDIR) {
 -                      if (remove_empty_directories(logfile)) {
 -                              strbuf_addf(err, "there are still logs under "
 -                                          "'%s'", logfile->buf);
 -                              return -1;
 -                      }
 -                      logfd = open(logfile->buf, oflags, 0666);
 +                      goto error;
                }
 -
 -              if (logfd < 0) {
 -                      strbuf_addf(err, "unable to append to '%s': %s",
 -                                  logfile->buf, strerror(errno));
 -                      return -1;
 +      } else {
 +              *logfd = open(logfile, O_APPEND | O_WRONLY, 0666);
 +              if (*logfd < 0) {
 +                      if (errno == ENOENT || errno == EISDIR) {
 +                              /*
 +                               * The logfile doesn't already exist,
 +                               * but that is not an error; it only
 +                               * means that we won't write log
 +                               * entries to it.
 +                               */
 +                              ;
 +                      } else {
 +                              strbuf_addf(err, "unable to append to '%s': %s",
 +                                          logfile, strerror(errno));
 +                              goto error;
 +                      }
                }
        }
  
 -      adjust_shared_perm(logfile->buf);
 -      close(logfd);
 +      if (*logfd >= 0)
 +              adjust_shared_perm(logfile);
 +
 +      free(logfile);
        return 0;
 -}
  
 +error:
 +      free(logfile);
 +      return -1;
 +}
  
  static int files_create_reflog(struct ref_store *ref_store,
                               const char *refname, int force_create,
                               struct strbuf *err)
  {
 -      int ret;
 -      struct strbuf sb = STRBUF_INIT;
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_WRITE, "create_reflog");
 +      int fd;
  
 -      /* Check validity (but we don't need the result): */
 -      files_downcast(ref_store, 0, "create_reflog");
 +      if (log_ref_setup(refs, refname, force_create, &fd, err))
 +              return -1;
  
 -      ret = log_ref_setup(refname, &sb, err, force_create);
 -      strbuf_release(&sb);
 -      return ret;
 +      if (fd >= 0)
 +              close(fd);
 +
 +      return 0;
  }
  
  static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
        return 0;
  }
  
 -static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
 -                         const unsigned char *new_sha1, const char *msg,
 -                         struct strbuf *logfile, int flags,
 -                         struct strbuf *err)
 +static int files_log_ref_write(struct files_ref_store *refs,
 +                             const char *refname, const unsigned char *old_sha1,
 +                             const unsigned char *new_sha1, const char *msg,
 +                             int flags, struct strbuf *err)
  {
 -      int logfd, result, oflags = O_APPEND | O_WRONLY;
 +      int logfd, result;
  
        if (log_all_ref_updates == LOG_REFS_UNSET)
                log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
  
 -      result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
 +      result = log_ref_setup(refs, refname,
 +                             flags & REF_FORCE_CREATE_REFLOG,
 +                             &logfd, err);
  
        if (result)
                return result;
  
 -      logfd = open(logfile->buf, oflags);
        if (logfd < 0)
                return 0;
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
 -              strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
 -                          strerror(errno));
 +              struct strbuf sb = STRBUF_INIT;
 +              int save_errno = errno;
 +
 +              files_reflog_path(refs, &sb, refname);
 +              strbuf_addf(err, "unable to append to '%s': %s",
 +                          sb.buf, strerror(save_errno));
 +              strbuf_release(&sb);
                close(logfd);
                return -1;
        }
        if (close(logfd)) {
 -              strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
 -                          strerror(errno));
 +              struct strbuf sb = STRBUF_INIT;
 +              int save_errno = errno;
 +
 +              files_reflog_path(refs, &sb, refname);
 +              strbuf_addf(err, "unable to append to '%s': %s",
 +                          sb.buf, strerror(save_errno));
 +              strbuf_release(&sb);
                return -1;
        }
        return 0;
  }
  
 -static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 -                       const unsigned char *new_sha1, const char *msg,
 -                       int flags, struct strbuf *err)
 -{
 -      return files_log_ref_write(refname, old_sha1, new_sha1, msg, flags,
 -                                 err);
 -}
 -
 -int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
 -                      const unsigned char *new_sha1, const char *msg,
 -                      int flags, struct strbuf *err)
 -{
 -      struct strbuf sb = STRBUF_INIT;
 -      int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
 -                                err);
 -      strbuf_release(&sb);
 -      return ret;
 -}
 -
  /*
   * Write sha1 into the open lockfile, then close the lockfile. On
   * errors, rollback the lockfile, fill in *err and
@@@ -3026,12 -2930,10 +3026,12 @@@ static int commit_ref_update(struct fil
                             const unsigned char *sha1, const char *logmsg,
                             struct strbuf *err)
  {
 -      assert_main_repository(&refs->base, "commit_ref_update");
 +      files_assert_main_repository(refs, "commit_ref_update");
  
        clear_loose_ref_cache(refs);
 -      if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
 +      if (files_log_ref_write(refs, lock->ref_name,
 +                              lock->old_oid.hash, sha1,
 +                              logmsg, 0, err)) {
                char *old_msg = strbuf_detach(err, NULL);
                strbuf_addf(err, "cannot update the ref '%s': %s",
                            lock->ref_name, old_msg);
                int head_flag;
                const char *head_ref;
  
 -              head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
 -                                            head_sha1, &head_flag);
 +              head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD",
 +                                                 RESOLVE_REF_READING,
 +                                                 head_sha1, &head_flag);
                if (head_ref && (head_flag & REF_ISSYMREF) &&
                    !strcmp(head_ref, lock->ref_name)) {
                        struct strbuf log_err = STRBUF_INIT;
 -                      if (log_ref_write("HEAD", lock->old_oid.hash, sha1,
 -                                        logmsg, 0, &log_err)) {
 +                      if (files_log_ref_write(refs, "HEAD",
 +                                              lock->old_oid.hash, sha1,
 +                                              logmsg, 0, &log_err)) {
                                error("%s", log_err.buf);
                                strbuf_release(&log_err);
                        }
@@@ -3097,28 -2997,23 +3097,28 @@@ static int create_ref_symlink(struct re
        return ret;
  }
  
 -static void update_symref_reflog(struct ref_lock *lock, const char *refname,
 +static void update_symref_reflog(struct files_ref_store *refs,
 +                               struct ref_lock *lock, const char *refname,
                                 const char *target, const char *logmsg)
  {
        struct strbuf err = STRBUF_INIT;
        unsigned char new_sha1[20];
 -      if (logmsg && !read_ref(target, new_sha1) &&
 -          log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {
 +      if (logmsg &&
 +          !refs_read_ref_full(&refs->base, target,
 +                              RESOLVE_REF_READING, new_sha1, NULL) &&
 +          files_log_ref_write(refs, refname, lock->old_oid.hash,
 +                              new_sha1, logmsg, 0, &err)) {
                error("%s", err.buf);
                strbuf_release(&err);
        }
  }
  
 -static int create_symref_locked(struct ref_lock *lock, const char *refname,
 +static int create_symref_locked(struct files_ref_store *refs,
 +                              struct ref_lock *lock, const char *refname,
                                const char *target, const char *logmsg)
  {
        if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
 -              update_symref_reflog(lock, refname, target, logmsg);
 +              update_symref_reflog(refs, lock, refname, target, logmsg);
                return 0;
        }
  
                return error("unable to fdopen %s: %s",
                             lock->lk->tempfile.filename.buf, strerror(errno));
  
 -      update_symref_reflog(lock, refname, target, logmsg);
 +      update_symref_reflog(refs, lock, refname, target, logmsg);
  
        /* no error check; commit_ref will check ferror */
        fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
@@@ -3141,7 -3036,7 +3141,7 @@@ static int files_create_symref(struct r
                               const char *logmsg)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 0, "create_symref");
 +              files_downcast(ref_store, REF_STORE_WRITE, "create_symref");
        struct strbuf err = STRBUF_INIT;
        struct ref_lock *lock;
        int ret;
                return -1;
        }
  
 -      ret = create_symref_locked(lock, refname, target, logmsg);
 +      ret = create_symref_locked(refs, lock, refname, target, logmsg);
        unlock_ref(lock);
        return ret;
  }
  
 -int set_worktree_head_symref(const char *gitdir, const char *target)
 +int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)
  {
 +      /*
 +       * FIXME: this obviously will not work well for future refs
 +       * backends. This function needs to die.
 +       */
 +      struct files_ref_store *refs =
 +              files_downcast(get_main_ref_store(),
 +                             REF_STORE_WRITE,
 +                             "set_head_symref");
 +
        static struct lock_file head_lock;
        struct ref_lock *lock;
        struct strbuf head_path = STRBUF_INIT;
        lock->lk = &head_lock;
        lock->ref_name = xstrdup(head_rel);
  
 -      ret = create_symref_locked(lock, head_rel, target, NULL);
 +      ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
  
        unlock_ref(lock); /* will free lock */
        strbuf_release(&head_path);
  static int files_reflog_exists(struct ref_store *ref_store,
                               const char *refname)
  {
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_READ, "reflog_exists");
 +      struct strbuf sb = STRBUF_INIT;
        struct stat st;
 +      int ret;
  
 -      /* Check validity (but we don't need the result): */
 -      files_downcast(ref_store, 0, "reflog_exists");
 -
 -      return !lstat(git_path("logs/%s", refname), &st) &&
 -              S_ISREG(st.st_mode);
 +      files_reflog_path(refs, &sb, refname);
 +      ret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);
 +      strbuf_release(&sb);
 +      return ret;
  }
  
  static int files_delete_reflog(struct ref_store *ref_store,
                               const char *refname)
  {
 -      /* Check validity (but we don't need the result): */
 -      files_downcast(ref_store, 0, "delete_reflog");
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_WRITE, "delete_reflog");
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret;
  
 -      return remove_path(git_path("logs/%s", refname));
 +      files_reflog_path(refs, &sb, refname);
 +      ret = remove_path(sb.buf);
 +      strbuf_release(&sb);
 +      return ret;
  }
  
  static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
  {
 -      unsigned char osha1[20], nsha1[20];
 +      struct object_id ooid, noid;
        char *email_end, *message;
        unsigned long timestamp;
        int tz;
 +      const char *p = sb->buf;
  
        /* old SP new SP name <email> SP time TAB msg LF */
 -      if (sb->len < 83 || sb->buf[sb->len - 1] != '\n' ||
 -          get_sha1_hex(sb->buf, osha1) || sb->buf[40] != ' ' ||
 -          get_sha1_hex(sb->buf + 41, nsha1) || sb->buf[81] != ' ' ||
 -          !(email_end = strchr(sb->buf + 82, '>')) ||
 +      if (!sb->len || sb->buf[sb->len - 1] != '\n' ||
 +          parse_oid_hex(p, &ooid, &p) || *p++ != ' ' ||
 +          parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
 +          !(email_end = strchr(p, '>')) ||
            email_end[1] != ' ' ||
            !(timestamp = strtoul(email_end + 2, &message, 10)) ||
            !message || message[0] != ' ' ||
                message += 6;
        else
                message += 7;
 -      return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data);
 +      return fn(&ooid, &noid, p, timestamp, tz, message, cb_data);
  }
  
  static char *find_beginning_of_line(char *bob, char *scan)
@@@ -3278,24 -3155,22 +3278,24 @@@ static int files_for_each_reflog_ent_re
                                             each_reflog_ent_fn fn,
                                             void *cb_data)
  {
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_READ,
 +                             "for_each_reflog_ent_reverse");
        struct strbuf sb = STRBUF_INIT;
        FILE *logfp;
        long pos;
        int ret = 0, at_tail = 1;
  
 -      /* Check validity (but we don't need the result): */
 -      files_downcast(ref_store, 0, "for_each_reflog_ent_reverse");
 -
 -      logfp = fopen(git_path("logs/%s", refname), "r");
 +      files_reflog_path(refs, &sb, refname);
 +      logfp = fopen(sb.buf, "r");
 +      strbuf_release(&sb);
        if (!logfp)
                return -1;
  
        /* Jump to the end */
        if (fseek(logfp, 0, SEEK_END) < 0)
-               return error("cannot seek back reflog for %s: %s",
-                            refname, strerror(errno));
+               ret = error("cannot seek back reflog for %s: %s",
+                           refname, strerror(errno));
        pos = ftell(logfp);
        while (!ret && 0 < pos) {
                int cnt;
  
                /* Fill next block from the end */
                cnt = (sizeof(buf) < pos) ? sizeof(buf) : pos;
-               if (fseek(logfp, pos - cnt, SEEK_SET))
-                       return error("cannot seek back reflog for %s: %s",
-                                    refname, strerror(errno));
+               if (fseek(logfp, pos - cnt, SEEK_SET)) {
+                       ret = error("cannot seek back reflog for %s: %s",
+                                   refname, strerror(errno));
+                       break;
+               }
                nread = fread(buf, cnt, 1, logfp);
-               if (nread != 1)
-                       return error("cannot read %d bytes from reflog for %s: %s",
-                                    cnt, refname, strerror(errno));
+               if (nread != 1) {
+                       ret = error("cannot read %d bytes from reflog for %s: %s",
+                                   cnt, refname, strerror(errno));
+                       break;
+               }
                pos -= cnt;
  
                scanp = endp = buf + cnt;
@@@ -3387,16 -3266,14 +3391,16 @@@ static int files_for_each_reflog_ent(st
                                     const char *refname,
                                     each_reflog_ent_fn fn, void *cb_data)
  {
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_READ,
 +                             "for_each_reflog_ent");
        FILE *logfp;
        struct strbuf sb = STRBUF_INIT;
        int ret = 0;
  
 -      /* Check validity (but we don't need the result): */
 -      files_downcast(ref_store, 0, "for_each_reflog_ent");
 -
 -      logfp = fopen(git_path("logs/%s", refname), "r");
 +      files_reflog_path(refs, &sb, refname);
 +      logfp = fopen(sb.buf, "r");
 +      strbuf_release(&sb);
        if (!logfp)
                return -1;
  
  struct files_reflog_iterator {
        struct ref_iterator base;
  
 +      struct ref_store *ref_store;
        struct dir_iterator *dir_iterator;
        struct object_id oid;
  };
@@@ -3432,9 -3308,8 +3436,9 @@@ static int files_reflog_iterator_advanc
                if (ends_with(diter->basename, ".lock"))
                        continue;
  
 -              if (read_ref_full(diter->relative_path, 0,
 -                                iter->oid.hash, &flags)) {
 +              if (refs_read_ref_full(iter->ref_store,
 +                                     diter->relative_path, 0,
 +                                     iter->oid.hash, &flags)) {
                        error("bad ref for %s", diter->path.buf);
                        continue;
                }
@@@ -3478,18 -3353,14 +3482,18 @@@ static struct ref_iterator_vtable files
  
  static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
  {
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_READ,
 +                             "reflog_iterator_begin");
        struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
        struct ref_iterator *ref_iterator = &iter->base;
 -
 -      /* Check validity (but we don't need the result): */
 -      files_downcast(ref_store, 0, "reflog_iterator_begin");
 +      struct strbuf sb = STRBUF_INIT;
  
        base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
 -      iter->dir_iterator = dir_iterator_begin(git_path("logs"));
 +      files_reflog_path(refs, &sb, NULL);
 +      iter->dir_iterator = dir_iterator_begin(sb.buf);
 +      iter->ref_store = ref_store;
 +      strbuf_release(&sb);
        return ref_iterator;
  }
  
@@@ -3693,7 -3564,7 +3697,7 @@@ static int lock_ref_for_update(struct f
        int ret;
        struct ref_lock *lock;
  
 -      assert_main_repository(&refs->base, "lock_ref_for_update");
 +      files_assert_main_repository(refs, "lock_ref_for_update");
  
        if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
                update->flags |= REF_DELETING;
                         * the transaction, so we have to read it here
                         * to record and possibly check old_sha1:
                         */
 -                      if (read_ref_full(referent.buf, 0,
 -                                        lock->old_oid.hash, NULL)) {
 +                      if (refs_read_ref_full(&refs->base,
 +                                             referent.buf, 0,
 +                                             lock->old_oid.hash, NULL)) {
                                if (update->flags & REF_HAVE_OLD) {
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
@@@ -3820,8 -3690,7 +3824,8 @@@ static int files_transaction_commit(str
                                    struct strbuf *err)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 0, "ref_transaction_commit");
 +              files_downcast(ref_store, REF_STORE_WRITE,
 +                             "ref_transaction_commit");
        int ret = 0, i;
        struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
        struct string_list_item *ref_to_delete;
        char *head_ref = NULL;
        int head_type;
        struct object_id head_oid;
 +      struct strbuf sb = STRBUF_INIT;
  
        assert(err);
  
         * head_ref within the transaction, then split_head_update()
         * arranges for the reflog of HEAD to be updated, too.
         */
 -      head_ref = resolve_refdup("HEAD", RESOLVE_REF_NO_RECURSE,
 -                                head_oid.hash, &head_type);
 +      head_ref = refs_resolve_refdup(ref_store, "HEAD",
 +                                     RESOLVE_REF_NO_RECURSE,
 +                                     head_oid.hash, &head_type);
  
        if (head_ref && !(head_type & REF_ISSYMREF)) {
                free(head_ref);
  
                if (update->flags & REF_NEEDS_COMMIT ||
                    update->flags & REF_LOG_ONLY) {
 -                      if (log_ref_write(lock->ref_name, lock->old_oid.hash,
 -                                        update->new_sha1,
 -                                        update->msg, update->flags, err)) {
 +                      if (files_log_ref_write(refs,
 +                                              lock->ref_name,
 +                                              lock->old_oid.hash,
 +                                              update->new_sha1,
 +                                              update->msg, update->flags,
 +                                              err)) {
                                char *old_msg = strbuf_detach(err, NULL);
  
                                strbuf_addf(err, "cannot update the ref '%s': %s",
  
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY)) {
 -                      if (delete_ref_loose(lock, update->type, err)) {
 -                              ret = TRANSACTION_GENERIC_ERROR;
 -                              goto cleanup;
 +                      if (!(update->type & REF_ISPACKED) ||
 +                          update->type & REF_ISSYMREF) {
 +                              /* It is a loose reference. */
 +                              strbuf_reset(&sb);
 +                              files_ref_path(refs, &sb, lock->ref_name);
 +                              if (unlink_or_msg(sb.buf, err)) {
 +                                      ret = TRANSACTION_GENERIC_ERROR;
 +                                      goto cleanup;
 +                              }
 +                              update->flags |= REF_DELETED_LOOSE;
                        }
  
                        if (!(update->flags & REF_ISPRUNING))
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
 -      for_each_string_list_item(ref_to_delete, &refs_to_delete)
 -              unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
 +
 +      /* Delete the reflogs of any references that were deleted: */
 +      for_each_string_list_item(ref_to_delete, &refs_to_delete) {
 +              strbuf_reset(&sb);
 +              files_reflog_path(refs, &sb, ref_to_delete->string);
 +              if (!unlink_or_warn(sb.buf))
 +                      try_remove_empty_parents(refs, ref_to_delete->string,
 +                                               REMOVE_EMPTY_PARENTS_REFLOG);
 +      }
 +
        clear_loose_ref_cache(refs);
  
  cleanup:
 +      strbuf_release(&sb);
        transaction->state = REF_TRANSACTION_CLOSED;
  
 -      for (i = 0; i < transaction->nr; i++)
 -              if (transaction->updates[i]->backend_data)
 -                      unlock_ref(transaction->updates[i]->backend_data);
 +      for (i = 0; i < transaction->nr; i++) {
 +              struct ref_update *update = transaction->updates[i];
 +              struct ref_lock *lock = update->backend_data;
 +
 +              if (lock)
 +                      unlock_ref(lock);
 +
 +              if (update->flags & REF_DELETED_LOOSE) {
 +                      /*
 +                       * The loose reference was deleted. Delete any
 +                       * empty parent directories. (Note that this
 +                       * can only work because we have already
 +                       * removed the lockfile.)
 +                       */
 +                      try_remove_empty_parents(refs, update->refname,
 +                                               REMOVE_EMPTY_PARENTS_REF);
 +              }
 +      }
 +
        string_list_clear(&refs_to_delete, 0);
        free(head_ref);
        string_list_clear(&affected_refnames, 0);
@@@ -4027,8 -3859,7 +4031,8 @@@ static int files_initial_transaction_co
                                            struct strbuf *err)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 0, "initial_ref_transaction_commit");
 +              files_downcast(ref_store, REF_STORE_WRITE,
 +                             "initial_ref_transaction_commit");
        int ret = 0, i;
        struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
  
         * so here we really only check that none of the references
         * that we are creating already exists.
         */
 -      if (for_each_rawref(ref_present, &affected_refnames))
 +      if (refs_for_each_rawref(&refs->base, ref_present,
 +                               &affected_refnames))
                die("BUG: initial ref transaction called with existing refs");
  
        for (i = 0; i < transaction->nr; i++) {
                if ((update->flags & REF_HAVE_OLD) &&
                    !is_null_sha1(update->old_sha1))
                        die("BUG: initial ref transaction with old_sha1 set");
 -              if (verify_refname_available(update->refname,
 -                                           &affected_refnames, NULL,
 -                                           err)) {
 +              if (refs_verify_refname_available(&refs->base, update->refname,
 +                                                &affected_refnames, NULL,
 +                                                err)) {
                        ret = TRANSACTION_NAME_CONFLICT;
                        goto cleanup;
                }
@@@ -4110,10 -3940,10 +4114,10 @@@ struct expire_reflog_cb 
        reflog_expiry_should_prune_fn *should_prune_fn;
        void *policy_cb;
        FILE *newlog;
 -      unsigned char last_kept_sha1[20];
 +      struct object_id last_kept_oid;
  };
  
 -static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 +static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
                             const char *email, unsigned long timestamp, int tz,
                             const char *message, void *cb_data)
  {
        struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
  
        if (cb->flags & EXPIRE_REFLOGS_REWRITE)
 -              osha1 = cb->last_kept_sha1;
 +              ooid = &cb->last_kept_oid;
  
 -      if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz,
 +      if ((*cb->should_prune_fn)(ooid->hash, noid->hash, email, timestamp, tz,
                                   message, policy_cb)) {
                if (!cb->newlog)
                        printf("would prune %s", message);
        } else {
                if (cb->newlog) {
                        fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
 -                              sha1_to_hex(osha1), sha1_to_hex(nsha1),
 +                              oid_to_hex(ooid), oid_to_hex(noid),
                                email, timestamp, tz, message);
 -                      hashcpy(cb->last_kept_sha1, nsha1);
 +                      oidcpy(&cb->last_kept_oid, noid);
                }
                if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
                        printf("keep %s", message);
@@@ -4151,11 -3981,10 +4155,11 @@@ static int files_reflog_expire(struct r
                               void *policy_cb_data)
  {
        struct files_ref_store *refs =
 -              files_downcast(ref_store, 0, "reflog_expire");
 +              files_downcast(ref_store, REF_STORE_WRITE, "reflog_expire");
        static struct lock_file reflog_lock;
        struct expire_reflog_cb cb;
        struct ref_lock *lock;
 +      struct strbuf log_file_sb = STRBUF_INIT;
        char *log_file;
        int status = 0;
        int type;
                strbuf_release(&err);
                return -1;
        }
 -      if (!reflog_exists(refname)) {
 +      if (!refs_reflog_exists(ref_store, refname)) {
                unlock_ref(lock);
                return 0;
        }
  
 -      log_file = git_pathdup("logs/%s", refname);
 +      files_reflog_path(refs, &log_file_sb, refname);
 +      log_file = strbuf_detach(&log_file_sb, NULL);
        if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
                /*
                 * Even though holding $GIT_DIR/logs/$reflog.lock has
        }
  
        (*prepare_fn)(refname, sha1, cb.policy_cb);
 -      for_each_reflog_ent(refname, expire_reflog_ent, &cb);
 +      refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);
        (*cleanup_fn)(cb.policy_cb);
  
        if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
                 */
                int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) &&
                        !(type & REF_ISSYMREF) &&
 -                      !is_null_sha1(cb.last_kept_sha1);
 +                      !is_null_oid(&cb.last_kept_oid);
  
                if (close_lock_file(&reflog_lock)) {
                        status |= error("couldn't write %s: %s", log_file,
                                        strerror(errno));
                } else if (update &&
                           (write_in_full(get_lock_file_fd(lock->lk),
 -                              sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
 +                              oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
                            write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
                            close_ref(lock) < 0)) {
                        status |= error("couldn't write %s",
  
  static int files_init_db(struct ref_store *ref_store, struct strbuf *err)
  {
 -      /* Check validity (but we don't need the result): */
 -      files_downcast(ref_store, 0, "init_db");
 +      struct files_ref_store *refs =
 +              files_downcast(ref_store, REF_STORE_WRITE, "init_db");
 +      struct strbuf sb = STRBUF_INIT;
  
        /*
         * Create .git/refs/{heads,tags}
         */
 -      safe_create_dir(git_path("refs/heads"), 1);
 -      safe_create_dir(git_path("refs/tags"), 1);
 -      if (get_shared_repository()) {
 -              adjust_shared_perm(git_path("refs/heads"));
 -              adjust_shared_perm(git_path("refs/tags"));
 -      }
 +      files_ref_path(refs, &sb, "refs/heads");
 +      safe_create_dir(sb.buf, 1);
 +
 +      strbuf_reset(&sb);
 +      files_ref_path(refs, &sb, "refs/tags");
 +      safe_create_dir(sb.buf, 1);
 +
 +      strbuf_release(&sb);
        return 0;
  }