diff options
Diffstat (limited to 'refs.c')
-rw-r--r-- | refs.c | 978 |
1 files changed, 743 insertions, 235 deletions
@@ -3,11 +3,16 @@ */ #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 @@ -169,11 +174,41 @@ int refname_is_safe(const char *refname) return 1; } +/* + * Return true if refname, which has the specified oid and flags, can + * be resolved to an object in the database. If the referred-to object + * does not exist, emit a warning and return false. + */ +int ref_resolves_to_object(const char *refname, + const struct object_id *oid, + unsigned int flags) +{ + if (flags & REF_ISBROKEN) + return 0; + if (!has_sha1_file(oid->hash)) { + error("%s does not point to a valid object!", refname); + return 0; + } + 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 */ @@ -183,13 +218,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); @@ -197,8 +239,7 @@ int read_ref(const char *refname, unsigned char *sha1) int ref_exists(const char *refname) { - unsigned char sha1[20]; - return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL); + return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL); } static int filter_refs(const char *refname, const struct object_id *oid, @@ -206,7 +247,7 @@ static int filter_refs(const char *refname, const struct object_id *oid, { 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); } @@ -244,12 +285,11 @@ static int warn_if_dangling_symref(const char *refname, const struct object_id * { struct warn_if_dangling_data *d = cb_data; const char *resolves_to; - struct object_id junk; if (!(flags & REF_ISSYMREF)) return 0; - resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL); + resolves_to = resolve_ref_unsafe(refname, 0, NULL, NULL); if (!resolves_to || (d->refname ? strcmp(resolves_to, d->refname) @@ -284,34 +324,34 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li for_each_rawref(warn_if_dangling_symref, &data); } -int for_each_tag_ref(each_ref_fn fn, void *cb_data) +int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return for_each_ref_in("refs/tags/", fn, cb_data); + return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data); } -int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) +int for_each_tag_ref(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_main_ref_store(), fn, cb_data); } -int for_each_branch_ref(each_ref_fn fn, void *cb_data) +int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return for_each_ref_in("refs/heads/", fn, cb_data); + return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data); } -int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) +int for_each_branch_ref(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_main_ref_store(), fn, cb_data); } -int for_each_remote_ref(each_ref_fn fn, void *cb_data) +int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return for_each_ref_in("refs/remotes/", fn, cb_data); + return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data); } -int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) +int for_each_remote_ref(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_main_ref_store(), fn, cb_data); } int head_ref_namespaced(each_ref_fn fn, void *cb_data) @@ -365,11 +405,11 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data) 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[] = { @@ -428,29 +468,31 @@ int expand_ref(const char *str, int len, unsigned char *sha1, char **ref) { const char **p, *r; int refs_found = 0; + struct strbuf fullref = STRBUF_INIT; *ref = NULL; for (p = ref_rev_parse_rules; *p; p++) { - char fullref[PATH_MAX]; unsigned char sha1_from_ref[20]; unsigned char *this_result; int flag; this_result = refs_found ? sha1_from_ref : sha1; - mksnpath(fullref, sizeof(fullref), *p, len, str); - r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING, + strbuf_reset(&fullref); + strbuf_addf(&fullref, *p, len, str); + r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING, this_result, &flag); if (r) { if (!refs_found++) *ref = xstrdup(r); if (!warn_ambiguous_refs) break; - } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) { - warning("ignoring dangling symref %s.", fullref); - } else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) { - warning("ignoring broken ref %s.", fullref); + } else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) { + warning("ignoring dangling symref %s.", fullref.buf); + } else if ((flag & REF_ISBROKEN) && strchr(fullref.buf, '/')) { + warning("ignoring broken ref %s.", fullref.buf); } } + strbuf_release(&fullref); return refs_found; } @@ -459,21 +501,22 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) char *last_branch = substitute_branch_name(&str, &len); const char **p; int logs_found = 0; + struct strbuf path = STRBUF_INIT; *log = NULL; for (p = ref_rev_parse_rules; *p; p++) { unsigned char hash[20]; - char path[PATH_MAX]; const char *ref, *it; - mksnpath(path, sizeof(path), *p, len, str); - ref = resolve_ref_unsafe(path, RESOLVE_REF_READING, + strbuf_reset(&path); + strbuf_addf(&path, *p, len, str); + ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING, hash, NULL); if (!ref) continue; - if (reflog_exists(path)) - it = path; - else if (strcmp(ref, path) && reflog_exists(ref)) + if (reflog_exists(path.buf)) + it = path.buf; + else if (strcmp(ref, path.buf) && reflog_exists(ref)) it = ref; else continue; @@ -484,6 +527,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) if (!warn_ambiguous_refs) break; } + strbuf_release(&path); free(last_branch); return logs_found; } @@ -515,6 +559,21 @@ enum ref_type ref_type(const char *refname) return REF_TYPE_NORMAL; } +long get_files_ref_lock_timeout_ms(void) +{ + static int configured = 0; + + /* The default timeout is 100 ms: */ + static int timeout_ms = 100; + + if (!configured) { + git_config_get_int("core.filesreflocktimeout", &timeout_ms); + configured = 1; + } + + return timeout_ms; +} + static int write_pseudoref(const char *pseudoref, const unsigned char *sha1, const unsigned char *old_sha1, struct strbuf *err) { @@ -527,11 +586,13 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1, strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1)); filename = git_path("%s", pseudoref); - fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR); + fd = hold_lock_file_for_update_timeout(&lock, filename, + LOCK_DIE_ON_ERROR, + get_files_ref_lock_timeout_ms()); if (fd < 0) { strbuf_addf(err, "could not open '%s' for writing: %s", filename, strerror(errno)); - return -1; + goto done; } if (old_sha1) { @@ -546,7 +607,7 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1, } } - if (write_in_full(fd, buf.buf, buf.len) != buf.len) { + if (write_in_full(fd, buf.buf, buf.len) < 0) { strbuf_addf(err, "could not write to '%s'", filename); rollback_lock_file(&lock); goto done; @@ -570,8 +631,9 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1 int fd; unsigned char actual_old_sha1[20]; - fd = hold_lock_file_for_update(&lock, filename, - LOCK_DIE_ON_ERROR); + fd = hold_lock_file_for_update_timeout( + &lock, filename, LOCK_DIE_ON_ERROR, + get_files_ref_lock_timeout_ms()); if (fd < 0) die_errno(_("Could not open '%s' for writing"), filename); if (read_ref(pseudoref, actual_old_sha1)) @@ -591,19 +653,23 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1 return 0; } -int delete_ref(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, NULL, &err) || + flags, msg, &err) || ref_transaction_commit(transaction, &err)) { error("%s", err.buf); ref_transaction_free(transaction); @@ -615,6 +681,13 @@ int delete_ref(const char *refname, const unsigned char *old_sha1, 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; @@ -658,7 +731,7 @@ int is_branch(const char *refname) struct read_ref_at_cb { const char *refname; - unsigned long at_time; + timestamp_t at_time; int cnt; int reccnt; unsigned char *sha1; @@ -667,15 +740,15 @@ struct read_ref_at_cb { 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(unsigned char *osha1, unsigned char *nsha1, - const char *email, unsigned long timestamp, int tz, +static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct read_ref_at_cb *cb = cb_data; @@ -698,31 +771,31 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1, * 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, - const char *email, unsigned long timestamp, +static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct read_ref_at_cb *cb = cb_data; @@ -735,16 +808,16 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1, *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; } -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; @@ -761,7 +834,7 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb); if (!cb.reccnt) { - if (flags & GET_SHA1_QUIETLY) + if (flags & GET_OID_QUIETLY) exit(128); else die("Log for %s is empty.", refname); @@ -774,20 +847,42 @@ 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) { - 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]); @@ -818,9 +913,9 @@ struct ref_update *ref_transaction_add_update( 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; } @@ -842,6 +937,8 @@ int ref_transaction_update(struct ref_transaction *transaction, return -1; } + flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS; + flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0); ref_transaction_add_update(transaction, refname, flags, @@ -895,18 +992,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) || @@ -937,12 +1036,22 @@ 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; static char **scanf_fmts; static int nr_rules; char *short_name; + struct strbuf resolved_buf = STRBUF_INIT; if (!nr_rules) { /* @@ -1001,7 +1110,6 @@ char *shorten_unambiguous_ref(const char *refname, int strict) */ for (j = 0; j < rules_to_fail; j++) { const char *rule = ref_rev_parse_rules[j]; - char refname[PATH_MAX]; /* skip matched rule */ if (i == j) @@ -1012,9 +1120,10 @@ char *shorten_unambiguous_ref(const char *refname, int strict) * (with this previous rule) to a valid ref * read_ref() returns 0 on success */ - mksnpath(refname, sizeof(refname), - rule, short_name_len, short_name); - if (ref_exists(refname)) + strbuf_reset(&resolved_buf); + strbuf_addf(&resolved_buf, rule, + short_name_len, short_name); + if (ref_exists(resolved_buf.buf)) break; } @@ -1022,10 +1131,13 @@ char *shorten_unambiguous_ref(const char *refname, int strict) * short name is non-ambiguous if all previous rules * haven't resolved to a valid ref */ - if (j == rules_to_fail) + if (j == rules_to_fail) { + strbuf_release(&resolved_buf); return short_name; + } } + strbuf_release(&resolved_buf); free(short_name); return xstrdup(refname); } @@ -1066,7 +1178,7 @@ int ref_is_hidden(const char *refname, const char *refname_full) const char *match = hide_refs->items[i].string; const char *subject; int neg = 0; - int len; + const char *p; if (*match == '!') { neg = 1; @@ -1081,10 +1193,9 @@ int ref_is_hidden(const char *refname, const char *refname_full) } /* 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; @@ -1118,14 +1229,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); @@ -1134,19 +1248,13 @@ int rename_ref_available(const char *old_refname, const char *new_refname) return ok; } -int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) +int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { struct object_id oid; int flag; - if (submodule) { - if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0) - return fn("HEAD", &oid, 0, cb_data); - - return 0; - } - - if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag)) + if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING, + oid.hash, &flag)) return fn("HEAD", &oid, flag, cb_data); return 0; @@ -1154,7 +1262,34 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data) int head_ref(each_ref_fn fn, void *cb_data) { - return head_ref_submodule(NULL, fn, cb_data); + return refs_head_ref(get_main_ref_store(), 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); + + /* Sanity check for subclasses: */ + if (!iter->ordered) + BUG("reference iterator is not ordered"); + + return iter; } /* @@ -1166,34 +1301,38 @@ 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) 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); } +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) +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(submodule, "", fn, 0, 0, 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) @@ -1202,19 +1341,27 @@ 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) +int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, + each_ref_fn fn, void *cb_data, + unsigned int broken) { - return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data); + unsigned int flag = 0; + + if (broken) + flag = DO_FOR_EACH_INCLUDE_BROKEN; + return do_for_each_ref(refs, prefix, fn, 0, flag, 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), + DO_FOR_EACH_INCLUDE_BROKEN, cb_data); } int for_each_namespaced_ref(each_ref_fn fn, void *cb_data) @@ -1222,27 +1369,43 @@ 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); +} + +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 */ -static const char *resolve_ref_recursively(struct ref_store *refs, - const char *refname, - int resolve_flags, - unsigned char *sha1, int *flags) +const char *refs_resolve_ref_unsafe(struct ref_store *refs, + const char *refname, + int resolve_flags, + unsigned char *sha1, int *flags) { static struct strbuf sb_refname = STRBUF_INIT; + struct object_id unused_oid; int unused_flags; int symref_count; + if (!sha1) + sha1 = unused_oid.hash; if (!flags) flags = &unused_flags; @@ -1269,11 +1432,24 @@ static const char *resolve_ref_recursively(struct ref_store *refs, 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)) + + /* In reading mode, refs must eventually resolve */ + if (resolve_flags & RESOLVE_REF_READING) + return NULL; + + /* + * Otherwise a missing ref is OK. But the files backend + * may show errors besides ENOENT if there are + * similarly-named refs. + */ + if (errno != ENOENT && + errno != EISDIR && + errno != ENOTDIR) return NULL; + hashclr(sha1); if (*flags & REF_BAD_NAME) *flags |= REF_ISBROKEN; @@ -1313,7 +1489,7 @@ static 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); } @@ -1321,176 +1497,448 @@ 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); } 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); - } + 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; } +struct ref_store_hash_entry +{ + struct hashmap_entry ent; /* must be the first member! */ + + struct ref_store *refs; + + /* NUL-terminated identifier of the ref store: */ + char name[FLEX_ARRAY]; +}; + +static int ref_store_hash_cmp(const void *unused_cmp_data, + const void *entry, const void *entry_or_key, + const void *keydata) +{ + const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key; + const char *name = keydata ? keydata : e2->name; + + return strcmp(e1->name, name); +} + +static struct ref_store_hash_entry *alloc_ref_store_hash_entry( + const char *name, struct ref_store *refs) +{ + struct ref_store_hash_entry *entry; + + FLEX_ALLOC_STR(entry, name, name); + hashmap_entry_init(entry, strhash(name)); + 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) +/* A hashmap of ref_stores, stored by worktree id: */ +static struct hashmap worktree_ref_stores; + +/* + * Look up a ref store by name. If that ref_store hasn't been + * registered yet, return NULL. + */ +static struct ref_store *lookup_ref_store_map(struct hashmap *map, + const char *name) { - refs->be = be; - if (!submodule) { - if (main_ref_store) - die("BUG: main_ref_store initialized twice"); + struct ref_store_hash_entry *entry; - refs->submodule = ""; - refs->next = NULL; - main_ref_store = refs; - } else { - if (lookup_ref_store(submodule)) - die("BUG: ref_store for submodule '%s' initialized twice", - submodule); + if (!map->tablesize) + /* It's initialized on demand in register_ref_store(). */ + return NULL; - refs->submodule = xstrdup(submodule); - refs->next = submodule_ref_stores; - submodule_ref_stores = refs; - } + entry = hashmap_get_from_hash(map, strhash(name), name); + return entry ? entry->refs : NULL; } -struct ref_store *ref_store_init(const char *submodule) +/* + * Create, record, and return a ref_store instance for the specified + * gitdir. + */ +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); + 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); + refs = be->init(gitdir, flags); + return refs; } -struct ref_store *lookup_ref_store(const char *submodule) +struct ref_store *get_main_ref_store(void) { - struct ref_store *refs; - - if (!submodule || !*submodule) + if (main_ref_store) return main_ref_store; - for (refs = submodule_ref_stores; refs; refs = refs->next) { - if (!strcmp(submodule, refs->submodule)) - return refs; - } + main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS); + return main_ref_store; +} - return NULL; +/* + * Associate a ref store with a name. It is a fatal error to call this + * function twice for the same name. + */ +static void register_ref_store_map(struct hashmap *map, + const char *type, + struct ref_store *refs, + const char *name) +{ + if (!map->tablesize) + hashmap_init(map, ref_store_hash_cmp, NULL, 0); + + 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_ref_store(const char *submodule) +struct ref_store *get_submodule_ref_store(const char *submodule) { + struct strbuf submodule_sb = STRBUF_INIT; struct ref_store *refs; + char *to_free = NULL; + size_t len; - if (!submodule || !*submodule) { - refs = lookup_ref_store(NULL); + if (!submodule) + return NULL; - if (!refs) - refs = ref_store_init(NULL); - } else { - refs = lookup_ref_store(submodule); + len = strlen(submodule); + while (len && is_dir_sep(submodule[len - 1])) + len--; + if (!len) + return NULL; - if (!refs) { - struct strbuf submodule_sb = STRBUF_INIT; + if (submodule[len]) + /* We need to strip off one or more trailing slashes */ + submodule = to_free = xmemdupz(submodule, len); - strbuf_addstr(&submodule_sb, submodule); - if (is_nonbare_repository_dir(&submodule_sb)) - refs = ref_store_init(submodule); - strbuf_release(&submodule_sb); - } - } + refs = lookup_ref_store_map(&submodule_ref_stores, submodule); + if (refs) + goto done; + + strbuf_addstr(&submodule_sb, submodule); + if (!is_nonbare_repository_dir(&submodule_sb)) + goto done; + + if (submodule_to_gitdir(&submodule_sb, submodule)) + goto done; + + /* assume that add_submodule_odb() has been called */ + refs = ref_store_init(submodule_sb.buf, + REF_STORE_READ | REF_STORE_ODB); + register_ref_store_map(&submodule_ref_stores, "submodule", + refs, submodule); + +done: + strbuf_release(&submodule_sb); + free(to_free); return refs; } -void assert_main_repository(struct ref_store *refs, const char *caller) +struct ref_store *get_worktree_ref_store(const struct worktree *wt) { - if (*refs->submodule) - die("BUG: %s called for a submodule", caller); + 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; } -/* backend functions */ -int pack_refs(unsigned int flags) +void base_ref_store_init(struct ref_store *refs, + const struct ref_storage_be *be) { - struct ref_store *refs = get_ref_store(NULL); + refs->be = be; +} +/* backend functions */ +int refs_pack_refs(struct ref_store *refs, unsigned int flags) +{ return refs->be->pack_refs(refs, flags); } +int refs_peel_ref(struct ref_store *refs, const char *refname, + unsigned char *sha1) +{ + int flag; + unsigned char base[20]; + + if (current_ref_iter && current_ref_iter->refname == refname) { + struct object_id peeled; + + if (ref_iterator_peel(current_ref_iter, &peeled)) + return -1; + hashcpy(sha1, peeled.hash); + return 0; + } + + if (refs_read_ref_full(refs, refname, + RESOLVE_REF_READING, base, &flag)) + return -1; + + return peel_object(base, 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_create_symref(get_main_ref_store(), ref_target, + refs_heads_master, logmsg); +} - return refs->be->create_symref(refs, ref_target, 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 = get_ref_store(NULL); + 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 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 *extras, + const struct string_list *skip, + struct strbuf *err) { - struct ref_store *refs = get_ref_store(NULL); + 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; - return refs->be->verify_refname_available(refs, refname, extra, skip, err); + /* + * 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 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); @@ -1498,43 +1946,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, @@ -1544,31 +2033,50 @@ 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, const char *msg, + struct string_list *refnames, unsigned int flags) +{ + return refs->be->delete_refs(refs, msg, refnames, flags); +} + +int delete_refs(const char *msg, struct string_list *refnames, + unsigned int flags) { - struct ref_store *refs = get_ref_store(NULL); + return refs_delete_refs(get_main_ref_store(), msg, refnames, flags); +} - return refs->be->delete_refs(refs, 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) { - struct ref_store *refs = get_ref_store(NULL); + return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg); +} - return refs->be->rename_ref(refs, oldref, newref, logmsg); +int refs_copy_existing_ref(struct ref_store *refs, const char *oldref, + const char *newref, const char *logmsg) +{ + return refs->be->copy_ref(refs, oldref, newref, logmsg); +} + +int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg) +{ + return refs_copy_existing_ref(get_main_ref_store(), oldref, newref, logmsg); } |