summaryrefslogtreecommitdiffstats
path: root/refs
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2024-12-23 18:32:29 +0100
committerJunio C Hamano <gitster@pobox.com>2024-12-23 18:32:29 +0100
commit6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a (patch)
tree1f01eb96edc6c2349de60bcdf39db81d165ba83f /refs
parentMerge branch 'ma/asciidoctor-build-fixes' (diff)
parentrefs: mark invalid refname message for translation (diff)
downloadgit-6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a.tar.xz
git-6f8ae955bda8ad246cc1f5f7a15f1c3b1c04696a.zip
Merge branch 'kn/reflog-migration'
"git refs migrate" learned to also migrate the reflog data across backends. * kn/reflog-migration: refs: mark invalid refname message for translation refs: add support for migrating reflogs refs: allow multiple reflog entries for the same refname refs: introduce the `ref_transaction_update_reflog` function refs: add `committer_info` to `ref_transaction_add_update()` refs: extract out refname verification in transactions refs/files: add count field to ref_lock refs: add `index` field to `struct ref_udpate` refs: include committer info in `ref_update` struct
Diffstat (limited to 'refs')
-rw-r--r--refs/files-backend.c131
-rw-r--r--refs/refs-internal.h9
-rw-r--r--refs/reftable-backend.c53
3 files changed, 140 insertions, 53 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 467fe347fa..5cfb8b7ca8 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -72,6 +72,7 @@ struct ref_lock {
char *ref_name;
struct lock_file lk;
struct object_id old_oid;
+ unsigned int count; /* track users of the lock (ref update + reflog updates) */
};
struct files_ref_store {
@@ -638,9 +639,12 @@ int parse_loose_ref_contents(const struct git_hash_algo *algop,
static void unlock_ref(struct ref_lock *lock)
{
- rollback_lock_file(&lock->lk);
- free(lock->ref_name);
- free(lock);
+ lock->count--;
+ if (!lock->count) {
+ rollback_lock_file(&lock->lk);
+ free(lock->ref_name);
+ free(lock);
+ }
}
/*
@@ -696,6 +700,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
*lock_p = CALLOC_ARRAY(lock, 1);
lock->ref_name = xstrdup(refname);
+ lock->count = 1;
files_ref_path(refs, &ref_file, refname);
retry:
@@ -1169,6 +1174,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
goto error_return;
lock->ref_name = xstrdup(refname);
+ lock->count = 1;
if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
unable_to_lock_message(ref_file.buf, errno, err);
@@ -1264,7 +1270,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
ref_transaction_add_update(
transaction, r->name,
REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
- null_oid(), &r->oid, NULL, NULL, NULL);
+ null_oid(), &r->oid, NULL, NULL, NULL, NULL);
if (ref_transaction_commit(transaction, &err))
goto cleanup;
@@ -1858,6 +1864,9 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,
struct strbuf sb = STRBUF_INIT;
int ret = 0;
+ if (!committer)
+ committer = git_committer_info(0);
+
strbuf_addf(&sb, "%s %s %s", oid_to_hex(old_oid), oid_to_hex(new_oid), committer);
if (msg && *msg) {
strbuf_addch(&sb, '\t');
@@ -1871,8 +1880,10 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,
}
static int files_log_ref_write(struct files_ref_store *refs,
- const char *refname, const struct object_id *old_oid,
- const struct object_id *new_oid, const char *msg,
+ const char *refname,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ const char *committer_info, const char *msg,
int flags, struct strbuf *err)
{
int logfd, result;
@@ -1889,8 +1900,7 @@ static int files_log_ref_write(struct files_ref_store *refs,
if (logfd < 0)
return 0;
- result = log_ref_write_fd(logfd, old_oid, new_oid,
- git_committer_info(0), msg);
+ result = log_ref_write_fd(logfd, old_oid, new_oid, committer_info, msg);
if (result) {
struct strbuf sb = STRBUF_INIT;
int save_errno = errno;
@@ -1974,8 +1984,7 @@ static int commit_ref_update(struct files_ref_store *refs,
files_assert_main_repository(refs, "commit_ref_update");
clear_loose_ref_cache(refs);
- if (files_log_ref_write(refs, lock->ref_name,
- &lock->old_oid, oid,
+ if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid, oid, NULL,
logmsg, flags, err)) {
char *old_msg = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -2007,9 +2016,9 @@ static int commit_ref_update(struct files_ref_store *refs,
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name)) {
struct strbuf log_err = STRBUF_INIT;
- if (files_log_ref_write(refs, "HEAD",
- &lock->old_oid, oid,
- logmsg, flags, &log_err)) {
+ if (files_log_ref_write(refs, "HEAD", &lock->old_oid,
+ oid, NULL, logmsg, flags,
+ &log_err)) {
error("%s", log_err.buf);
strbuf_release(&log_err);
}
@@ -2408,7 +2417,7 @@ static int split_head_update(struct ref_update *update,
transaction, "HEAD",
update->flags | REF_LOG_ONLY | REF_NO_DEREF,
&update->new_oid, &update->old_oid,
- NULL, NULL, update->msg);
+ NULL, NULL, update->committer_info, update->msg);
/*
* Add "HEAD". This insertion is O(N) in the transaction
@@ -2472,7 +2481,8 @@ static int split_symref_update(struct ref_update *update,
transaction, referent, new_flags,
update->new_target ? NULL : &update->new_oid,
update->old_target ? NULL : &update->old_oid,
- update->new_target, update->old_target, update->msg);
+ update->new_target, update->old_target, NULL,
+ update->msg);
new_update->parent_update = update;
@@ -2536,6 +2546,12 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
return ret;
}
+struct files_transaction_backend_data {
+ struct ref_transaction *packed_transaction;
+ int packed_refs_locked;
+ struct strmap ref_locks;
+};
+
/*
* Prepare for carrying out update:
* - Lock the reference referred to by update.
@@ -2558,11 +2574,14 @@ static int lock_ref_for_update(struct files_ref_store *refs,
{
struct strbuf referent = STRBUF_INIT;
int mustexist = ref_update_expects_existing_old_ref(update);
+ struct files_transaction_backend_data *backend_data;
int ret = 0;
struct ref_lock *lock;
files_assert_main_repository(refs, "lock_ref_for_update");
+ backend_data = transaction->backend_data;
+
if ((update->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(update))
update->flags |= REF_DELETING;
@@ -2573,22 +2592,32 @@ static int lock_ref_for_update(struct files_ref_store *refs,
goto out;
}
- ret = lock_raw_ref(refs, update->refname, mustexist,
- affected_refnames,
- &lock, &referent,
- &update->type, err);
- if (ret) {
- char *reason;
+ lock = strmap_get(&backend_data->ref_locks, update->refname);
+ if (lock) {
+ lock->count++;
+ } else {
+ ret = lock_raw_ref(refs, update->refname, mustexist,
+ affected_refnames,
+ &lock, &referent,
+ &update->type, err);
+ if (ret) {
+ char *reason;
+
+ reason = strbuf_detach(err, NULL);
+ strbuf_addf(err, "cannot lock ref '%s': %s",
+ ref_update_original_update_refname(update), reason);
+ free(reason);
+ goto out;
+ }
- reason = strbuf_detach(err, NULL);
- strbuf_addf(err, "cannot lock ref '%s': %s",
- ref_update_original_update_refname(update), reason);
- free(reason);
- goto out;
+ strmap_put(&backend_data->ref_locks, update->refname, lock);
}
update->backend_data = lock;
+ if (update->flags & REF_LOG_ONLY)
+ goto out;
+
if (update->type & REF_ISSYMREF) {
if (update->flags & REF_NO_DEREF) {
/*
@@ -2735,11 +2764,6 @@ out:
return ret;
}
-struct files_transaction_backend_data {
- struct ref_transaction *packed_transaction;
- int packed_refs_locked;
-};
-
/*
* Unlock any references in `transaction` that are still locked, and
* mark the transaction closed.
@@ -2772,6 +2796,8 @@ static void files_transaction_cleanup(struct files_ref_store *refs,
if (backend_data->packed_refs_locked)
packed_refs_unlock(refs->packed_ref_store);
+ strmap_clear(&backend_data->ref_locks, 0);
+
free(backend_data);
}
@@ -2801,6 +2827,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
goto cleanup;
CALLOC_ARRAY(backend_data, 1);
+ strmap_init(&backend_data->ref_locks);
transaction->backend_data = backend_data;
/*
@@ -2813,13 +2840,16 @@ static int files_transaction_prepare(struct ref_store *ref_store,
*/
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
- struct string_list_item *item =
- string_list_append(&affected_refnames, update->refname);
+ struct string_list_item *item;
if ((update->flags & REF_IS_PRUNING) &&
!(update->flags & REF_NO_DEREF))
BUG("REF_IS_PRUNING set without REF_NO_DEREF");
+ if (update->flags & REF_LOG_ONLY)
+ continue;
+
+ item = string_list_append(&affected_refnames, update->refname);
/*
* We store a pointer to update in item->util, but at
* the moment we never use the value of this field
@@ -2899,7 +2929,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
packed_transaction, update->refname,
REF_HAVE_NEW | REF_NO_DEREF,
&update->new_oid, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
}
}
@@ -2977,7 +3007,8 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
}
if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
- &update->new_oid, update->msg, update->flags, err)) {
+ &update->new_oid, update->committer_info,
+ update->msg, update->flags, err)) {
char *old_msg = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -3018,8 +3049,9 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
/* Fail if a refname appears more than once in the transaction: */
for (i = 0; i < transaction->nr; i++)
- string_list_append(&affected_refnames,
- transaction->updates[i]->refname);
+ if (!(transaction->updates[i]->flags & REF_LOG_ONLY))
+ string_list_append(&affected_refnames,
+ transaction->updates[i]->refname);
string_list_sort(&affected_refnames);
if (ref_update_reject_duplicates(&affected_refnames, err)) {
ret = TRANSACTION_GENERIC_ERROR;
@@ -3063,10 +3095,12 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
}
/*
- * packed-refs don't support symbolic refs and root refs, so we
- * have to queue these references via the loose transaction.
+ * packed-refs don't support symbolic refs, root refs and reflogs,
+ * so we have to queue these references via the loose transaction.
*/
- if (update->new_target || is_root_ref(update->refname)) {
+ if (update->new_target ||
+ is_root_ref(update->refname) ||
+ (update->flags & REF_LOG_ONLY)) {
if (!loose_transaction) {
loose_transaction = ref_store_transaction_begin(&refs->base, 0, err);
if (!loose_transaction) {
@@ -3075,15 +3109,22 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
}
}
- ref_transaction_add_update(loose_transaction, update->refname,
- update->flags & ~REF_HAVE_OLD,
- update->new_target ? NULL : &update->new_oid, NULL,
- update->new_target, NULL, NULL);
+ if (update->flags & REF_LOG_ONLY)
+ ref_transaction_add_update(loose_transaction, update->refname,
+ update->flags, &update->new_oid,
+ &update->old_oid, NULL, NULL,
+ update->committer_info, update->msg);
+ else
+ ref_transaction_add_update(loose_transaction, update->refname,
+ update->flags & ~REF_HAVE_OLD,
+ update->new_target ? NULL : &update->new_oid, NULL,
+ update->new_target, NULL, update->committer_info,
+ NULL);
} else {
ref_transaction_add_update(packed_transaction, update->refname,
update->flags & ~REF_HAVE_OLD,
&update->new_oid, &update->old_oid,
- NULL, NULL, NULL);
+ NULL, NULL, update->committer_info, NULL);
}
}
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 66e66e0fc1..16550862d3 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -113,6 +113,14 @@ struct ref_update {
void *backend_data;
unsigned int type;
char *msg;
+ char *committer_info;
+
+ /*
+ * The index overrides the default sort algorithm. This is needed
+ * when migrating reflogs and we want to ensure we carry over the
+ * same order.
+ */
+ unsigned int index;
/*
* If this ref_update was split off of a symref update via
@@ -154,6 +162,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);
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 8a2a5b847c..00d95a9a2f 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1091,8 +1091,9 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
if (ret)
goto done;
- string_list_append(&affected_refnames,
- transaction->updates[i]->refname);
+ if (!(transaction->updates[i]->flags & REF_LOG_ONLY))
+ string_list_append(&affected_refnames,
+ transaction->updates[i]->refname);
}
/*
@@ -1202,7 +1203,8 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
new_update = ref_transaction_add_update(
transaction, "HEAD",
u->flags | REF_LOG_ONLY | REF_NO_DEREF,
- &u->new_oid, &u->old_oid, NULL, NULL, u->msg);
+ &u->new_oid, &u->old_oid, NULL, NULL, NULL,
+ u->msg);
string_list_insert(&affected_refnames, new_update->refname);
}
@@ -1285,7 +1287,8 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
transaction, referent.buf, new_flags,
u->new_target ? NULL : &u->new_oid,
u->old_target ? NULL : &u->old_oid,
- u->new_target, u->old_target, u->msg);
+ u->new_target, u->old_target,
+ u->committer_info, u->msg);
new_update->parent_update = u;
@@ -1405,8 +1408,17 @@ static int reftable_be_transaction_abort(struct ref_store *ref_store UNUSED,
static int transaction_update_cmp(const void *a, const void *b)
{
- return strcmp(((struct reftable_transaction_update *)a)->update->refname,
- ((struct reftable_transaction_update *)b)->update->refname);
+ struct reftable_transaction_update *update_a = (struct reftable_transaction_update *)a;
+ struct reftable_transaction_update *update_b = (struct reftable_transaction_update *)b;
+
+ /*
+ * If there is an index set, it should take preference (default is 0).
+ * This ensures that updates with indexes are sorted amongst themselves.
+ */
+ if (update_a->update->index || update_b->update->index)
+ return update_a->update->index - update_b->update->index;
+
+ return strcmp(update_a->update->refname, update_b->update->refname);
}
static int write_transaction_table(struct reftable_writer *writer, void *cb_data)
@@ -1416,6 +1428,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct reftable_log_record *logs = NULL;
struct ident_split committer_ident = {0};
size_t logs_nr = 0, logs_alloc = 0, i;
+ uint64_t max_update_index = ts;
const char *committer_info;
int ret = 0;
@@ -1505,12 +1518,34 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
}
if (create_reflog) {
+ struct ident_split c;
+
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
log = &logs[logs_nr++];
memset(log, 0, sizeof(*log));
- fill_reftable_log_record(log, &committer_ident);
- log->update_index = ts;
+ if (u->committer_info) {
+ if (split_ident_line(&c, u->committer_info,
+ strlen(u->committer_info)))
+ BUG("failed splitting committer info");
+ } else {
+ c = committer_ident;
+ }
+
+ fill_reftable_log_record(log, &c);
+
+ /*
+ * Updates are sorted by the writer. So updates for the same
+ * refname need to contain different update indices.
+ */
+ log->update_index = ts + u->index;
+
+ /*
+ * Note the max update_index so the limit can be set later on.
+ */
+ if (log->update_index > max_update_index)
+ max_update_index = log->update_index;
+
log->refname = xstrdup(u->refname);
memcpy(log->value.update.new_hash,
u->new_oid.hash, GIT_MAX_RAWSZ);
@@ -1574,6 +1609,8 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
* and log blocks.
*/
if (logs) {
+ reftable_writer_set_limits(writer, ts, max_update_index);
+
ret = reftable_writer_add_logs(writer, logs, logs_nr);
if (ret < 0)
goto done;