summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--builtin/pack-refs.c2
-rw-r--r--path.c35
-rw-r--r--refs.c421
-rw-r--r--refs.h106
-rw-r--r--refs/files-backend.c548
-rw-r--r--refs/refs-internal.h64
-rw-r--r--submodule.c31
-rw-r--r--submodule.h6
-rw-r--r--t/helper/.gitignore1
-rw-r--r--t/helper/test-ref-store.c277
-rwxr-xr-xt/t1405-main-ref-store.sh129
-rwxr-xr-xt/t1406-submodule-ref-store.sh101
13 files changed, 1319 insertions, 403 deletions
diff --git a/Makefile b/Makefile
index d595ea3837..c6dbeccf5e 100644
--- a/Makefile
+++ b/Makefile
@@ -630,6 +630,7 @@ TEST_PROGRAMS_NEED_X += test-parse-options
TEST_PROGRAMS_NEED_X += test-path-utils
TEST_PROGRAMS_NEED_X += test-prio-queue
TEST_PROGRAMS_NEED_X += test-read-cache
+TEST_PROGRAMS_NEED_X += test-ref-store
TEST_PROGRAMS_NEED_X += test-regex
TEST_PROGRAMS_NEED_X += test-revision-walking
TEST_PROGRAMS_NEED_X += test-run-command
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index 39f9a55d16..b106a392a4 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -17,5 +17,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
};
if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
usage_with_options(pack_refs_usage, opts);
- return pack_refs(flags);
+ return refs_pack_refs(get_main_ref_store(), flags);
}
diff --git a/path.c b/path.c
index 22248436bf..302f9af2bd 100644
--- a/path.c
+++ b/path.c
@@ -471,39 +471,19 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
}
/* Returns 0 on success, negative on failure. */
-#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1
static int do_submodule_path(struct strbuf *buf, const char *path,
const char *fmt, va_list args)
{
- const char *git_dir;
struct strbuf git_submodule_common_dir = STRBUF_INIT;
struct strbuf git_submodule_dir = STRBUF_INIT;
- const struct submodule *sub;
- int err = 0;
+ int ret;
- strbuf_addstr(buf, path);
- strbuf_complete(buf, '/');
- strbuf_addstr(buf, ".git");
-
- git_dir = read_gitfile(buf->buf);
- if (git_dir) {
- strbuf_reset(buf);
- strbuf_addstr(buf, git_dir);
- }
- if (!is_git_directory(buf->buf)) {
- gitmodules_config();
- sub = submodule_from_path(null_sha1, path);
- if (!sub) {
- err = SUBMODULE_PATH_ERR_NOT_CONFIGURED;
- goto cleanup;
- }
- strbuf_reset(buf);
- strbuf_git_path(buf, "%s/%s", "modules", sub->name);
- }
-
- strbuf_addch(buf, '/');
- strbuf_addbuf(&git_submodule_dir, buf);
+ ret = submodule_to_gitdir(&git_submodule_dir, path);
+ if (ret)
+ goto cleanup;
+ strbuf_complete(&git_submodule_dir, '/');
+ strbuf_addbuf(buf, &git_submodule_dir);
strbuf_vaddf(buf, fmt, args);
if (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))
@@ -514,8 +494,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
cleanup:
strbuf_release(&git_submodule_dir);
strbuf_release(&git_submodule_common_dir);
-
- return err;
+ return ret;
}
char *git_pathdup_submodule(const char *path, const char *fmt, ...)
diff --git a/refs.c b/refs.c
index c2472184a7..d1e1b4399b 100644
--- a/refs.c
+++ b/refs.c
@@ -9,6 +9,7 @@
#include "refs/refs-internal.h"
#include "object.h"
#include "tag.h"
+#include "submodule.h"
/*
* List of all available backends
@@ -170,11 +171,23 @@ int refname_is_safe(const char *refname)
return 1;
}
+char *refs_resolve_refdup(struct ref_store *refs,
+ const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags)
+{
+ const char *result;
+
+ result = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
+ sha1, flags);
+ return xstrdup_or_null(result);
+}
+
char *resolve_refdup(const char *refname, int resolve_flags,
unsigned char *sha1, int *flags)
{
- return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
- sha1, flags));
+ return refs_resolve_refdup(get_main_ref_store(),
+ refname, resolve_flags,
+ sha1, flags);
}
/* The argument to filter_refs */
@@ -184,13 +197,20 @@ struct ref_filter {
void *cb_data;
};
-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+ int resolve_flags, unsigned char *sha1, int *flags)
{
- if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
+ if (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags))
return 0;
return -1;
}
+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+{
+ return refs_read_ref_full(get_main_ref_store(), refname,
+ resolve_flags, sha1, flags);
+}
+
int read_ref(const char *refname, unsigned char *sha1)
{
return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
@@ -285,34 +305,52 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li
for_each_rawref(warn_if_dangling_symref, &data);
}
+int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data);
+}
+
int for_each_tag_ref(each_ref_fn fn, void *cb_data)
{
- return for_each_ref_in("refs/tags/", fn, cb_data);
+ return refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);
}
int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
{
- return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
+ return refs_for_each_tag_ref(get_submodule_ref_store(submodule),
+ fn, cb_data);
+}
+
+int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
}
int for_each_branch_ref(each_ref_fn fn, void *cb_data)
{
- return for_each_ref_in("refs/heads/", fn, cb_data);
+ return refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);
}
int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
{
- return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
+ return refs_for_each_branch_ref(get_submodule_ref_store(submodule),
+ fn, cb_data);
+}
+
+int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
}
int for_each_remote_ref(each_ref_fn fn, void *cb_data)
{
- return for_each_ref_in("refs/remotes/", fn, cb_data);
+ return refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);
}
int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
{
- return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
+ return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+ fn, cb_data);
}
int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@ -596,16 +634,20 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
return 0;
}
-int delete_ref(const char *msg, const char *refname,
- const unsigned char *old_sha1, unsigned int flags)
+int refs_delete_ref(struct ref_store *refs, const char *msg,
+ const char *refname,
+ const unsigned char *old_sha1,
+ unsigned int flags)
{
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
- if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+ if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+ assert(refs == get_main_ref_store());
return delete_pseudoref(refname, old_sha1);
+ }
- transaction = ref_transaction_begin(&err);
+ transaction = ref_store_transaction_begin(refs, &err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_sha1,
flags, msg, &err) ||
@@ -620,6 +662,13 @@ int delete_ref(const char *msg, const char *refname,
return 0;
}
+int delete_ref(const char *msg, const char *refname,
+ const unsigned char *old_sha1, unsigned int flags)
+{
+ return refs_delete_ref(get_main_ref_store(), msg, refname,
+ old_sha1, flags);
+}
+
int copy_reflog_msg(char *buf, const char *msg)
{
char *cp = buf;
@@ -779,11 +828,20 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,
return 1;
}
-struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
+ struct strbuf *err)
{
+ struct ref_transaction *tr;
assert(err);
- return xcalloc(1, sizeof(struct ref_transaction));
+ tr = xcalloc(1, sizeof(struct ref_transaction));
+ tr->ref_store = refs;
+ return tr;
+}
+
+struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+{
+ return ref_store_transaction_begin(get_main_ref_store(), err);
}
void ref_transaction_free(struct ref_transaction *transaction)
@@ -900,18 +958,20 @@ int update_ref_oid(const char *msg, const char *refname,
old_oid ? old_oid->hash : NULL, flags, onerr);
}
-int update_ref(const char *msg, const char *refname,
- const unsigned char *new_sha1, const unsigned char *old_sha1,
- unsigned int flags, enum action_on_err onerr)
+int refs_update_ref(struct ref_store *refs, const char *msg,
+ const char *refname, const unsigned char *new_sha1,
+ const unsigned char *old_sha1, unsigned int flags,
+ enum action_on_err onerr)
{
struct ref_transaction *t = NULL;
struct strbuf err = STRBUF_INIT;
int ret = 0;
if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+ assert(refs == get_main_ref_store());
ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
} else {
- t = ref_transaction_begin(&err);
+ t = ref_store_transaction_begin(refs, &err);
if (!t ||
ref_transaction_update(t, refname, new_sha1, old_sha1,
flags, msg, &err) ||
@@ -942,6 +1002,15 @@ int update_ref(const char *msg, const char *refname,
return 0;
}
+int update_ref(const char *msg, const char *refname,
+ const unsigned char *new_sha1,
+ const unsigned char *old_sha1,
+ unsigned int flags, enum action_on_err onerr)
+{
+ return refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,
+ old_sha1, flags, onerr);
+}
+
char *shorten_unambiguous_ref(const char *refname, int strict)
{
int i;
@@ -1127,14 +1196,17 @@ const char *find_descendant_ref(const char *dirname,
return NULL;
}
-int rename_ref_available(const char *old_refname, const char *new_refname)
+int refs_rename_ref_available(struct ref_store *refs,
+ const char *old_refname,
+ const char *new_refname)
{
struct string_list skip = STRING_LIST_INIT_NODUP;
struct strbuf err = STRBUF_INIT;
int ok;
string_list_insert(&skip, old_refname);
- ok = !verify_refname_available(new_refname, NULL, &skip, &err);
+ ok = !refs_verify_refname_available(refs, new_refname,
+ NULL, &skip, &err);
if (!ok)
error("%s", err.buf);
@@ -1175,10 +1247,9 @@ int head_ref(each_ref_fn fn, void *cb_data)
* non-zero value, stop the iteration and return that value;
* otherwise, return 0.
*/
-static int do_for_each_ref(const char *submodule, const char *prefix,
+static int do_for_each_ref(struct ref_store *refs, const char *prefix,
each_ref_fn fn, int trim, int flags, void *cb_data)
{
- struct ref_store *refs = get_ref_store(submodule);
struct ref_iterator *iter;
if (!refs)
@@ -1190,19 +1261,30 @@ static int do_for_each_ref(const char *submodule, const char *prefix,
return do_for_each_ref_iterator(iter, fn, cb_data);
}
+int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
+}
+
int for_each_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+ return refs_for_each_ref(get_main_ref_store(), fn, cb_data);
}
int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
+ return refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);
+}
+
+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
+ each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
}
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+ return refs_for_each_ref_in(get_main_ref_store(), prefix, fn, cb_data);
}
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
@@ -1211,19 +1293,23 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
if (broken)
flag = DO_FOR_EACH_INCLUDE_BROKEN;
- return do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);
+ return do_for_each_ref(get_main_ref_store(),
+ prefix, fn, 0, flag, cb_data);
}
int for_each_ref_in_submodule(const char *submodule, const char *prefix,
- each_ref_fn fn, void *cb_data)
+ each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
+ return refs_for_each_ref_in(get_submodule_ref_store(submodule),
+ prefix, fn, cb_data);
}
int for_each_replace_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(NULL, git_replace_ref_base, fn,
- strlen(git_replace_ref_base), 0, cb_data);
+ return do_for_each_ref(get_main_ref_store(),
+ git_replace_ref_base, fn,
+ strlen(git_replace_ref_base),
+ 0, cb_data);
}
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -1231,19 +1317,25 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
struct strbuf buf = STRBUF_INIT;
int ret;
strbuf_addf(&buf, "%srefs/", get_git_namespace());
- ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+ ret = do_for_each_ref(get_main_ref_store(),
+ buf.buf, fn, 0, 0, cb_data);
strbuf_release(&buf);
return ret;
}
-int for_each_rawref(each_ref_fn fn, void *cb_data)
+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(NULL, "", fn, 0,
+ return do_for_each_ref(refs, "", fn, 0,
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+ return refs_for_each_rawref(get_main_ref_store(), fn, cb_data);
+}
+
/* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_recursively(struct ref_store *refs,
+const char *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname,
int resolve_flags,
unsigned char *sha1, int *flags)
@@ -1322,7 +1414,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,
/* backend functions */
int refs_init_db(struct strbuf *err)
{
- struct ref_store *refs = get_ref_store(NULL);
+ struct ref_store *refs = get_main_ref_store();
return refs->be->init_db(refs, err);
}
@@ -1330,7 +1422,7 @@ int refs_init_db(struct strbuf *err)
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
unsigned char *sha1, int *flags)
{
- return resolve_ref_recursively(get_ref_store(NULL), refname,
+ return refs_resolve_ref_unsafe(get_main_ref_store(), refname,
resolve_flags, sha1, flags);
}
@@ -1351,16 +1443,16 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
/* We need to strip off one or more trailing slashes */
char *stripped = xmemdupz(submodule, len);
- refs = get_ref_store(stripped);
+ refs = get_submodule_ref_store(stripped);
free(stripped);
} else {
- refs = get_ref_store(submodule);
+ refs = get_submodule_ref_store(submodule);
}
if (!refs)
return -1;
- if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
+ if (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) ||
is_null_sha1(sha1))
return -1;
return 0;
@@ -1403,17 +1495,13 @@ static struct ref_store *main_ref_store;
static struct hashmap submodule_ref_stores;
/*
- * Return the ref_store instance for the specified submodule (or the
- * main repository if submodule is NULL). If that ref_store hasn't
- * been initialized yet, return NULL.
+ * Return the ref_store instance for the specified submodule. If that
+ * ref_store hasn't been initialized yet, return NULL.
*/
-static struct ref_store *lookup_ref_store(const char *submodule)
+static struct ref_store *lookup_submodule_ref_store(const char *submodule)
{
struct submodule_hash_entry *entry;
- if (!submodule)
- return main_ref_store;
-
if (!submodule_ref_stores.tablesize)
/* It's initialized on demand in register_ref_store(). */
return NULL;
@@ -1424,33 +1512,11 @@ static struct ref_store *lookup_ref_store(const char *submodule)
}
/*
- * Register the specified ref_store to be the one that should be used
- * for submodule (or the main repository if submodule is NULL). It is
- * a fatal error to call this function twice for the same submodule.
- */
-static void register_ref_store(struct ref_store *refs, const char *submodule)
-{
- if (!submodule) {
- if (main_ref_store)
- die("BUG: main_ref_store initialized twice");
-
- main_ref_store = refs;
- } else {
- if (!submodule_ref_stores.tablesize)
- hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
-
- if (hashmap_put(&submodule_ref_stores,
- alloc_submodule_hash_entry(submodule, refs)))
- die("BUG: ref_store for submodule '%s' initialized twice",
- submodule);
- }
-}
-
-/*
* Create, record, and return a ref_store instance for the specified
- * submodule (or the main repository if submodule is NULL).
+ * gitdir.
*/
-static struct ref_store *ref_store_init(const char *submodule)
+static struct ref_store *ref_store_init(const char *gitdir,
+ unsigned int flags)
{
const char *be_name = "files";
struct ref_storage_be *be = find_ref_storage_backend(be_name);
@@ -1459,33 +1525,76 @@ static struct ref_store *ref_store_init(const char *submodule)
if (!be)
die("BUG: reference backend %s is unknown", be_name);
- refs = be->init(submodule);
- register_ref_store(refs, submodule);
+ refs = be->init(gitdir, flags);
return refs;
}
-struct ref_store *get_ref_store(const char *submodule)
+struct ref_store *get_main_ref_store(void)
+{
+ if (main_ref_store)
+ return main_ref_store;
+
+ main_ref_store = ref_store_init(get_git_dir(),
+ (REF_STORE_READ |
+ REF_STORE_WRITE |
+ REF_STORE_ODB |
+ REF_STORE_MAIN));
+ return main_ref_store;
+}
+
+/*
+ * Register the specified ref_store to be the one that should be used
+ * for submodule. It is a fatal error to call this function twice for
+ * the same submodule.
+ */
+static void register_submodule_ref_store(struct ref_store *refs,
+ const char *submodule)
+{
+ if (!submodule_ref_stores.tablesize)
+ hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
+
+ if (hashmap_put(&submodule_ref_stores,
+ alloc_submodule_hash_entry(submodule, refs)))
+ die("BUG: ref_store for submodule '%s' initialized twice",
+ submodule);
+}
+
+struct ref_store *get_submodule_ref_store(const char *submodule)
{
+ struct strbuf submodule_sb = STRBUF_INIT;
struct ref_store *refs;
+ int ret;
if (!submodule || !*submodule) {
- refs = lookup_ref_store(NULL);
+ /*
+ * FIXME: This case is ideally not allowed. But that
+ * can't happen until we clean up all the callers.
+ */
+ return get_main_ref_store();
+ }
- if (!refs)
- refs = ref_store_init(NULL);
- } else {
- refs = lookup_ref_store(submodule);
+ refs = lookup_submodule_ref_store(submodule);
+ if (refs)
+ return refs;
- if (!refs) {
- struct strbuf submodule_sb = STRBUF_INIT;
+ strbuf_addstr(&submodule_sb, submodule);
+ ret = is_nonbare_repository_dir(&submodule_sb);
+ strbuf_release(&submodule_sb);
+ if (!ret)
+ return NULL;
- strbuf_addstr(&submodule_sb, submodule);
- if (is_nonbare_repository_dir(&submodule_sb))
- refs = ref_store_init(submodule);
- strbuf_release(&submodule_sb);
- }
+ ret = submodule_to_gitdir(&submodule_sb, submodule);
+ if (ret) {
+ strbuf_release(&submodule_sb);
+ return NULL;
}
+ /* assume that add_submodule_odb() has been called */
+ refs = ref_store_init(submodule_sb.buf,
+ REF_STORE_READ | REF_STORE_ODB);
+ register_submodule_ref_store(refs, submodule);
+
+ strbuf_release(&submodule_sb);
return refs;
}
@@ -1496,50 +1605,58 @@ void base_ref_store_init(struct ref_store *refs,
}
/* backend functions */
-int pack_refs(unsigned int flags)
+int refs_pack_refs(struct ref_store *refs, unsigned int flags)
{
- struct ref_store *refs = get_ref_store(NULL);
-
return refs->be->pack_refs(refs, flags);
}
+int refs_peel_ref(struct ref_store *refs, const char *refname,
+ unsigned char *sha1)
+{
+ return refs->be->peel_ref(refs, refname, sha1);
+}
+
int peel_ref(const char *refname, unsigned char *sha1)
{
- struct ref_store *refs = get_ref_store(NULL);
+ return refs_peel_ref(get_main_ref_store(), refname, sha1);
+}
- return refs->be->peel_ref(refs, refname, sha1);
+int refs_create_symref(struct ref_store *refs,
+ const char *ref_target,
+ const char *refs_heads_master,
+ const char *logmsg)
+{
+ return refs->be->create_symref(refs, ref_target,
+ refs_heads_master,
+ logmsg);
}
int create_symref(const char *ref_target, const char *refs_heads_master,
const char *logmsg)
{
- struct ref_store *refs = get_ref_store(NULL);
-
- return refs->be->create_symref(refs, ref_target, refs_heads_master,
- logmsg);
+ return refs_create_symref(get_main_ref_store(), ref_target,
+ refs_heads_master, logmsg);
}
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
- struct ref_store *refs = get_ref_store(NULL);
+ struct ref_store *refs = transaction->ref_store;
return refs->be->transaction_commit(refs, transaction, err);
}
-int verify_refname_available(const char *refname,
- const struct string_list *extra,
- const struct string_list *skip,
- struct strbuf *err)
+int refs_verify_refname_available(struct ref_store *refs,
+ const char *refname,
+ const struct string_list *extra,
+ const struct string_list *skip,
+ struct strbuf *err)
{
- struct ref_store *refs = get_ref_store(NULL);
-
return refs->be->verify_refname_available(refs, refname, extra, skip, err);
}
-int for_each_reflog(each_ref_fn fn, void *cb_data)
+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
- struct ref_store *refs = get_ref_store(NULL);
struct ref_iterator *iter;
iter = refs->be->reflog_iterator_begin(refs);
@@ -1547,43 +1664,84 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
return do_for_each_ref_iterator(iter, fn, cb_data);
}
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
- void *cb_data)
+int for_each_reflog(each_ref_fn fn, void *cb_data)
{
- struct ref_store *refs = get_ref_store(NULL);
+ return refs_for_each_reflog(get_main_ref_store(), fn, cb_data);
+}
+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
+ const char *refname,
+ each_reflog_ent_fn fn,
+ void *cb_data)
+{
return refs->be->for_each_reflog_ent_reverse(refs, refname,
fn, cb_data);
}
+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
+ void *cb_data)
+{
+ return refs_for_each_reflog_ent_reverse(get_main_ref_store(),
+ refname, fn, cb_data);
+}
+
+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
+ each_reflog_ent_fn fn, void *cb_data)
+{
+ return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+}
+
int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
void *cb_data)
{
- struct ref_store *refs = get_ref_store(NULL);
+ return refs_for_each_reflog_ent(get_main_ref_store(), refname,
+ fn, cb_data);
+}
- return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+int refs_reflog_exists(struct ref_store *refs, const char *refname)
+{
+ return refs->be->reflog_exists(refs, refname);
}
int reflog_exists(const char *refname)
{
- struct ref_store *refs = get_ref_store(NULL);
+ return refs_reflog_exists(get_main_ref_store(), refname);
+}
- return refs->be->reflog_exists(refs, refname);
+int refs_create_reflog(struct ref_store *refs, const char *refname,
+ int force_create, struct strbuf *err)
+{
+ return refs->be->create_reflog(refs, refname, force_create, err);
}
int safe_create_reflog(const char *refname, int force_create,
struct strbuf *err)
{
- struct ref_store *refs = get_ref_store(NULL);
+ return refs_create_reflog(get_main_ref_store(), refname,
+ force_create, err);
+}
- return refs->be->create_reflog(refs, refname, force_create, err);
+int refs_delete_reflog(struct ref_store *refs, const char *refname)
+{
+ return refs->be->delete_reflog(refs, refname);
}
int delete_reflog(const char *refname)
{
- struct ref_store *refs = get_ref_store(NULL);
+ return refs_delete_reflog(get_main_ref_store(), refname);
+}
- return refs->be->delete_reflog(refs, refname);
+int refs_reflog_expire(struct ref_store *refs,
+ const char *refname, const unsigned char *sha1,
+ unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data)
+{
+ return refs->be->reflog_expire(refs, refname, sha1, flags,
+ prepare_fn, should_prune_fn,
+ cleanup_fn, policy_cb_data);
}
int reflog_expire(const char *refname, const unsigned char *sha1,
@@ -1593,31 +1751,38 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
reflog_expiry_cleanup_fn cleanup_fn,
void *policy_cb_data)
{
- struct ref_store *refs = get_ref_store(NULL);
-
- return refs->be->reflog_expire(refs, refname, sha1, flags,
- prepare_fn, should_prune_fn,
- cleanup_fn, policy_cb_data);
+ return refs_reflog_expire(get_main_ref_store(),
+ refname, sha1, flags,
+ prepare_fn, should_prune_fn,
+ cleanup_fn, policy_cb_data);
}
int initial_ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
- struct ref_store *refs = get_ref_store(NULL);
+ struct ref_store *refs = transaction->ref_store;
return refs->be->initial_transaction_commit(refs, transaction, err);
}
-int delete_refs(struct string_list *refnames, unsigned int flags)
+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
+ unsigned int flags)
{
- struct ref_store *refs = get_ref_store(NULL);
-
return refs->be->delete_refs(refs, refnames, flags);
}
-int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+int delete_refs(struct string_list *refnames, unsigned int flags)
{
- struct ref_store *refs = get_ref_store(NULL);
+ return refs_delete_refs(get_main_ref_store(), refnames, flags);
+}
+int refs_rename_ref(struct ref_store *refs, const char *oldref,
+ const char *newref, const char *logmsg)
+{
return refs->be->rename_ref(refs, oldref, newref, logmsg);
}
+
+int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+ return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);
+}
diff --git a/refs.h b/refs.h
index 3df0d45ebb..49e97d7d5f 100644
--- a/refs.h
+++ b/refs.h
@@ -1,6 +1,11 @@
#ifndef REFS_H
#define REFS_H
+struct object_id;
+struct ref_store;
+struct strbuf;
+struct string_list;
+
/*
* Resolve a reference, recursively following symbolic refererences.
*
@@ -52,16 +57,50 @@
#define RESOLVE_REF_NO_RECURSE 0x02
#define RESOLVE_REF_ALLOW_BAD_NAME 0x04
+const char *refs_resolve_ref_unsafe(struct ref_store *refs,
+ const char *refname,
+ int resolve_flags,
+ unsigned char *sha1,
+ int *flags);
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
unsigned char *sha1, int *flags);
+char *refs_resolve_refdup(struct ref_store *refs,
+ const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags);
char *resolve_refdup(const char *refname, int resolve_flags,
unsigned char *sha1, int *flags);
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+ int resolve_flags, unsigned char *sha1, int *flags);
int read_ref_full(const char *refname, int resolve_flags,
unsigned char *sha1, int *flags);
int read_ref(const char *refname, unsigned char *sha1);
+/*
+ * Return 0 if a reference named refname could be created without
+ * conflicting with the name of an existing reference. Otherwise,
+ * return a negative value and write an explanation to err. If extras
+ * is non-NULL, it is a list of additional refnames with which refname
+ * is not allowed to conflict. If skip is non-NULL, ignore potential
+ * conflicts with refs in skip (e.g., because they are scheduled for
+ * deletion in the same operation). Behavior is undefined if the same
+ * name is listed in both extras and skip.
+ *
+ * Two reference names conflict if one of them exactly matches the
+ * leading components of the other; e.g., "foo/bar" conflicts with
+ * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
+ * "foo/barbados".
+ *
+ * extras and skip must be sorted.
+ */
+
+int refs_verify_refname_available(struct ref_store *refs,
+ const char *refname,
+ const struct string_list *extra,
+ const struct string_list *skip,
+ struct strbuf *err);
+
int ref_exists(const char *refname);
int should_autocreate_reflog(const char *refname);
@@ -78,6 +117,8 @@ extern int refs_init_db(struct strbuf *err);
* Symbolic references are considered unpeelable, even if they
* ultimately resolve to a peelable tag.
*/
+int refs_peel_ref(struct ref_store *refs, const char *refname,
+ unsigned char *sha1);
int peel_ref(const char *refname, unsigned char *sha1);
/**
@@ -189,8 +230,19 @@ typedef int each_ref_fn(const char *refname,
* it is not safe to modify references while an iteration is in
* progress, unless the same callback function invocation that
* modifies the reference also returns a nonzero value to immediately
- * stop the iteration.
+ * stop the iteration. Returned references are sorted.
*/
+int refs_for_each_ref(struct ref_store *refs,
+ each_ref_fn fn, void *cb_data);
+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
+ each_ref_fn fn, void *cb_data);
+int refs_for_each_tag_ref(struct ref_store *refs,
+ each_ref_fn fn, void *cb_data);
+int refs_for_each_branch_ref(struct ref_store *refs,
+ each_ref_fn fn, void *cb_data);
+int refs_for_each_remote_ref(struct ref_store *refs,
+ each_ref_fn fn, void *cb_data);
+
int head_ref(each_ref_fn fn, void *cb_data);
int for_each_ref(each_ref_fn fn, void *cb_data);
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
@@ -220,6 +272,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data);
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
/* can be used to learn about broken ref and symref */
+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
int for_each_rawref(each_ref_fn fn, void *cb_data);
static inline const char *has_glob_specials(const char *pattern)
@@ -243,7 +296,7 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
* Write a packed-refs file for the current repository.
* flags: Combination of the above PACK_REFS_* flags.
*/
-int pack_refs(unsigned int flags);
+int refs_pack_refs(struct ref_store *refs, unsigned int flags);
/*
* Flags controlling ref_transaction_update(), ref_transaction_create(), etc.
@@ -258,6 +311,8 @@ int pack_refs(unsigned int flags);
/*
* Setup reflog before using. Fill in err and return -1 on failure.
*/
+int refs_create_reflog(struct ref_store *refs, const char *refname,
+ int force_create, struct strbuf *err);
int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);
/** Reads log for the value of ref during at_time. **/
@@ -267,6 +322,7 @@ int read_ref_at(const char *refname, unsigned int flags,
unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
/** Check if a particular reflog exists */
+int refs_reflog_exists(struct ref_store *refs, const char *refname);
int reflog_exists(const char *refname);
/*
@@ -276,6 +332,10 @@ int reflog_exists(const char *refname);
* exists, regardless of its old value. It is an error for old_sha1 to
* be NULL_SHA1. flags is passed through to ref_transaction_delete().
*/
+int refs_delete_ref(struct ref_store *refs, const char *msg,
+ const char *refname,
+ const unsigned char *old_sha1,
+ unsigned int flags);
int delete_ref(const char *msg, const char *refname,
const unsigned char *old_sha1, unsigned int flags);
@@ -285,9 +345,12 @@ int delete_ref(const char *msg, const char *refname,
* an all-or-nothing transaction). flags is passed through to
* ref_transaction_delete().
*/
+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
+ unsigned int flags);
int delete_refs(struct string_list *refnames, unsigned int flags);
/** Delete a reflog */
+int refs_delete_reflog(struct ref_store *refs, const char *refname);
int delete_reflog(const char *refname);
/* iterate over reflog entries */
@@ -296,13 +359,20 @@ typedef int each_reflog_ent_fn(
const char *committer, unsigned long timestamp,
int tz, const char *msg, void *cb_data);
+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
+ each_reflog_ent_fn fn, void *cb_data);
+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
+ const char *refname,
+ each_reflog_ent_fn fn,
+ void *cb_data);
int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
/*
* Calls the specified function for each reflog file until it returns nonzero,
- * and returns the value
+ * and returns the value. Reflog file order is unspecified.
*/
+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);
int for_each_reflog(each_ref_fn fn, void *cb_data);
#define REFNAME_ALLOW_ONELEVEL 1
@@ -323,8 +393,12 @@ const char *prettify_refname(const char *refname);
char *shorten_unambiguous_ref(const char *refname, int strict);
/** rename ref, return 0 on success **/
+int refs_rename_ref(struct ref_store *refs, const char *oldref,
+ const char *newref, const char *logmsg);
int rename_ref(const char *oldref, const char *newref, const char *logmsg);
+int refs_create_symref(struct ref_store *refs, const char *refname,
+ const char *target, const char *logmsg);
int create_symref(const char *refname, const char *target, const char *logmsg);
/*
@@ -347,6 +421,8 @@ enum action_on_err {
* Begin a reference transaction. The reference transaction must
* be freed by calling ref_transaction_free().
*/
+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
+ struct strbuf *err);
struct ref_transaction *ref_transaction_begin(struct strbuf *err);
/*
@@ -481,6 +557,9 @@ void ref_transaction_free(struct ref_transaction *transaction);
* ref_transaction_update(). Handle errors as requested by the `onerr`
* argument.
*/
+int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,
+ const unsigned char *new_sha1, const unsigned char *old_sha1,
+ unsigned int flags, enum action_on_err onerr);
int update_ref(const char *msg, const char *refname,
const unsigned char *new_sha1, const unsigned char *old_sha1,
unsigned int flags, enum action_on_err onerr);
@@ -547,6 +626,14 @@ typedef void reflog_expiry_cleanup_fn(void *cb_data);
* enum expire_reflog_flags. The three function pointers are described
* above. On success, return zero.
*/
+int refs_reflog_expire(struct ref_store *refs,
+ const char *refname,
+ const unsigned char *sha1,
+ unsigned int flags,
+ reflog_expiry_prepare_fn prepare_fn,
+ reflog_expiry_should_prune_fn should_prune_fn,
+ reflog_expiry_cleanup_fn cleanup_fn,
+ void *policy_cb_data);
int reflog_expire(const char *refname, const unsigned char *sha1,
unsigned int flags,
reflog_expiry_prepare_fn prepare_fn,
@@ -556,4 +643,17 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
int ref_storage_backend_exists(const char *name);
+struct ref_store *get_main_ref_store(void);
+/*
+ * Return the ref_store instance for the specified submodule. For the
+ * main repository, use submodule==NULL; such a call cannot fail. For
+ * a submodule, the submodule must exist and be a nonbare repository,
+ * otherwise return NULL. If the requested reference store has not yet
+ * been initialized, initialize it first.
+ *
+ * For backwards compatibility, submodule=="" is treated the same as
+ * submodule==NULL.
+ */
+struct ref_store *get_submodule_ref_store(const char *submodule);
+
#endif /* REFS_H */
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 50188e92f9..4d705b4037 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -165,6 +165,10 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
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)
{
@@ -912,13 +916,11 @@ struct packed_ref_cache {
*/
struct files_ref_store {
struct ref_store base;
+ unsigned int store_flags;
- /*
- * The name of the submodule represented by this object, or
- * NULL if it represents the main repository's reference
- * store:
- */
- const char *submodule;
+ char *gitdir;
+ char *gitcommondir;
+ char *packed_refs_path;
struct ref_entry *loose;
struct packed_ref_cache *packed;
@@ -975,38 +977,47 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
* 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;
- refs->submodule = xstrdup_or_null(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 for a submodule (i.e., not for the main repository).
- * caller is used in any necessary error messages.
+ * 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->submodule)
- die("BUG: %s called for a submodule", 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;
@@ -1016,8 +1027,9 @@ static struct files_ref_store *files_downcast(
refs = (struct files_ref_store *)ref_store;
- if (!submodule_allowed)
- files_assert_main_repository(refs, caller);
+ 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;
}
@@ -1150,19 +1162,63 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
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->submodule)
- packed_refs_file = git_pathdup_submodule(refs->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))
@@ -1181,7 +1237,6 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
fclose(f);
}
}
- free(packed_refs_file);
return refs->packed;
}
@@ -1226,19 +1281,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
struct strbuf refname;
struct strbuf path = STRBUF_INIT;
size_t path_baselen;
- int err = 0;
- if (refs->submodule)
- err = strbuf_git_path_submodule(&path, refs->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);
@@ -1267,7 +1313,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
create_dir_entry(refs, refname.buf,
refname.len, 1));
} else {
- if (!resolve_ref_recursively(&refs->base,
+ if (!refs_resolve_ref_unsafe(&refs->base,
refname.buf,
RESOLVE_REF_READING,
sha1, &flag)) {
@@ -1359,7 +1405,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
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;
@@ -1373,10 +1419,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
*type = 0;
strbuf_reset(&sb_path);
- if (refs->submodule)
- strbuf_git_path_submodule(&sb_path, refs->submodule, "%s", refname);
- else
- strbuf_git_path(&sb_path, "%s", refname);
+ files_ref_path(refs, &sb_path, refname);
path = sb_path.buf;
@@ -1564,7 +1607,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
*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)) {
@@ -1579,7 +1622,8 @@ retry:
* 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
@@ -1779,7 +1823,9 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
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];
@@ -1792,7 +1838,8 @@ static int files_peel_ref(struct ref_store *ref_store,
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;
/*
@@ -1887,21 +1934,21 @@ static struct ref_iterator *files_ref_iterator_begin(
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);
@@ -1960,15 +2007,15 @@ static struct ref_iterator *files_ref_iterator_begin(
* 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);
@@ -2036,9 +2083,10 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
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
@@ -2055,8 +2103,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
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;
@@ -2094,7 +2143,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
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;
}
@@ -2157,7 +2206,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)
}
if (hold_lock_file_for_update_timeout(
- &packlock, git_path("packed-refs"),
+ &packlock, files_packed_refs_path(refs),
flags, timeout_value) < 0)
return -1;
/*
@@ -2306,9 +2355,12 @@ enum {
* subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or
* REMOVE_EMPTY_PARENTS_REFLOG.
*/
-static void try_remove_empty_parents(const char *refname, unsigned int flags)
+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;
@@ -2330,18 +2382,23 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)
if (q == p)
break;
strbuf_setlen(&buf, q - buf.buf);
- if ((flags & REMOVE_EMPTY_PARENTS_REF) &&
- rmdir(git_path("%s", 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;
- if ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&
- rmdir(git_path("logs/%s", buf.buf)))
+
+ 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;
@@ -2349,7 +2406,7 @@ static void prune_ref(struct ref_to_prune *r)
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) ||
@@ -2363,10 +2420,10 @@ static void prune_ref(struct ref_to_prune *r)
strbuf_release(&err);
}
-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;
}
}
@@ -2374,7 +2431,8 @@ static void prune_refs(struct ref_to_prune *r)
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));
@@ -2389,7 +2447,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
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;
}
@@ -2423,7 +2481,7 @@ static int repack_without_refs(struct files_ref_store *refs,
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);
@@ -2453,7 +2511,7 @@ 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;
@@ -2481,7 +2539,7 @@ static int files_delete_refs(struct ref_store *ref_store,
for (i = 0; i < refnames->nr; i++) {
const char *refname = refnames->items[i].string;
- if (delete_ref(NULL, refname, NULL, flags))
+ if (refs_delete_ref(&refs->base, NULL, refname, NULL, flags))
result |= error(_("could not remove reference %s"), refname);
}
@@ -2497,13 +2555,18 @@ out:
* 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"
+
+struct rename_cb {
+ const char *tmp_renamed_log;
+ int true_errno;
+};
-static int rename_tmp_log_callback(const char *path, void *cb)
+static int rename_tmp_log_callback(const char *path, void *cb_data)
{
- int *true_errno = cb;
+ struct rename_cb *cb = cb_data;
- if (rename(git_path(TMP_RENAMED_LOG), path)) {
+ 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.
@@ -2511,7 +2574,7 @@ static int rename_tmp_log_callback(const char *path, void *cb)
* but report EISDIR to raceproof_create_file() so
* that it knows to retry.
*/
- *true_errno = errno;
+ cb->true_errno = errno;
if (errno == ENOTDIR)
errno = EISDIR;
return -1;
@@ -2520,22 +2583,28 @@ static int rename_tmp_log_callback(const char *path, void *cb)
}
}
-static int rename_tmp_log(const char *newrefname)
+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
{
- char *path = git_pathdup("logs/%s", newrefname);
- int ret, true_errno;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf tmp = STRBUF_INIT;
+ struct rename_cb cb;
+ int ret;
- ret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);
+ 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);
+ error("directory not empty: %s", path.buf);
else
error("unable to move logfile %s to %s: %s",
- git_path(TMP_RENAMED_LOG), path,
- strerror(true_errno));
+ tmp.buf, path.buf,
+ strerror(cb.true_errno));
}
- free(path);
+ strbuf_release(&path);
+ strbuf_release(&tmp);
return ret;
}
@@ -2546,7 +2615,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,
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);
@@ -2571,32 +2640,52 @@ static int files_rename_ref(struct ref_store *ref_store,
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 (delete_ref(logmsg, oldrefname, orig_sha1, REF_NODEREF)) {
+ 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 (refs_delete_ref(&refs->base, logmsg, oldrefname,
+ orig_sha1, REF_NODEREF)) {
error("unable to delete old %s", oldrefname);
goto rollback;
}
@@ -2608,14 +2697,16 @@ static int files_rename_ref(struct ref_store *ref_store,
* 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(NULL, newrefname, NULL, REF_NODEREF)) {
+ 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);
@@ -2629,7 +2720,7 @@ static int files_rename_ref(struct ref_store *ref_store,
}
}
- if (log && rename_tmp_log(newrefname))
+ if (log && rename_tmp_log(refs, newrefname))
goto rollback;
logmoved = log;
@@ -2650,7 +2741,8 @@ static int files_rename_ref(struct ref_store *ref_store,
goto rollback;
}
- return 0;
+ ret = 0;
+ goto out;
rollback:
lock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,
@@ -2671,15 +2763,20 @@ static int files_rename_ref(struct ref_store *ref_store,
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)
@@ -2738,10 +2835,15 @@ static int open_or_create_logfile(const char *path, void *cb)
* set *logfd to -1. On failure, fill in *err, set *logfd to -1, and
* return -1.
*/
-static int log_ref_setup(const char *refname, int force_create,
+static int log_ref_setup(struct files_ref_store *refs,
+ const char *refname, int force_create,
int *logfd, struct strbuf *err)
{
- char *logfile = git_pathdup("logs/%s", refname);
+ struct strbuf logfile_sb = STRBUF_INIT;
+ char *logfile;
+
+ files_reflog_path(refs, &logfile_sb, refname);
+ logfile = strbuf_detach(&logfile_sb, NULL);
if (force_create || should_autocreate_reflog(refname)) {
if (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {
@@ -2791,12 +2893,11 @@ static int files_create_reflog(struct ref_store *ref_store,
const char *refname, int force_create,
struct strbuf *err)
{
+ 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(refname, force_create, &fd, err))
+ if (log_ref_setup(refs, refname, force_create, &fd, err))
return -1;
if (fd >= 0)
@@ -2831,16 +2932,18 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
return 0;
}
-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)
+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;
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, flags & REF_FORCE_CREATE_REFLOG,
+ result = log_ref_setup(refs, refname,
+ flags & REF_FORCE_CREATE_REFLOG,
&logfd, err);
if (result)
@@ -2851,18 +2954,24 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
result = log_ref_write_fd(logfd, old_sha1, new_sha1,
git_committer_info(0), msg);
if (result) {
+ struct strbuf sb = STRBUF_INIT;
int save_errno = errno;
+ files_reflog_path(refs, &sb, refname);
strbuf_addf(err, "unable to append to '%s': %s",
- git_path("logs/%s", refname), strerror(save_errno));
+ sb.buf, strerror(save_errno));
+ strbuf_release(&sb);
close(logfd);
return -1;
}
if (close(logfd)) {
+ struct strbuf sb = STRBUF_INIT;
int save_errno = errno;
+ files_reflog_path(refs, &sb, refname);
strbuf_addf(err, "unable to append to '%s': %s",
- git_path("logs/%s", refname), strerror(save_errno));
+ sb.buf, strerror(save_errno));
+ strbuf_release(&sb);
return -1;
}
return 0;
@@ -2920,7 +3029,8 @@ static int commit_ref_update(struct files_ref_store *refs,
files_assert_main_repository(refs, "commit_ref_update");
clear_loose_ref_cache(refs);
- if (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,
+ 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",
@@ -2947,13 +3057,15 @@ static int commit_ref_update(struct files_ref_store *refs,
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 (files_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);
}
@@ -2985,24 +3097,28 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
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) &&
- files_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;
}
@@ -3010,7 +3126,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,
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);
@@ -3025,7 +3141,7 @@ static int files_create_symref(struct ref_store *ref_store,
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;
@@ -3039,13 +3155,22 @@ static int files_create_symref(struct ref_store *ref_store,
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, 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;
@@ -3072,7 +3197,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char
lock->lk = &head_lock;
lock->ref_name = xstrdup(head_rel);
- ret = create_symref_locked(lock, head_rel, target, logmsg);
+ ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
unlock_ref(lock); /* will free lock */
strbuf_release(&head_path);
@@ -3082,22 +3207,30 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char
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)
@@ -3145,15 +3278,17 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
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;
@@ -3252,14 +3387,16 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
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;
@@ -3273,6 +3410,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
struct files_reflog_iterator {
struct ref_iterator base;
+ struct ref_store *ref_store;
struct dir_iterator *dir_iterator;
struct object_id oid;
};
@@ -3294,8 +3432,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
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;
}
@@ -3339,14 +3478,18 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
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;
}
@@ -3585,8 +3728,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* 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",
@@ -3676,7 +3820,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
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;
@@ -3684,6 +3829,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
char *head_ref = NULL;
int head_type;
struct object_id head_oid;
+ struct strbuf sb = STRBUF_INIT;
assert(err);
@@ -3738,8 +3884,9 @@ static int files_transaction_commit(struct ref_store *ref_store,
* 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);
@@ -3768,7 +3915,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
if (update->flags & REF_NEEDS_COMMIT ||
update->flags & REF_LOG_ONLY) {
- if (files_log_ref_write(lock->ref_name,
+ if (files_log_ref_write(refs,
+ lock->ref_name,
lock->old_oid.hash,
update->new_sha1,
update->msg, update->flags,
@@ -3805,7 +3953,9 @@ static int files_transaction_commit(struct ref_store *ref_store,
if (!(update->type & REF_ISPACKED) ||
update->type & REF_ISSYMREF) {
/* It is a loose reference. */
- if (unlink_or_msg(git_path("%s", lock->ref_name), err)) {
+ strbuf_reset(&sb);
+ files_ref_path(refs, &sb, lock->ref_name);
+ if (unlink_or_msg(sb.buf, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
@@ -3825,14 +3975,17 @@ static int files_transaction_commit(struct ref_store *ref_store,
/* Delete the reflogs of any references that were deleted: */
for_each_string_list_item(ref_to_delete, &refs_to_delete) {
- if (!unlink_or_warn(git_path("logs/%s", ref_to_delete->string)))
- try_remove_empty_parents(ref_to_delete->string,
+ 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++) {
@@ -3849,7 +4002,7 @@ cleanup:
* can only work because we have already
* removed the lockfile.)
*/
- try_remove_empty_parents(update->refname,
+ try_remove_empty_parents(refs, update->refname,
REMOVE_EMPTY_PARENTS_REF);
}
}
@@ -3874,7 +4027,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
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;
@@ -3905,7 +4059,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
* 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++) {
@@ -3914,9 +4069,9 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
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;
}
@@ -3996,10 +4151,11 @@ static int files_reflog_expire(struct ref_store *ref_store,
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;
@@ -4023,12 +4179,13 @@ static int files_reflog_expire(struct ref_store *ref_store,
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
@@ -4053,7 +4210,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
}
(*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)) {
@@ -4099,18 +4256,21 @@ static int files_reflog_expire(struct ref_store *ref_store,
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;
}
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index fa93c9a32e..690498698e 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -112,28 +112,6 @@ enum peel_status {
enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);
/*
- * Return 0 if a reference named refname could be created without
- * conflicting with the name of an existing reference. Otherwise,
- * return a negative value and write an explanation to err. If extras
- * is non-NULL, it is a list of additional refnames with which refname
- * is not allowed to conflict. If skip is non-NULL, ignore potential
- * conflicts with refs in skip (e.g., because they are scheduled for
- * deletion in the same operation). Behavior is undefined if the same
- * name is listed in both extras and skip.
- *
- * Two reference names conflict if one of them exactly matches the
- * leading components of the other; e.g., "foo/bar" conflicts with
- * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
- * "foo/barbados".
- *
- * extras and skip must be sorted.
- */
-int verify_refname_available(const char *newname,
- const struct string_list *extras,
- const struct string_list *skip,
- struct strbuf *err);
-
-/*
* Copy the reflog message msg to buf, which has been allocated sufficiently
* large, while cleaning up the whitespaces. Especially, convert LF to space,
* because reflog file is one line per entry.
@@ -222,16 +200,13 @@ enum ref_transaction_state {
* as atomically as possible. This structure is opaque to callers.
*/
struct ref_transaction {
+ struct ref_store *ref_store;
struct ref_update **updates;
size_t alloc;
size_t nr;
enum ref_transaction_state state;
};
-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);
-
/*
* Check for entries in extras that are within the specified
* directory, where dirname is a reference directory name including
@@ -256,7 +231,9 @@ const char *find_descendant_ref(const char *dirname,
* processes (though rename_ref() catches some races that might get by
* this check).
*/
-int rename_ref_available(const char *old_refname, const char *new_refname);
+int refs_rename_ref_available(struct ref_store *refs,
+ const char *old_refname,
+ const char *new_refname);
/* We allow "recursive" symbolic refs. Only within reason, though */
#define SYMREF_MAXDEPTH 5
@@ -485,13 +462,19 @@ struct ref_store;
/* refs backends */
+/* ref_store_init flags */
+#define REF_STORE_READ (1 << 0)
+#define REF_STORE_WRITE (1 << 1) /* can perform update operations */
+#define REF_STORE_ODB (1 << 2) /* has access to object database */
+#define REF_STORE_MAIN (1 << 3)
+
/*
- * Initialize the ref_store for the specified submodule, or for the
- * main repository if submodule == NULL. These functions should call
- * base_ref_store_init() to initialize the shared part of the
- * ref_store and to record the ref_store for later lookup.
+ * Initialize the ref_store for the specified gitdir. These functions
+ * should call base_ref_store_init() to initialize the shared part of
+ * the ref_store and to record the ref_store for later lookup.
*/
-typedef struct ref_store *ref_store_init_fn(const char *submodule);
+typedef struct ref_store *ref_store_init_fn(const char *gitdir,
+ unsigned int flags);
typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
@@ -644,21 +627,4 @@ struct ref_store {
void base_ref_store_init(struct ref_store *refs,
const struct ref_storage_be *be);
-/*
- * Return the ref_store instance for the specified submodule. For the
- * main repository, use submodule==NULL; such a call cannot fail. For
- * a submodule, the submodule must exist and be a nonbare repository,
- * otherwise return NULL. If the requested reference store has not yet
- * been initialized, initialize it first.
- *
- * For backwards compatibility, submodule=="" is treated the same as
- * submodule==NULL.
- */
-struct ref_store *get_ref_store(const char *submodule);
-
-const char *resolve_ref_recursively(struct ref_store *refs,
- const char *refname,
- int resolve_flags,
- unsigned char *sha1, int *flags);
-
#endif /* REFS_REFS_INTERNAL_H */
diff --git a/submodule.c b/submodule.c
index 620504d325..5615d7392e 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1866,3 +1866,34 @@ const char *get_superproject_working_tree(void)
return ret;
}
+
+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
+{
+ const struct submodule *sub;
+ const char *git_dir;
+ int ret = 0;
+
+ strbuf_reset(buf);
+ strbuf_addstr(buf, submodule);
+ strbuf_complete(buf, '/');
+ strbuf_addstr(buf, ".git");
+
+ git_dir = read_gitfile(buf->buf);
+ if (git_dir) {
+ strbuf_reset(buf);
+ strbuf_addstr(buf, git_dir);
+ }
+ if (!is_git_directory(buf->buf)) {
+ gitmodules_config();
+ sub = submodule_from_path(null_sha1, submodule);
+ if (!sub) {
+ ret = -1;
+ goto cleanup;
+ }
+ strbuf_reset(buf);
+ strbuf_git_path(buf, "%s/%s", "modules", sub->name);
+ }
+
+cleanup:
+ return ret;
+}
diff --git a/submodule.h b/submodule.h
index 486371d2c3..1277480add 100644
--- a/submodule.h
+++ b/submodule.h
@@ -98,6 +98,12 @@ extern int push_unpushed_submodules(struct oid_array *commits,
int dry_run);
extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
extern int parallel_submodules(void);
+/*
+ * Given a submodule path (as in the index), return the repository
+ * path of that submodule in 'buf'. Return -1 on error or when the
+ * submodule is not initialized.
+ */
+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
#define SUBMODULE_MOVE_HEAD_FORCE (1<<1)
diff --git a/t/helper/.gitignore b/t/helper/.gitignore
index 758ed2e8fa..d1fa751cf9 100644
--- a/t/helper/.gitignore
+++ b/t/helper/.gitignore
@@ -20,6 +20,7 @@
/test-path-utils
/test-prio-queue
/test-read-cache
+/test-ref-store
/test-regex
/test-revision-walking
/test-run-command
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
new file mode 100644
index 0000000000..2d84c45ffe
--- /dev/null
+++ b/t/helper/test-ref-store.c
@@ -0,0 +1,277 @@
+#include "cache.h"
+#include "refs.h"
+
+static const char *notnull(const char *arg, const char *name)
+{
+ if (!arg)
+ die("%s required", name);
+ return arg;
+}
+
+static unsigned int arg_flags(const char *arg, const char *name)
+{
+ return atoi(notnull(arg, name));
+}
+
+static const char **get_store(const char **argv, struct ref_store **refs)
+{
+ const char *gitdir;
+
+ if (!argv[0]) {
+ die("ref store required");
+ } else if (!strcmp(argv[0], "main")) {
+ *refs = get_main_ref_store();
+ } else if (skip_prefix(argv[0], "submodule:", &gitdir)) {
+ struct strbuf sb = STRBUF_INIT;
+ int ret;
+
+ ret = strbuf_git_path_submodule(&sb, gitdir, "objects/");
+ if (ret)
+ die("strbuf_git_path_submodule failed: %d", ret);
+ add_to_alternates_memory(sb.buf);
+ strbuf_release(&sb);
+
+ *refs = get_submodule_ref_store(gitdir);
+ } else
+ die("unknown backend %s", argv[0]);
+
+ if (!*refs)
+ die("no ref store");
+
+ /* consume store-specific optional arguments if needed */
+
+ return argv + 1;
+}
+
+
+static int cmd_pack_refs(struct ref_store *refs, const char **argv)
+{
+ unsigned int flags = arg_flags(*argv++, "flags");
+
+ return refs_pack_refs(refs, flags);
+}
+
+static int cmd_peel_ref(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+ unsigned char sha1[20];
+ int ret;
+
+ ret = refs_peel_ref(refs, refname, sha1);
+ if (!ret)
+ puts(sha1_to_hex(sha1));
+ return ret;
+}
+
+static int cmd_create_symref(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+ const char *target = notnull(*argv++, "target");
+ const char *logmsg = *argv++;
+
+ return refs_create_symref(refs, refname, target, logmsg);
+}
+
+static int cmd_delete_refs(struct ref_store *refs, const char **argv)
+{
+ unsigned int flags = arg_flags(*argv++, "flags");
+ struct string_list refnames = STRING_LIST_INIT_NODUP;
+
+ while (*argv)
+ string_list_append(&refnames, *argv++);
+
+ return refs_delete_refs(refs, &refnames, flags);
+}
+
+static int cmd_rename_ref(struct ref_store *refs, const char **argv)
+{
+ const char *oldref = notnull(*argv++, "oldref");
+ const char *newref = notnull(*argv++, "newref");
+ const char *logmsg = *argv++;
+
+ return refs_rename_ref(refs, oldref, newref, logmsg);
+}
+
+static int each_ref(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
+{
+ printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
+ return 0;
+}
+
+static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
+{
+ const char *prefix = notnull(*argv++, "prefix");
+
+ return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
+}
+
+static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
+{
+ unsigned char sha1[20];
+ const char *refname = notnull(*argv++, "refname");
+ int resolve_flags = arg_flags(*argv++, "resolve-flags");
+ int flags;
+ const char *ref;
+
+ ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
+ sha1, &flags);
+ printf("%s %s 0x%x\n", sha1_to_hex(sha1), ref, flags);
+ return ref ? 0 : 1;
+}
+
+static int cmd_verify_ref(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+ struct strbuf err = STRBUF_INIT;
+ int ret;
+
+ ret = refs_verify_refname_available(refs, refname, NULL, NULL, &err);
+ if (err.len)
+ puts(err.buf);
+ return ret;
+}
+
+static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
+{
+ return refs_for_each_reflog(refs, each_ref, NULL);
+}
+
+static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
+ const char *committer, unsigned long timestamp,
+ int tz, const char *msg, void *cb_data)
+{
+ printf("%s %s %s %lu %d %s\n",
+ oid_to_hex(old_oid), oid_to_hex(new_oid),
+ committer, timestamp, tz, msg);
+ return 0;
+}
+
+static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+
+ return refs_for_each_reflog_ent(refs, refname, each_reflog, refs);
+}
+
+static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+
+ return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);
+}
+
+static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+
+ return !refs_reflog_exists(refs, refname);
+}
+
+static int cmd_create_reflog(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+ int force_create = arg_flags(*argv++, "force-create");
+ struct strbuf err = STRBUF_INIT;
+ int ret;
+
+ ret = refs_create_reflog(refs, refname, force_create, &err);
+ if (err.len)
+ puts(err.buf);
+ return ret;
+}
+
+static int cmd_delete_reflog(struct ref_store *refs, const char **argv)
+{
+ const char *refname = notnull(*argv++, "refname");
+
+ return refs_delete_reflog(refs, refname);
+}
+
+static int cmd_reflog_expire(struct ref_store *refs, const char **argv)
+{
+ die("not supported yet");
+}
+
+static int cmd_delete_ref(struct ref_store *refs, const char **argv)
+{
+ const char *msg = notnull(*argv++, "msg");
+ const char *refname = notnull(*argv++, "refname");
+ const char *sha1_buf = notnull(*argv++, "old-sha1");
+ unsigned int flags = arg_flags(*argv++, "flags");
+ unsigned char old_sha1[20];
+
+ if (get_sha1_hex(sha1_buf, old_sha1))
+ die("not sha-1");
+
+ return refs_delete_ref(refs, msg, refname, old_sha1, flags);
+}
+
+static int cmd_update_ref(struct ref_store *refs, const char **argv)
+{
+ const char *msg = notnull(*argv++, "msg");
+ const char *refname = notnull(*argv++, "refname");
+ const char *new_sha1_buf = notnull(*argv++, "old-sha1");
+ const char *old_sha1_buf = notnull(*argv++, "old-sha1");
+ unsigned int flags = arg_flags(*argv++, "flags");
+ unsigned char old_sha1[20];
+ unsigned char new_sha1[20];
+
+ if (get_sha1_hex(old_sha1_buf, old_sha1) ||
+ get_sha1_hex(new_sha1_buf, new_sha1))
+ die("not sha-1");
+
+ return refs_update_ref(refs, msg, refname,
+ new_sha1, old_sha1,
+ flags, UPDATE_REFS_DIE_ON_ERR);
+}
+
+struct command {
+ const char *name;
+ int (*func)(struct ref_store *refs, const char **argv);
+};
+
+static struct command commands[] = {
+ { "pack-refs", cmd_pack_refs },
+ { "peel-ref", cmd_peel_ref },
+ { "create-symref", cmd_create_symref },
+ { "delete-refs", cmd_delete_refs },
+ { "rename-ref", cmd_rename_ref },
+ { "for-each-ref", cmd_for_each_ref },
+ { "resolve-ref", cmd_resolve_ref },
+ { "verify-ref", cmd_verify_ref },
+ { "for-each-reflog", cmd_for_each_reflog },
+ { "for-each-reflog-ent", cmd_for_each_reflog_ent },
+ { "for-each-reflog-ent-reverse", cmd_for_each_reflog_ent_reverse },
+ { "reflog-exists", cmd_reflog_exists },
+ { "create-reflog", cmd_create_reflog },
+ { "delete-reflog", cmd_delete_reflog },
+ { "reflog-expire", cmd_reflog_expire },
+ /*
+ * backend transaction functions can't be tested separately
+ */
+ { "delete-ref", cmd_delete_ref },
+ { "update-ref", cmd_update_ref },
+ { NULL, NULL }
+};
+
+int cmd_main(int argc, const char **argv)
+{
+ struct ref_store *refs;
+ const char *func;
+ struct command *cmd;
+
+ setup_git_directory();
+
+ argv = get_store(argv + 1, &refs);
+
+ func = *argv++;
+ if (!func)
+ die("ref function required");
+ for (cmd = commands; cmd->name; cmd++) {
+ if (!strcmp(func, cmd->name))
+ return cmd->func(refs, argv);
+ }
+ die("unknown function %s", func);
+ return 0;
+}
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
new file mode 100755
index 0000000000..490521f8cb
--- /dev/null
+++ b/t/t1405-main-ref-store.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+test_description='test main ref store api'
+
+. ./test-lib.sh
+
+RUN="test-ref-store main"
+
+test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
+ test_commit one &&
+ N=`find .git/refs -type f | wc -l` &&
+ test "$N" != 0 &&
+ $RUN pack-refs 3 &&
+ N=`find .git/refs -type f | wc -l`
+'
+
+test_expect_success 'peel_ref(new-tag)' '
+ git rev-parse HEAD >expected &&
+ git tag -a -m new-tag new-tag HEAD &&
+ $RUN peel-ref refs/tags/new-tag >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'create_symref(FOO, refs/heads/master)' '
+ $RUN create-symref FOO refs/heads/master nothing &&
+ echo refs/heads/master >expected &&
+ git symbolic-ref FOO >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
+ git rev-parse FOO -- &&
+ git rev-parse refs/tags/new-tag -- &&
+ $RUN delete-refs 0 FOO refs/tags/new-tag &&
+ test_must_fail git rev-parse FOO -- &&
+ test_must_fail git rev-parse refs/tags/new-tag --
+'
+
+test_expect_success 'rename_refs(master, new-master)' '
+ git rev-parse master >expected &&
+ $RUN rename-ref refs/heads/master refs/heads/new-master &&
+ git rev-parse new-master >actual &&
+ test_cmp expected actual &&
+ test_commit recreate-master
+'
+
+test_expect_success 'for_each_ref(refs/heads/)' '
+ $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+ cat >expected <<-\EOF &&
+ master 0x0
+ new-master 0x0
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'for_each_ref() is sorted' '
+ $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+ sort actual > expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'resolve_ref(new-master)' '
+ SHA1=`git rev-parse new-master` &&
+ echo "$SHA1 refs/heads/new-master 0x0" >expected &&
+ $RUN resolve-ref refs/heads/new-master 0 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify_ref(new-master)' '
+ $RUN verify-ref refs/heads/new-master
+'
+
+test_expect_success 'for_each_reflog()' '
+ $RUN for-each-reflog | sort | cut -c 42- >actual &&
+ cat >expected <<-\EOF &&
+ HEAD 0x1
+ refs/heads/master 0x0
+ refs/heads/new-master 0x0
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'for_each_reflog_ent()' '
+ $RUN for-each-reflog-ent HEAD >actual &&
+ head -n1 actual | grep one &&
+ tail -n2 actual | head -n1 | grep recreate-master
+'
+
+test_expect_success 'for_each_reflog_ent_reverse()' '
+ $RUN for-each-reflog-ent-reverse HEAD >actual &&
+ head -n1 actual | grep recreate-master &&
+ tail -n2 actual | head -n1 | grep one
+'
+
+test_expect_success 'reflog_exists(HEAD)' '
+ $RUN reflog-exists HEAD
+'
+
+test_expect_success 'delete_reflog(HEAD)' '
+ $RUN delete-reflog HEAD &&
+ ! test -f .git/logs/HEAD
+'
+
+test_expect_success 'create-reflog(HEAD)' '
+ $RUN create-reflog HEAD 1 &&
+ test -f .git/logs/HEAD
+'
+
+test_expect_success 'delete_ref(refs/heads/foo)' '
+ git checkout -b foo &&
+ FOO_SHA1=`git rev-parse foo` &&
+ git checkout --detach &&
+ test_commit bar-commit &&
+ git checkout -b bar &&
+ BAR_SHA1=`git rev-parse bar` &&
+ $RUN update-ref updating refs/heads/foo $BAR_SHA1 $FOO_SHA1 0 &&
+ echo $BAR_SHA1 >expected &&
+ git rev-parse refs/heads/foo >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'delete_ref(refs/heads/foo)' '
+ SHA1=`git rev-parse foo` &&
+ git checkout --detach &&
+ $RUN delete-ref msg refs/heads/foo $SHA1 0 &&
+ test_must_fail git rev-parse refs/heads/foo --
+'
+
+test_done
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
new file mode 100755
index 0000000000..13b5454c56
--- /dev/null
+++ b/t/t1406-submodule-ref-store.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+test_description='test submodule ref store api'
+
+. ./test-lib.sh
+
+RUN="test-ref-store submodule:sub"
+
+test_expect_success 'setup' '
+ git init sub &&
+ (
+ cd sub &&
+ test_commit first &&
+ git checkout -b new-master
+ )
+'
+
+test_expect_success 'pack_refs() not allowed' '
+ test_must_fail $RUN pack-refs 3
+'
+
+test_expect_success 'peel_ref(new-tag)' '
+ git -C sub rev-parse HEAD >expected &&
+ git -C sub tag -a -m new-tag new-tag HEAD &&
+ $RUN peel-ref refs/tags/new-tag >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'create_symref() not allowed' '
+ test_must_fail $RUN create-symref FOO refs/heads/master nothing
+'
+
+test_expect_success 'delete_refs() not allowed' '
+ test_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag
+'
+
+test_expect_success 'rename_refs() not allowed' '
+ test_must_fail $RUN rename-ref refs/heads/master refs/heads/new-master
+'
+
+test_expect_success 'for_each_ref(refs/heads/)' '
+ $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+ cat >expected <<-\EOF &&
+ master 0x0
+ new-master 0x0
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'for_each_ref() is sorted' '
+ $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+ sort actual > expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'resolve_ref(master)' '
+ SHA1=`git -C sub rev-parse master` &&
+ echo "$SHA1 refs/heads/master 0x0" >expected &&
+ $RUN resolve-ref refs/heads/master 0 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'verify_ref(new-master)' '
+ $RUN verify-ref refs/heads/new-master
+'
+
+test_expect_success 'for_each_reflog()' '
+ $RUN for-each-reflog | sort | cut -c 42- >actual &&
+ cat >expected <<-\EOF &&
+ HEAD 0x1
+ refs/heads/master 0x0
+ refs/heads/new-master 0x0
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'for_each_reflog_ent()' '
+ $RUN for-each-reflog-ent HEAD >actual && cat actual &&
+ head -n1 actual | grep first &&
+ tail -n2 actual | head -n1 | grep master.to.new
+'
+
+test_expect_success 'for_each_reflog_ent_reverse()' '
+ $RUN for-each-reflog-ent-reverse HEAD >actual &&
+ head -n1 actual | grep master.to.new &&
+ tail -n2 actual | head -n1 | grep first
+'
+
+test_expect_success 'reflog_exists(HEAD)' '
+ $RUN reflog-exists HEAD
+'
+
+test_expect_success 'delete_reflog() not allowed' '
+ test_must_fail $RUN delete-reflog HEAD
+'
+
+test_expect_success 'create-reflog() not allowed' '
+ test_must_fail $RUN create-reflog HEAD 1
+'
+
+test_done