aboutsummaryrefslogtreecommitdiffstats
path: root/refs.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c446
1 files changed, 332 insertions, 114 deletions
diff --git a/refs.c b/refs.c
index e4fda6b4bb..50c3849845 100644
--- a/refs.c
+++ b/refs.c
@@ -3,6 +3,7 @@
*/
#define USE_THE_REPOSITORY_VARIABLE
+#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
#include "advice.h"
@@ -24,12 +25,13 @@
#include "submodule.h"
#include "worktree.h"
#include "strvec.h"
-#include "repository.h"
+#include "repo-settings.h"
#include "setup.h"
#include "sigchain.h"
#include "date.h"
#include "commit.h"
#include "wildmatch.h"
+#include "ident.h"
/*
* List of all available backends
@@ -318,6 +320,12 @@ int check_refname_format(const char *refname, int flags)
return check_or_sanitize_refname(refname, flags, NULL);
}
+int refs_fsck(struct ref_store *refs, struct fsck_options *o,
+ struct worktree *wt)
+{
+ return refs->be->fsck(refs, o, wt);
+}
+
void sanitize_refname_component(const char *refname, struct strbuf *out)
{
if (check_or_sanitize_refname(refname, REFNAME_ALLOW_ONELEVEL, out))
@@ -414,7 +422,7 @@ int refs_ref_exists(struct ref_store *refs, const char *refname)
NULL, NULL);
}
-static int for_each_filter_refs(const char *refname,
+static int for_each_filter_refs(const char *refname, const char *referent,
const struct object_id *oid,
int flags, void *data)
{
@@ -424,7 +432,7 @@ static int for_each_filter_refs(const char *refname,
return 0;
if (filter->prefix)
skip_prefix(refname, filter->prefix, &refname);
- return filter->fn(refname, oid, flags, filter->cb_data);
+ return filter->fn(refname, referent, oid, flags, filter->cb_data);
}
struct warn_if_dangling_data {
@@ -435,7 +443,7 @@ struct warn_if_dangling_data {
const char *msg_fmt;
};
-static int warn_if_dangling_symref(const char *refname,
+static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags, void *cb_data)
{
@@ -506,7 +514,7 @@ int refs_head_ref_namespaced(struct ref_store *refs, each_ref_fn fn, void *cb_da
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
if (!refs_read_ref_full(refs, buf.buf, RESOLVE_REF_READING, &oid, &flag))
- ret = fn(buf.buf, &oid, flag, cb_data);
+ ret = fn(buf.buf, NULL, &oid, flag, cb_data);
strbuf_release(&buf);
return ret;
@@ -692,6 +700,53 @@ static char *substitute_branch_name(struct repository *r,
return NULL;
}
+void copy_branchname(struct strbuf *sb, const char *name, unsigned allowed)
+{
+ int len = strlen(name);
+ struct interpret_branch_name_options options = {
+ .allowed = allowed
+ };
+ int used = repo_interpret_branch_name(the_repository, name, len, sb,
+ &options);
+
+ if (used < 0)
+ used = 0;
+ strbuf_add(sb, name + used, len - used);
+}
+
+int check_branch_ref(struct strbuf *sb, const char *name)
+{
+ if (startup_info->have_repository)
+ copy_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
+ else
+ strbuf_addstr(sb, name);
+
+ /*
+ * This splice must be done even if we end up rejecting the
+ * name; builtin/branch.c::copy_or_rename_branch() still wants
+ * to see what the name expanded to so that "branch -m" can be
+ * used as a tool to correct earlier mistakes.
+ */
+ strbuf_splice(sb, 0, 0, "refs/heads/", 11);
+
+ if (*name == '-' ||
+ !strcmp(sb->buf, "refs/heads/HEAD"))
+ return -1;
+
+ return check_refname_format(sb->buf, 0);
+}
+
+int check_tag_ref(struct strbuf *sb, const char *name)
+{
+ if (name[0] == '-' || !strcmp(name, "HEAD"))
+ return -1;
+
+ strbuf_reset(sb);
+ strbuf_addf(sb, "refs/tags/%s", name);
+
+ return check_refname_format(sb->buf, 0);
+}
+
int repo_dwim_ref(struct repository *r, const char *str, int len,
struct object_id *oid, char **ref, int nonfatal_dangling_mark)
{
@@ -725,7 +780,7 @@ int expand_ref(struct repository *repo, const char *str, int len,
if (r) {
if (!refs_found++)
*ref = xstrdup(r);
- if (!warn_ambiguous_refs)
+ if (!repo_settings_get_warn_ambiguous_refs(repo))
break;
} else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) {
warning(_("ignoring dangling symref %s"), fullref.buf);
@@ -770,7 +825,7 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
if (oid)
oidcpy(oid, &hash);
}
- if (!warn_ambiguous_refs)
+ if (!repo_settings_get_warn_ambiguous_refs(r))
break;
}
strbuf_release(&path);
@@ -913,7 +968,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
- transaction = ref_store_transaction_begin(refs, &err);
+ transaction = ref_store_transaction_begin(refs, 0, &err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_oid,
NULL, flags, msg, &err) ||
@@ -953,7 +1008,8 @@ static char *normalize_reflog_message(const char *msg)
return strbuf_detach(&sb, NULL);
}
-int should_autocreate_reflog(const char *refname)
+int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,
+ const char *refname)
{
switch (log_all_ref_updates) {
case LOG_REFS_ALWAYS:
@@ -1110,6 +1166,7 @@ int read_ref_at(struct ref_store *refs, const char *refname,
}
struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
+ unsigned int flags,
struct strbuf *err)
{
struct ref_transaction *tr;
@@ -1117,6 +1174,7 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
CALLOC_ARRAY(tr, 1);
tr->ref_store = refs;
+ tr->flags = flags;
return tr;
}
@@ -1142,6 +1200,7 @@ void ref_transaction_free(struct ref_transaction *transaction)
for (i = 0; i < transaction->nr; i++) {
free(transaction->updates[i]->msg);
+ free(transaction->updates[i]->committer_info);
free((char *)transaction->updates[i]->new_target);
free((char *)transaction->updates[i]->old_target);
free(transaction->updates[i]);
@@ -1156,6 +1215,7 @@ struct ref_update *ref_transaction_add_update(
const struct object_id *new_oid,
const struct object_id *old_oid,
const char *new_target, const char *old_target,
+ const char *committer_info,
const char *msg)
{
struct ref_update *update;
@@ -1180,11 +1240,44 @@ struct ref_update *ref_transaction_add_update(
oidcpy(&update->new_oid, new_oid);
if ((flags & REF_HAVE_OLD) && old_oid)
oidcpy(&update->old_oid, old_oid);
+ if (!(flags & REF_SKIP_CREATE_REFLOG)) {
+ update->committer_info = xstrdup_or_null(committer_info);
+ update->msg = normalize_reflog_message(msg);
+ }
- update->msg = normalize_reflog_message(msg);
return update;
}
+static int transaction_refname_valid(const char *refname,
+ const struct object_id *new_oid,
+ unsigned int flags, struct strbuf *err)
+{
+ if (flags & REF_SKIP_REFNAME_VERIFICATION)
+ return 1;
+
+ if (is_pseudo_ref(refname)) {
+ const char *refusal_msg;
+ if (flags & REF_LOG_ONLY)
+ refusal_msg = _("refusing to update reflog for pseudoref '%s'");
+ else
+ refusal_msg = _("refusing to update pseudoref '%s'");
+ strbuf_addf(err, refusal_msg, refname);
+ return 0;
+ } else if ((new_oid && !is_null_oid(new_oid)) ?
+ check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
+ !refname_is_safe(refname)) {
+ const char *refusal_msg;
+ if (flags & REF_LOG_ONLY)
+ refusal_msg = _("refusing to update reflog with bad name '%s'");
+ else
+ refusal_msg = _("refusing to update ref with bad name '%s'");
+ strbuf_addf(err, refusal_msg, refname);
+ return 0;
+ }
+
+ return 1;
+}
+
int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const struct object_id *new_oid,
@@ -1202,21 +1295,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
return -1;
}
- if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
- ((new_oid && !is_null_oid(new_oid)) ?
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
- !refname_is_safe(refname))) {
- strbuf_addf(err, _("refusing to update ref with bad name '%s'"),
- refname);
+ if (!transaction_refname_valid(refname, new_oid, flags, err))
return -1;
- }
-
- if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
- is_pseudo_ref(refname)) {
- strbuf_addf(err, _("refusing to update pseudoref '%s'"),
- refname);
- return -1;
- }
if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
@@ -1233,7 +1313,53 @@ int ref_transaction_update(struct ref_transaction *transaction,
ref_transaction_add_update(transaction, refname, flags,
new_oid, old_oid, new_target,
- old_target, msg);
+ old_target, NULL, msg);
+
+ return 0;
+}
+
+/*
+ * Similar to`ref_transaction_update`, but this function is only for adding
+ * a reflog update. Supports providing custom committer information. The index
+ * field can be utiltized to order updates as desired. When not used, the
+ * updates default to being ordered by refname.
+ */
+static int ref_transaction_update_reflog(struct ref_transaction *transaction,
+ const char *refname,
+ const struct object_id *new_oid,
+ const struct object_id *old_oid,
+ const char *committer_info,
+ unsigned int flags,
+ const char *msg,
+ uint64_t index,
+ struct strbuf *err)
+{
+ struct ref_update *update;
+
+ assert(err);
+
+ flags |= REF_LOG_ONLY | REF_FORCE_CREATE_REFLOG | REF_NO_DEREF;
+
+ if (!transaction_refname_valid(refname, new_oid, flags, err))
+ return -1;
+
+ update = ref_transaction_add_update(transaction, refname, flags,
+ new_oid, old_oid, NULL, NULL,
+ committer_info, msg);
+ /*
+ * While we do set the old_oid value, we unset the flag to skip
+ * old_oid verification which only makes sense for refs.
+ */
+ update->flags &= ~REF_HAVE_OLD;
+ update->index = index;
+
+ /*
+ * Reference backends may need to know the max index to optimize
+ * their writes. So we store the max_index on the transaction level.
+ */
+ if (index > transaction->max_index)
+ transaction->max_index = index;
+
return 0;
}
@@ -1303,7 +1429,7 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
struct strbuf err = STRBUF_INIT;
int ret = 0;
- t = ref_store_transaction_begin(refs, &err);
+ t = ref_store_transaction_begin(refs, 0, &err);
if (!t ||
ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
flags, msg, &err) ||
@@ -1512,6 +1638,19 @@ const char **hidden_refs_to_excludes(const struct strvec *hide_refs)
return hide_refs->v;
}
+const char **get_namespaced_exclude_patterns(const char **exclude_patterns,
+ const char *namespace,
+ struct strvec *out)
+{
+ if (!namespace || !*namespace || !exclude_patterns || !*exclude_patterns)
+ return exclude_patterns;
+
+ for (size_t i = 0; exclude_patterns[i]; i++)
+ strvec_pushf(out, "%s%s", namespace, exclude_patterns[i]);
+
+ return out->v;
+}
+
const char *find_descendant_ref(const char *dirname,
const struct string_list *extras,
const struct string_list *skip)
@@ -1547,7 +1686,7 @@ int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
if (refs_resolve_ref_unsafe(refs, "HEAD", RESOLVE_REF_READING,
&oid, &flag))
- return fn("HEAD", &oid, flag, cb_data);
+ return fn("HEAD", NULL, &oid, flag, cb_data);
return 0;
}
@@ -1649,11 +1788,19 @@ int refs_for_each_namespaced_ref(struct ref_store *refs,
const char **exclude_patterns,
each_ref_fn fn, void *cb_data)
{
- struct strbuf buf = STRBUF_INIT;
+ struct strvec namespaced_exclude_patterns = STRVEC_INIT;
+ struct strbuf prefix = STRBUF_INIT;
int ret;
- strbuf_addf(&buf, "%srefs/", get_git_namespace());
- ret = do_for_each_ref(refs, buf.buf, exclude_patterns, fn, 0, 0, cb_data);
- strbuf_release(&buf);
+
+ exclude_patterns = get_namespaced_exclude_patterns(exclude_patterns,
+ get_git_namespace(),
+ &namespaced_exclude_patterns);
+
+ strbuf_addf(&prefix, "%srefs/", get_git_namespace());
+ ret = do_for_each_ref(refs, prefix.buf, exclude_patterns, fn, 0, 0, cb_data);
+
+ strvec_clear(&namespaced_exclude_patterns);
+ strbuf_release(&prefix);
return ret;
}
@@ -1734,6 +1881,7 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
const char **exclude_patterns,
each_ref_fn fn, void *cb_data)
{
+ struct strvec namespaced_exclude_patterns = STRVEC_INIT;
struct string_list prefixes = STRING_LIST_INIT_DUP;
struct string_list_item *prefix;
struct strbuf buf = STRBUF_INIT;
@@ -1745,6 +1893,10 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
strbuf_addstr(&buf, namespace);
namespace_len = buf.len;
+ exclude_patterns = get_namespaced_exclude_patterns(exclude_patterns,
+ namespace,
+ &namespaced_exclude_patterns);
+
for_each_string_list_item(prefix, &prefixes) {
strbuf_addstr(&buf, prefix->string);
ret = refs_for_each_fullref_in(ref_store, buf.buf,
@@ -1754,6 +1906,7 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store,
strbuf_setlen(&buf, namespace_len);
}
+ strvec_clear(&namespaced_exclude_patterns);
string_list_clear(&prefixes, 0);
strbuf_release(&buf);
return ret;
@@ -1774,8 +1927,8 @@ static int refs_read_special_head(struct ref_store *ref_store,
goto done;
}
- result = parse_loose_ref_contents(content.buf, oid, referent, type,
- failure_errno);
+ result = parse_loose_ref_contents(ref_store->repo->hash_algo, content.buf,
+ oid, referent, type, NULL, failure_errno);
done:
strbuf_release(&full_path);
@@ -1858,7 +2011,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
failure_errno != ENOTDIR)
return NULL;
- oidclr(oid, the_repository->hash_algo);
+ oidclr(oid, refs->repo->hash_algo);
if (*flags & REF_BAD_NAME)
*flags |= REF_ISBROKEN;
return refname;
@@ -1868,7 +2021,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
if (!(read_flags & REF_ISSYMREF)) {
if (*flags & REF_BAD_NAME) {
- oidclr(oid, the_repository->hash_algo);
+ oidclr(oid, refs->repo->hash_algo);
*flags |= REF_ISBROKEN;
}
return refname;
@@ -1876,7 +2029,7 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
refname = sb_refname.buf;
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
- oidclr(oid, the_repository->hash_algo);
+ oidclr(oid, refs->repo->hash_algo);
return refname;
}
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
@@ -2021,7 +2174,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
if (!is_nonbare_repository_dir(&submodule_sb))
goto done;
- if (submodule_to_gitdir(&submodule_sb, submodule))
+ if (submodule_to_gitdir(repo, &submodule_sb, submodule))
goto done;
subrepo = xmalloc(sizeof(*subrepo));
@@ -2031,7 +2184,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
free(subrepo);
goto done;
}
- refs = ref_store_init(subrepo, the_repository->ref_storage_format,
+ refs = ref_store_init(subrepo, subrepo->ref_storage_format,
submodule_sb.buf,
REF_STORE_READ | REF_STORE_ODB);
register_ref_store_map(&repo->submodule_ref_stores, "submodule",
@@ -2059,13 +2212,13 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
if (wt->id) {
struct strbuf common_path = STRBUF_INIT;
- strbuf_git_common_path(&common_path, wt->repo,
- "worktrees/%s", wt->id);
+ repo_common_path_append(wt->repo, &common_path,
+ "worktrees/%s", wt->id);
refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
common_path.buf, REF_STORE_ALL_CAPS);
strbuf_release(&common_path);
} else {
- refs = ref_store_init(wt->repo, the_repository->ref_storage_format,
+ refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
wt->repo->commondir, REF_STORE_ALL_CAPS);
}
@@ -2103,19 +2256,53 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
int refs_update_symref(struct ref_store *refs, const char *ref,
const char *target, const char *logmsg)
{
+ return refs_update_symref_extended(refs, ref, target, logmsg, NULL, 0);
+}
+
+int refs_update_symref_extended(struct ref_store *refs, const char *ref,
+ const char *target, const char *logmsg,
+ struct strbuf *referent, int create_only)
+{
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
- int ret = 0;
+ int ret = 0, prepret = 0;
- transaction = ref_store_transaction_begin(refs, &err);
- if (!transaction ||
- ref_transaction_update(transaction, ref, NULL, NULL,
- target, NULL, REF_NO_DEREF,
- logmsg, &err) ||
- ref_transaction_commit(transaction, &err)) {
+ transaction = ref_store_transaction_begin(refs, 0, &err);
+ if (!transaction) {
+ error_return:
ret = error("%s", err.buf);
+ goto cleanup;
}
+ if (create_only) {
+ if (ref_transaction_create(transaction, ref, NULL, target,
+ REF_NO_DEREF, logmsg, &err))
+ goto error_return;
+ prepret = ref_transaction_prepare(transaction, &err);
+ if (prepret && prepret != TRANSACTION_CREATE_EXISTS)
+ goto error_return;
+ } else {
+ if (ref_transaction_update(transaction, ref, NULL, NULL,
+ target, NULL, REF_NO_DEREF,
+ logmsg, &err) ||
+ ref_transaction_prepare(transaction, &err))
+ goto error_return;
+ }
+
+ if (referent && refs_read_symbolic_ref(refs, ref, referent) == NOT_A_SYMREF) {
+ struct object_id oid;
+ if (!refs_read_ref(refs, ref, &oid)) {
+ strbuf_addstr(referent, oid_to_hex(&oid));
+ ret = NOT_A_SYMREF;
+ }
+ }
+
+ if (prepret == TRANSACTION_CREATE_EXISTS)
+ goto cleanup;
+ if (ref_transaction_commit(transaction, &err))
+ goto error_return;
+
+cleanup:
strbuf_release(&err);
if (transaction)
ref_transaction_free(transaction);
@@ -2154,7 +2341,7 @@ static int run_transaction_hook(struct ref_transaction *transaction,
const char *hook;
int ret = 0, i;
- hook = find_hook("reference-transaction");
+ hook = find_hook(transaction->ref_store->repo, "reference-transaction");
if (!hook)
return ret;
@@ -2172,6 +2359,9 @@ static int run_transaction_hook(struct ref_transaction *transaction,
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
+ if (update->flags & REF_LOG_ONLY)
+ continue;
+
strbuf_reset(&buf);
if (!(update->flags & REF_HAVE_OLD))
@@ -2300,7 +2490,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
}
ret = refs->be->transaction_finish(refs, transaction, err);
- if (!ret)
+ if (!ret && !(transaction->flags & REF_TRANSACTION_FLAG_INITIAL))
run_transaction_hook(transaction, "committed");
return ret;
}
@@ -2309,6 +2499,7 @@ int refs_verify_refname_available(struct ref_store *refs,
const char *refname,
const struct string_list *extras,
const struct string_list *skip,
+ unsigned int initial_transaction,
struct strbuf *err)
{
const char *slash;
@@ -2317,8 +2508,6 @@ int refs_verify_refname_available(struct ref_store *refs,
struct strbuf referent = STRBUF_INIT;
struct object_id oid;
unsigned int type;
- struct ref_iterator *iter;
- int ok;
int ret = -1;
/*
@@ -2348,7 +2537,8 @@ int refs_verify_refname_available(struct ref_store *refs,
if (skip && string_list_has_string(skip, dirname.buf))
continue;
- if (!refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
+ if (!initial_transaction &&
+ !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
&type, &ignore_errno)) {
strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
dirname.buf, refname);
@@ -2373,21 +2563,26 @@ int refs_verify_refname_available(struct ref_store *refs,
strbuf_addstr(&dirname, refname + dirname.len);
strbuf_addch(&dirname, '/');
- iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
- DO_FOR_EACH_INCLUDE_BROKEN);
- while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- if (skip &&
- string_list_has_string(skip, iter->refname))
- continue;
+ if (!initial_transaction) {
+ struct ref_iterator *iter;
+ int ok;
- strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- iter->refname, refname);
- ref_iterator_abort(iter);
- goto cleanup;
- }
+ iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 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)
- BUG("error while iterating over references");
+ if (ok != ITER_DONE)
+ BUG("error while iterating over references");
+ }
extra_refname = find_descendant_ref(dirname.buf, extras, skip);
if (extra_refname)
@@ -2408,8 +2603,9 @@ struct do_for_each_reflog_help {
};
static int do_for_each_reflog_helper(const char *refname,
+ const char *referent UNUSED,
const struct object_id *oid UNUSED,
- int flags,
+ int flags UNUSED,
void *cb_data)
{
struct do_for_each_reflog_help *hp = cb_data;
@@ -2470,14 +2666,6 @@ int refs_reflog_expire(struct ref_store *refs,
cleanup_fn, policy_cb_data);
}
-int initial_ref_transaction_commit(struct ref_transaction *transaction,
- struct strbuf *err)
-{
- struct ref_store *refs = transaction->ref_store;
-
- return refs->be->initial_transaction_commit(refs, transaction, err);
-}
-
void ref_transaction_for_each_queued_update(struct ref_transaction *transaction,
ref_transaction_for_each_queued_update_fn cb,
void *cb_data)
@@ -2513,7 +2701,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg,
* individual updates can't fail, so we can pack all of the
* updates into a single transaction.
*/
- transaction = ref_store_transaction_begin(refs, &err);
+ transaction = ref_store_transaction_begin(refs, 0, &err);
if (!transaction) {
ret = error("%s", err.buf);
goto out;
@@ -2611,9 +2799,10 @@ struct migration_data {
struct ref_store *old_refs;
struct ref_transaction *transaction;
struct strbuf *errbuf;
+ struct strbuf sb;
};
-static int migrate_one_ref(const char *refname, const struct object_id *oid,
+static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
int flags, void *cb_data)
{
struct migration_data *data = cb_data;
@@ -2643,6 +2832,52 @@ done:
return ret;
}
+struct reflog_migration_data {
+ uint64_t index;
+ const char *refname;
+ struct ref_store *old_refs;
+ struct ref_transaction *transaction;
+ struct strbuf *errbuf;
+ struct strbuf *sb;
+};
+
+static int migrate_one_reflog_entry(struct object_id *old_oid,
+ struct object_id *new_oid,
+ const char *committer,
+ timestamp_t timestamp, int tz,
+ const char *msg, void *cb_data)
+{
+ struct reflog_migration_data *data = cb_data;
+ const char *date;
+ int ret;
+
+ date = show_date(timestamp, tz, DATE_MODE(NORMAL));
+ strbuf_reset(data->sb);
+ /* committer contains name and email */
+ strbuf_addstr(data->sb, fmt_ident("", committer, WANT_BLANK_IDENT, date, 0));
+
+ ret = ref_transaction_update_reflog(data->transaction, data->refname,
+ new_oid, old_oid, data->sb->buf,
+ REF_HAVE_NEW | REF_HAVE_OLD, msg,
+ data->index++, data->errbuf);
+ return ret;
+}
+
+static int migrate_one_reflog(const char *refname, void *cb_data)
+{
+ struct migration_data *migration_data = cb_data;
+ struct reflog_migration_data data = {
+ .refname = refname,
+ .old_refs = migration_data->old_refs,
+ .transaction = migration_data->transaction,
+ .errbuf = migration_data->errbuf,
+ .sb = &migration_data->sb,
+ };
+
+ return refs_for_each_reflog_ent(migration_data->old_refs, refname,
+ migrate_one_reflog_entry, &data);
+}
+
static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)
{
struct strbuf from_buf = STRBUF_INIT, to_buf = STRBUF_INIT;
@@ -2709,13 +2944,6 @@ done:
return ret;
}
-static int count_reflogs(const char *reflog UNUSED, void *payload)
-{
- size_t *reflog_count = payload;
- (*reflog_count)++;
- return 0;
-}
-
static int has_worktrees(void)
{
struct worktree **worktrees = get_worktrees();
@@ -2740,8 +2968,9 @@ int repo_migrate_ref_storage_format(struct repository *repo,
struct ref_store *old_refs = NULL, *new_refs = NULL;
struct ref_transaction *transaction = NULL;
struct strbuf new_gitdir = STRBUF_INIT;
- struct migration_data data;
- size_t reflog_count = 0;
+ struct migration_data data = {
+ .sb = STRBUF_INIT,
+ };
int did_migrate_refs = 0;
int ret;
@@ -2754,21 +2983,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
old_refs = get_main_ref_store(repo);
/*
- * We do not have any interfaces that would allow us to write many
- * reflog entries. Once we have them we can remove this restriction.
- */
- if (refs_for_each_reflog(old_refs, count_reflogs, &reflog_count) < 0) {
- strbuf_addstr(errbuf, "cannot count reflogs");
- ret = -1;
- goto done;
- }
- if (reflog_count) {
- strbuf_addstr(errbuf, "migrating reflogs is not supported yet");
- ret = -1;
- goto done;
- }
-
- /*
* Worktrees complicate the migration because every worktree has a
* separate ref storage. While it should be feasible to implement, this
* is pushed out to a future iteration.
@@ -2793,17 +3007,21 @@ int repo_migrate_ref_storage_format(struct repository *repo,
* This operation is safe as we do not yet modify the main
* repository.
*
- * 3. If we're in dry-run mode then we are done and can hand over the
+ * 3. Enumerate all reflogs and write them into the new ref storage.
+ * This operation is safe as we do not yet modify the main
+ * repository.
+ *
+ * 4. If we're in dry-run mode then we are done and can hand over the
* directory to the caller for inspection. If not, we now start
* with the destructive part.
*
- * 4. Delete the old ref storage from disk. As we have a copy of refs
+ * 5. Delete the old ref storage from disk. As we have a copy of refs
* in the new ref storage it's okay(ish) if we now get interrupted
* as there is an equivalent copy of all refs available.
*
- * 5. Move the new ref storage files into place.
+ * 6. Move the new ref storage files into place.
*
- * 6. Change the repository format to the new ref format.
+ * 7. Change the repository format to the new ref format.
*/
strbuf_addf(&new_gitdir, "%s/%s", old_refs->gitdir, "ref_migration.XXXXXX");
if (!mkdtemp(new_gitdir.buf)) {
@@ -2819,7 +3037,8 @@ int repo_migrate_ref_storage_format(struct repository *repo,
if (ret < 0)
goto done;
- transaction = ref_store_transaction_begin(new_refs, errbuf);
+ transaction = ref_store_transaction_begin(new_refs, REF_TRANSACTION_FLAG_INITIAL,
+ errbuf);
if (!transaction)
goto done;
@@ -2844,13 +3063,12 @@ int repo_migrate_ref_storage_format(struct repository *repo,
if (ret < 0)
goto done;
- /*
- * TODO: we might want to migrate to `initial_ref_transaction_commit()`
- * here, which is more efficient for the files backend because it would
- * write new refs into the packed-refs file directly. At this point,
- * the files backend doesn't handle pseudo-refs and symrefs correctly
- * though, so this requires some more work.
- */
+ if (!(flags & REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG)) {
+ ret = refs_for_each_reflog(old_refs, migrate_one_reflog, &data);
+ if (ret < 0)
+ goto done;
+ }
+
ret = ref_transaction_commit(transaction, errbuf);
if (ret < 0)
goto done;
@@ -2926,6 +3144,7 @@ done:
}
ref_transaction_free(transaction);
strbuf_release(&new_gitdir);
+ strbuf_release(&data.sb);
return ret;
}
@@ -2934,4 +3153,3 @@ int ref_update_expects_existing_old_ref(struct ref_update *update)
return (update->flags & REF_HAVE_OLD) &&
(!is_null_oid(&update->old_oid) || update->old_target);
}
-