*/
#include "cache.h"
+#include "hashmap.h"
#include "lockfile.h"
#include "refs.h"
#include "refs/refs-internal.h"
const char *prettify_refname(const char *name)
{
- return name + (
- starts_with(name, "refs/heads/") ? 11 :
- starts_with(name, "refs/tags/") ? 10 :
- starts_with(name, "refs/remotes/") ? 13 :
- 0);
+ if (skip_prefix(name, "refs/heads/", &name) ||
+ skip_prefix(name, "refs/tags/", &name) ||
+ skip_prefix(name, "refs/remotes/", &name))
+ ; /* nothing */
+ return name;
}
static const char *ref_rev_parse_rules[] = {
static char *substitute_branch_name(const char **string, int *len)
{
struct strbuf buf = STRBUF_INIT;
- int ret = interpret_branch_name(*string, *len, &buf);
+ int ret = interpret_branch_name(*string, *len, &buf, 0);
if (ret == *len) {
size_t size;
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
{
char *last_branch = substitute_branch_name(&str, &len);
+ int refs_found = expand_ref(str, len, sha1, ref);
+ free(last_branch);
+ return refs_found;
+}
+
+int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
+{
const char **p, *r;
int refs_found = 0;
warning("ignoring broken ref %s.", fullref);
}
}
- free(last_branch);
return refs_found;
}
return 0;
}
-int delete_ref(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)
{
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_sha1,
- flags, NULL, &err) ||
+ flags, msg, &err) ||
ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ref_transaction_free(transaction);
int should_autocreate_reflog(const char *refname)
{
- if (!log_all_ref_updates)
+ switch (log_all_ref_updates) {
+ case LOG_REFS_ALWAYS:
+ return 1;
+ case LOG_REFS_NORMAL:
+ return starts_with(refname, "refs/heads/") ||
+ starts_with(refname, "refs/remotes/") ||
+ starts_with(refname, "refs/notes/") ||
+ !strcmp(refname, "HEAD");
+ default:
return 0;
- return starts_with(refname, "refs/heads/") ||
- starts_with(refname, "refs/remotes/") ||
- starts_with(refname, "refs/notes/") ||
- !strcmp(refname, "HEAD");
+ }
}
int is_branch(const char *refname)
int *cutoff_cnt;
};
-static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
{
* hold the values for the previous record.
*/
if (!is_null_sha1(cb->osha1)) {
- hashcpy(cb->sha1, nsha1);
- if (hashcmp(cb->osha1, nsha1))
+ hashcpy(cb->sha1, noid->hash);
+ if (hashcmp(cb->osha1, noid->hash))
warning("Log for ref %s has gap after %s.",
cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
}
else if (cb->date == cb->at_time)
- hashcpy(cb->sha1, nsha1);
- else if (hashcmp(nsha1, cb->sha1))
+ hashcpy(cb->sha1, noid->hash);
+ else if (hashcmp(noid->hash, cb->sha1))
warning("Log for ref %s unexpectedly ended on %s.",
cb->refname, show_date(cb->date, cb->tz,
DATE_MODE(RFC2822)));
- hashcpy(cb->osha1, osha1);
- hashcpy(cb->nsha1, nsha1);
+ hashcpy(cb->osha1, ooid->hash);
+ hashcpy(cb->nsha1, noid->hash);
cb->found_it = 1;
return 1;
}
- hashcpy(cb->osha1, osha1);
- hashcpy(cb->nsha1, nsha1);
+ hashcpy(cb->osha1, ooid->hash);
+ hashcpy(cb->nsha1, noid->hash);
if (cb->cnt > 0)
cb->cnt--;
return 0;
}
-static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
const char *email, unsigned long timestamp,
int tz, const char *message, void *cb_data)
{
*cb->cutoff_tz = tz;
if (cb->cutoff_cnt)
*cb->cutoff_cnt = cb->reccnt;
- hashcpy(cb->sha1, osha1);
+ hashcpy(cb->sha1, ooid->hash);
if (is_null_sha1(cb->sha1))
- hashcpy(cb->sha1, nsha1);
+ hashcpy(cb->sha1, noid->hash);
/* We just want the first entry */
return 1;
}
hashcpy(update->new_sha1, new_sha1);
if (flags & REF_HAVE_OLD)
hashcpy(update->old_sha1, old_sha1);
- if (msg)
- update->msg = xstrdup(msg);
+ update->msg = xstrdup_or_null(msg);
return update;
}
flags, NULL, err);
}
+int update_ref_oid(const char *msg, const char *refname,
+ const struct object_id *new_oid, const struct object_id *old_oid,
+ unsigned int flags, enum action_on_err onerr)
+{
+ return update_ref(msg, refname, new_oid ? new_oid->hash : NULL,
+ 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)
/* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
- scanf_fmts = xmalloc(st_add(st_mult(nr_rules, sizeof(char *)), total_len));
+ scanf_fmts = xmalloc(st_add(st_mult(sizeof(char *), nr_rules), total_len));
offset = 0;
for (i = 0; i < nr_rules; i++) {
int parse_hide_refs_config(const char *var, const char *value, const char *section)
{
+ const char *key;
if (!strcmp("transfer.hiderefs", var) ||
- /* NEEDSWORK: use parse_config_key() once both are merged */
- (starts_with(var, section) && var[strlen(section)] == '.' &&
- !strcmp(var + strlen(section), ".hiderefs"))) {
+ (!parse_config_key(var, section, NULL, NULL, &key) &&
+ !strcmp(key, "hiderefs"))) {
char *ref;
int len;
if (!refs)
return 0;
- iter = files_ref_iterator_begin(submodule, prefix, flags);
+ iter = refs->be->iterator_begin(refs, prefix, flags);
iter = prefix_ref_iterator_begin(iter, prefix, trim);
return do_for_each_ref_iterator(iter, fn, cb_data);
}
/* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
- unsigned char *sha1, int *flags)
+const char *resolve_ref_recursively(struct ref_store *refs,
+ const char *refname,
+ int resolve_flags,
+ unsigned char *sha1, int *flags)
{
static struct strbuf sb_refname = STRBUF_INIT;
int unused_flags;
int symref_count;
- struct ref_store *refs = get_ref_store(NULL);
if (!flags)
flags = &unused_flags;
for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
unsigned int read_flags = 0;
- if (read_raw_ref(refs, refname,
- sha1, &sb_refname, &read_flags)) {
+ if (refs->be->read_raw_ref(refs, refname,
+ sha1, &sb_refname, &read_flags)) {
*flags |= read_flags;
if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
return NULL;
return NULL;
}
+/* backend functions */
+int refs_init_db(struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->init_db(refs, 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,
+ resolve_flags, sha1, flags);
+}
+
+int resolve_gitlink_ref(const char *submodule, const char *refname,
+ unsigned char *sha1)
+{
+ size_t len = strlen(submodule);
+ struct ref_store *refs;
+ int flags;
+
+ while (len && submodule[len - 1] == '/')
+ len--;
+
+ if (!len)
+ return -1;
+
+ if (submodule[len]) {
+ /* We need to strip off one or more trailing slashes */
+ char *stripped = xmemdupz(submodule, len);
+
+ refs = get_ref_store(stripped);
+ free(stripped);
+ } else {
+ refs = get_ref_store(submodule);
+ }
+
+ if (!refs)
+ return -1;
+
+ if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
+ is_null_sha1(sha1))
+ return -1;
+ return 0;
+}
+
+struct submodule_hash_entry
+{
+ struct hashmap_entry ent; /* must be the first member! */
+
+ struct ref_store *refs;
+
+ /* NUL-terminated name of submodule: */
+ char submodule[FLEX_ARRAY];
+};
+
+static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
+ const void *keydata)
+{
+ const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
+ const char *submodule = keydata ? keydata : e2->submodule;
+
+ return strcmp(e1->submodule, submodule);
+}
+
+static struct submodule_hash_entry *alloc_submodule_hash_entry(
+ const char *submodule, struct ref_store *refs)
+{
+ struct submodule_hash_entry *entry;
+
+ FLEX_ALLOC_STR(entry, submodule, submodule);
+ hashmap_entry_init(entry, strhash(submodule));
+ entry->refs = refs;
+ return entry;
+}
+
/* A pointer to the ref_store for the main repository: */
static struct ref_store *main_ref_store;
-/* A linked list of ref_stores for submodules: */
-static struct ref_store *submodule_ref_stores;
+/* A hashmap of ref_stores, stored by submodule name: */
+static struct hashmap submodule_ref_stores;
-void base_ref_store_init(struct ref_store *refs,
- const struct ref_storage_be *be,
- const char *submodule)
+/*
+ * 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.
+ */
+static struct ref_store *lookup_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;
+
+ entry = hashmap_get_from_hash(&submodule_ref_stores,
+ strhash(submodule), submodule);
+ return entry ? entry->refs : NULL;
+}
+
+/*
+ * 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)
{
- refs->be = be;
if (!submodule) {
if (main_ref_store)
die("BUG: main_ref_store initialized twice");
- refs->submodule = "";
- refs->next = NULL;
main_ref_store = refs;
} else {
- if (lookup_ref_store(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);
-
- refs->submodule = xstrdup(submodule);
- refs->next = submodule_ref_stores;
- submodule_ref_stores = refs;
}
}
-struct ref_store *ref_store_init(const char *submodule)
+/*
+ * Create, record, and return a ref_store instance for the specified
+ * submodule (or the main repository if submodule is NULL).
+ */
+static struct ref_store *ref_store_init(const char *submodule)
{
const char *be_name = "files";
struct ref_storage_be *be = find_ref_storage_backend(be_name);
+ struct ref_store *refs;
if (!be)
die("BUG: reference backend %s is unknown", be_name);
- if (!submodule || !*submodule)
- return be->init(NULL);
- else
- return be->init(submodule);
-}
-
-struct ref_store *lookup_ref_store(const char *submodule)
-{
- struct ref_store *refs;
-
- if (!submodule || !*submodule)
- return main_ref_store;
-
- for (refs = submodule_ref_stores; refs; refs = refs->next) {
- if (!strcmp(submodule, refs->submodule))
- return refs;
- }
-
- return NULL;
+ refs = be->init(submodule);
+ register_ref_store(refs, submodule);
+ return refs;
}
struct ref_store *get_ref_store(const char *submodule)
return refs;
}
-void assert_main_repository(struct ref_store *refs, const char *caller)
+void base_ref_store_init(struct ref_store *refs,
+ const struct ref_storage_be *be)
{
- if (*refs->submodule)
- die("BUG: %s called for a submodule", caller);
+ refs->be = be;
}
/* backend functions */
+int pack_refs(unsigned int flags)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->pack_refs(refs, flags);
+}
+
+int peel_ref(const char *refname, unsigned char *sha1)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->peel_ref(refs, refname, sha1);
+}
+
+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);
+}
+
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
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)
+{
+ 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)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+ struct ref_iterator *iter;
+
+ iter = refs->be->reflog_iterator_begin(refs);
+
+ 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)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->for_each_reflog_ent_reverse(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->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+}
+
+int reflog_exists(const char *refname)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->reflog_exists(refs, refname);
+}
+
+int safe_create_reflog(const char *refname, int force_create,
+ struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->create_reflog(refs, refname, force_create, err);
+}
+
+int delete_reflog(const char *refname)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->delete_reflog(refs, refname);
+}
+
+int reflog_expire(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)
+{
+ 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);
+}
+
+int initial_ref_transaction_commit(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->initial_transaction_commit(refs, transaction, err);
+}
+
+int delete_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)
+{
+ struct ref_store *refs = get_ref_store(NULL);
+
+ return refs->be->rename_ref(refs, oldref, newref, logmsg);
+}