*/
#include "cache.h"
+#include "config.h"
#include "hashmap.h"
#include "lockfile.h"
+#include "iterator.h"
#include "refs.h"
#include "refs/refs-internal.h"
#include "object.h"
#include "tag.h"
#include "submodule.h"
+#include "worktree.h"
/*
* List of all available backends
{
struct ref_filter *filter = (struct ref_filter *)data;
- if (wildmatch(filter->pattern, refname, 0, NULL))
+ if (wildmatch(filter->pattern, refname, 0))
return 0;
return filter->fn(refname, oid, flags, filter->cb_data);
}
struct read_ref_at_cb {
const char *refname;
- unsigned long at_time;
+ timestamp_t at_time;
int cnt;
int reccnt;
unsigned char *sha1;
unsigned char osha1[20];
unsigned char nsha1[20];
int tz;
- unsigned long date;
+ timestamp_t date;
char **msg;
- unsigned long *cutoff_time;
+ timestamp_t *cutoff_time;
int *cutoff_tz;
int *cutoff_cnt;
};
static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct read_ref_at_cb *cb = cb_data;
}
static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp,
+ const char *email, timestamp_t timestamp,
int tz, const char *message, void *cb_data)
{
struct read_ref_at_cb *cb = cb_data;
return 1;
}
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
unsigned char *sha1, char **msg,
- unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+ timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
{
struct read_ref_at_cb cb;
void ref_transaction_free(struct ref_transaction *transaction)
{
- int i;
+ size_t i;
if (!transaction)
return;
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ case REF_TRANSACTION_CLOSED:
+ /* OK */
+ break;
+ case REF_TRANSACTION_PREPARED:
+ die("BUG: free called on a prepared reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
+
for (i = 0; i < transaction->nr; i++) {
free(transaction->updates[i]->msg);
free(transaction->updates[i]);
update->flags = flags;
if (flags & REF_HAVE_NEW)
- hashcpy(update->new_sha1, new_sha1);
+ hashcpy(update->new_oid.hash, new_sha1);
if (flags & REF_HAVE_OLD)
- hashcpy(update->old_sha1, old_sha1);
+ hashcpy(update->old_oid.hash, old_sha1);
update->msg = xstrdup_or_null(msg);
return update;
}
const char *match = hide_refs->items[i].string;
const char *subject;
int neg = 0;
- int len;
+ const char *p;
if (*match == '!') {
neg = 1;
}
/* refname can be NULL when namespaces are used. */
- if (!subject || !starts_with(subject, match))
- continue;
- len = strlen(match);
- if (!subject[len] || subject[len] == '/')
+ if (subject &&
+ skip_prefix(subject, match, &p) &&
+ (!*p || *p == '/'))
return !neg;
}
return 0;
return head_ref_submodule(NULL, fn, cb_data);
}
+struct ref_iterator *refs_ref_iterator_begin(
+ struct ref_store *refs,
+ const char *prefix, int trim, int flags)
+{
+ struct ref_iterator *iter;
+
+ if (ref_paranoia < 0)
+ ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
+ if (ref_paranoia)
+ flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+
+ iter = refs->be->iterator_begin(refs, prefix, flags);
+
+ /*
+ * `iterator_begin()` already takes care of prefix, but we
+ * might need to do some trimming:
+ */
+ if (trim)
+ iter = prefix_ref_iterator_begin(iter, "", trim);
+
+ return iter;
+}
+
/*
* Call fn for each reference in the specified submodule for which the
* refname begins with prefix. If trim is non-zero, then trim that
if (!refs)
return 0;
- iter = refs->be->iterator_begin(refs, prefix, flags);
- iter = prefix_ref_iterator_begin(iter, prefix, trim);
+ iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
return do_for_each_ref_iterator(iter, fn, cb_data);
}
prefix, fn, cb_data);
}
+int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
+ each_ref_fn fn, void *cb_data,
+ unsigned int broken)
+{
+ unsigned int flag = 0;
+
+ if (broken)
+ flag = DO_FOR_EACH_INCLUDE_BROKEN;
+ return do_for_each_ref(get_submodule_ref_store(submodule),
+ prefix, fn, 0, flag, cb_data);
+}
+
int for_each_replace_ref(each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(get_main_ref_store(),
return refs_for_each_rawref(get_main_ref_store(), fn, cb_data);
}
+int refs_read_raw_ref(struct ref_store *ref_store,
+ const char *refname, unsigned char *sha1,
+ struct strbuf *referent, unsigned int *type)
+{
+ return ref_store->be->read_raw_ref(ref_store, refname, sha1, referent, type);
+}
+
/* This function needs to return a meaningful errno on failure */
const char *refs_resolve_ref_unsafe(struct ref_store *refs,
const char *refname,
for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
unsigned int read_flags = 0;
- if (refs->be->read_raw_ref(refs, refname,
- sha1, &sb_refname, &read_flags)) {
+ if (refs_read_raw_ref(refs, refname,
+ sha1, &sb_refname, &read_flags)) {
*flags |= read_flags;
if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
return NULL;
return 0;
}
-struct submodule_hash_entry
+struct ref_store_hash_entry
{
struct hashmap_entry ent; /* must be the first member! */
struct ref_store *refs;
- /* NUL-terminated name of submodule: */
- char submodule[FLEX_ARRAY];
+ /* NUL-terminated identifier of the ref store: */
+ char name[FLEX_ARRAY];
};
-static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
+static int ref_store_hash_cmp(const void *unused_cmp_data,
+ 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;
+ const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
+ const char *name = keydata ? keydata : e2->name;
- return strcmp(e1->submodule, submodule);
+ return strcmp(e1->name, name);
}
-static struct submodule_hash_entry *alloc_submodule_hash_entry(
- const char *submodule, struct ref_store *refs)
+static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
+ const char *name, struct ref_store *refs)
{
- struct submodule_hash_entry *entry;
+ struct ref_store_hash_entry *entry;
- FLEX_ALLOC_STR(entry, submodule, submodule);
- hashmap_entry_init(entry, strhash(submodule));
+ FLEX_ALLOC_STR(entry, name, name);
+ hashmap_entry_init(entry, strhash(name));
entry->refs = refs;
return entry;
}
/* A hashmap of ref_stores, stored by submodule name: */
static struct hashmap submodule_ref_stores;
+/* A hashmap of ref_stores, stored by worktree id: */
+static struct hashmap worktree_ref_stores;
+
/*
- * Return the ref_store instance for the specified submodule. If that
- * ref_store hasn't been initialized yet, return NULL.
+ * Look up a ref store by name. If that ref_store hasn't been
+ * registered yet, return NULL.
*/
-static struct ref_store *lookup_submodule_ref_store(const char *submodule)
+static struct ref_store *lookup_ref_store_map(struct hashmap *map,
+ const char *name)
{
- struct submodule_hash_entry *entry;
+ struct ref_store_hash_entry *entry;
- if (!submodule_ref_stores.tablesize)
+ if (!map->tablesize)
/* It's initialized on demand in register_ref_store(). */
return NULL;
- entry = hashmap_get_from_hash(&submodule_ref_stores,
- strhash(submodule), submodule);
+ entry = hashmap_get_from_hash(map, strhash(name), name);
return entry ? entry->refs : NULL;
}
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));
+ main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS);
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.
+ * Associate a ref store with a name. It is a fatal error to call this
+ * function twice for the same name.
*/
-static void register_submodule_ref_store(struct ref_store *refs,
- const char *submodule)
+static void register_ref_store_map(struct hashmap *map,
+ const char *type,
+ struct ref_store *refs,
+ const char *name)
{
- if (!submodule_ref_stores.tablesize)
- hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
+ if (!map->tablesize)
+ hashmap_init(map, ref_store_hash_cmp, NULL, 0);
- if (hashmap_put(&submodule_ref_stores,
- alloc_submodule_hash_entry(submodule, refs)))
- die("BUG: ref_store for submodule '%s' initialized twice",
- submodule);
+ if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
+ die("BUG: %s ref_store '%s' initialized twice", type, name);
}
struct ref_store *get_submodule_ref_store(const char *submodule)
return get_main_ref_store();
}
- refs = lookup_submodule_ref_store(submodule);
+ refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
if (refs)
return refs;
/* 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);
+ register_ref_store_map(&submodule_ref_stores, "submodule",
+ refs, submodule);
strbuf_release(&submodule_sb);
return refs;
}
+struct ref_store *get_worktree_ref_store(const struct worktree *wt)
+{
+ struct ref_store *refs;
+ const char *id;
+
+ if (wt->is_current)
+ return get_main_ref_store();
+
+ id = wt->id ? wt->id : "/";
+ refs = lookup_ref_store_map(&worktree_ref_stores, id);
+ if (refs)
+ return refs;
+
+ if (wt->id)
+ refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
+ REF_STORE_ALL_CAPS);
+ else
+ refs = ref_store_init(get_git_common_dir(),
+ REF_STORE_ALL_CAPS);
+
+ if (refs)
+ register_ref_store_map(&worktree_ref_stores, "worktree",
+ refs, id);
+ return refs;
+}
+
void base_ref_store_init(struct ref_store *refs,
const struct ref_storage_be *be)
{
refs_heads_master, logmsg);
}
+int ref_update_reject_duplicates(struct string_list *refnames,
+ struct strbuf *err)
+{
+ size_t i, n = refnames->nr;
+
+ assert(err);
+
+ for (i = 1; i < n; i++) {
+ int cmp = strcmp(refnames->items[i - 1].string,
+ refnames->items[i].string);
+
+ if (!cmp) {
+ strbuf_addf(err,
+ "multiple updates for ref '%s' not allowed.",
+ refnames->items[i].string);
+ return 1;
+ } else if (cmp > 0) {
+ die("BUG: ref_update_reject_duplicates() received unsorted list");
+ }
+ }
+ return 0;
+}
+
+int ref_transaction_prepare(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = transaction->ref_store;
+
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ /* Good. */
+ break;
+ case REF_TRANSACTION_PREPARED:
+ die("BUG: prepare called twice on reference transaction");
+ break;
+ case REF_TRANSACTION_CLOSED:
+ die("BUG: prepare called on a closed reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
+
+ if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+ strbuf_addstr(err,
+ _("ref updates forbidden inside quarantine environment"));
+ return -1;
+ }
+
+ return refs->be->transaction_prepare(refs, transaction, err);
+}
+
+int ref_transaction_abort(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = transaction->ref_store;
+ int ret = 0;
+
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ /* No need to abort explicitly. */
+ break;
+ case REF_TRANSACTION_PREPARED:
+ ret = refs->be->transaction_abort(refs, transaction, err);
+ break;
+ case REF_TRANSACTION_CLOSED:
+ die("BUG: abort called on a closed reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
+
+ ref_transaction_free(transaction);
+ return ret;
+}
+
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err)
{
struct ref_store *refs = transaction->ref_store;
+ int ret;
+
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ /* Need to prepare first. */
+ ret = ref_transaction_prepare(transaction, err);
+ if (ret)
+ return ret;
+ break;
+ case REF_TRANSACTION_PREPARED:
+ /* Fall through to finish. */
+ break;
+ case REF_TRANSACTION_CLOSED:
+ die("BUG: commit called on a closed reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
- return refs->be->transaction_commit(refs, transaction, err);
+ return refs->be->transaction_finish(refs, transaction, err);
}
int refs_verify_refname_available(struct ref_store *refs,
const char *refname,
- const struct string_list *extra,
+ const struct string_list *extras,
const struct string_list *skip,
struct strbuf *err)
{
- return refs->be->verify_refname_available(refs, refname, extra, skip, err);
+ const char *slash;
+ const char *extra_refname;
+ struct strbuf dirname = STRBUF_INIT;
+ struct strbuf referent = STRBUF_INIT;
+ struct object_id oid;
+ unsigned int type;
+ struct ref_iterator *iter;
+ int ok;
+ int ret = -1;
+
+ /*
+ * For the sake of comments in this function, suppose that
+ * refname is "refs/foo/bar".
+ */
+
+ assert(err);
+
+ strbuf_grow(&dirname, strlen(refname) + 1);
+ for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+ /* Expand dirname to the new prefix, not including the trailing slash: */
+ strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
+
+ /*
+ * We are still at a leading dir of the refname (e.g.,
+ * "refs/foo"; if there is a reference with that name,
+ * it is a conflict, *unless* it is in skip.
+ */
+ if (skip && string_list_has_string(skip, dirname.buf))
+ continue;
+
+ if (!refs_read_raw_ref(refs, dirname.buf, oid.hash, &referent, &type)) {
+ strbuf_addf(err, "'%s' exists; cannot create '%s'",
+ dirname.buf, refname);
+ goto cleanup;
+ }
+
+ if (extras && string_list_has_string(extras, dirname.buf)) {
+ strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
+ refname, dirname.buf);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * We are at the leaf of our refname (e.g., "refs/foo/bar").
+ * There is no point in searching for a reference with that
+ * name, because a refname isn't considered to conflict with
+ * itself. But we still need to check for references whose
+ * names are in the "refs/foo/bar/" namespace, because they
+ * *do* conflict.
+ */
+ strbuf_addstr(&dirname, refname + dirname.len);
+ strbuf_addch(&dirname, '/');
+
+ iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN);
+ while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+ if (skip &&
+ string_list_has_string(skip, iter->refname))
+ continue;
+
+ strbuf_addf(err, "'%s' exists; cannot create '%s'",
+ iter->refname, refname);
+ ref_iterator_abort(iter);
+ goto cleanup;
+ }
+
+ if (ok != ITER_DONE)
+ die("BUG: error while iterating over references");
+
+ extra_refname = find_descendant_ref(dirname.buf, extras, skip);
+ if (extra_refname)
+ strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
+ refname, extra_refname);
+ else
+ ret = 0;
+
+cleanup:
+ strbuf_release(&referent);
+ strbuf_release(&dirname);
+ return ret;
}
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
return refs->be->initial_transaction_commit(refs, transaction, err);
}
-int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
- unsigned int flags)
+int refs_delete_refs(struct ref_store *refs, const char *msg,
+ struct string_list *refnames, unsigned int flags)
{
- return refs->be->delete_refs(refs, refnames, flags);
+ return refs->be->delete_refs(refs, msg, refnames, flags);
}
-int delete_refs(struct string_list *refnames, unsigned int flags)
+int delete_refs(const char *msg, struct string_list *refnames,
+ unsigned int flags)
{
- return refs_delete_refs(get_main_ref_store(), refnames, flags);
+ return refs_delete_refs(get_main_ref_store(), msg, refnames, flags);
}
int refs_rename_ref(struct ref_store *refs, const char *oldref,