Merge branch 'ps/reflog-migrate-fixes'
"git refs migrate" to migrate the reflog entries from a refs backend to another had a handful of bugs squashed. * ps/reflog-migrate-fixes: refs: fix invalid old object IDs when migrating reflogs refs: stop unsetting REF_HAVE_OLD for log-only updates refs/files: detect race when generating reflog entry for HEAD refs: fix identity for migrated reflogs ident: fix type of string length parameter builtin/reflog: implement subcommand to write new entries refs: export `ref_transaction_update_reflog()` builtin/reflog: improve grouping of subcommands Documentation/git-reflog: convert to use synopsis type
This commit is contained in:
@@ -68,6 +68,12 @@
|
||||
*/
|
||||
#define REF_DELETED_RMDIR (1 << 9)
|
||||
|
||||
/*
|
||||
* Used to indicate that the reflog-only update has been created via
|
||||
* `split_head_update()`.
|
||||
*/
|
||||
#define REF_LOG_VIA_SPLIT (1 << 14)
|
||||
|
||||
struct ref_lock {
|
||||
char *ref_name;
|
||||
struct lock_file lk;
|
||||
@@ -2421,9 +2427,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
|
||||
|
||||
new_update = ref_transaction_add_update(
|
||||
transaction, "HEAD",
|
||||
update->flags | REF_LOG_ONLY | REF_NO_DEREF,
|
||||
update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT,
|
||||
&update->new_oid, &update->old_oid,
|
||||
NULL, NULL, update->committer_info, update->msg);
|
||||
new_update->parent_update = update;
|
||||
|
||||
/*
|
||||
* Add "HEAD". This insertion is O(N) in the transaction
|
||||
@@ -2494,7 +2501,6 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,
|
||||
* done when new_update is processed.
|
||||
*/
|
||||
update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
|
||||
update->flags &= ~REF_HAVE_OLD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2509,8 +2515,9 @@ static enum ref_transaction_error check_old_oid(struct ref_update *update,
|
||||
struct object_id *oid,
|
||||
struct strbuf *err)
|
||||
{
|
||||
if (!(update->flags & REF_HAVE_OLD) ||
|
||||
oideq(oid, &update->old_oid))
|
||||
if (update->flags & REF_LOG_ONLY ||
|
||||
!(update->flags & REF_HAVE_OLD) ||
|
||||
oideq(oid, &update->old_oid))
|
||||
return 0;
|
||||
|
||||
if (is_null_oid(&update->old_oid)) {
|
||||
@@ -2601,7 +2608,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
|
||||
|
||||
update->backend_data = lock;
|
||||
|
||||
if (update->type & REF_ISSYMREF) {
|
||||
if (update->flags & REF_LOG_VIA_SPLIT) {
|
||||
struct ref_lock *parent_lock;
|
||||
|
||||
if (!update->parent_update)
|
||||
BUG("split update without a parent");
|
||||
|
||||
parent_lock = update->parent_update->backend_data;
|
||||
|
||||
/*
|
||||
* Check that "HEAD" didn't racily change since we have looked
|
||||
* it up. If it did we must refuse to write the reflog entry.
|
||||
*
|
||||
* Note that this does not catch all races: if "HEAD" was
|
||||
* racily changed to point to one of the refs part of the
|
||||
* transaction then we would miss writing the split reflog
|
||||
* entry for "HEAD".
|
||||
*/
|
||||
if (!(update->type & REF_ISSYMREF) ||
|
||||
strcmp(update->parent_update->refname, referent.buf)) {
|
||||
strbuf_addstr(err, "HEAD has been racily updated");
|
||||
ret = REF_TRANSACTION_ERROR_GENERIC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (update->flags & REF_HAVE_OLD) {
|
||||
oidcpy(&lock->old_oid, &update->old_oid);
|
||||
} else {
|
||||
oidcpy(&lock->old_oid, &parent_lock->old_oid);
|
||||
}
|
||||
} else if (update->type & REF_ISSYMREF) {
|
||||
if (update->flags & REF_NO_DEREF) {
|
||||
/*
|
||||
* We won't be reading the referent as part of
|
||||
@@ -2977,6 +3013,20 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
|
||||
struct ref_lock *lock,
|
||||
struct strbuf *err)
|
||||
{
|
||||
struct object_id *old_oid = &lock->old_oid;
|
||||
|
||||
if (update->flags & REF_LOG_USE_PROVIDED_OIDS) {
|
||||
if (!(update->flags & REF_HAVE_OLD) ||
|
||||
!(update->flags & REF_HAVE_NEW) ||
|
||||
!(update->flags & REF_LOG_ONLY)) {
|
||||
strbuf_addf(err, _("trying to write reflog for '%s'"
|
||||
"with incomplete values"), update->refname);
|
||||
return REF_TRANSACTION_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
old_oid = &update->old_oid;
|
||||
}
|
||||
|
||||
if (update->new_target) {
|
||||
/*
|
||||
* We want to get the resolved OID for the target, to ensure
|
||||
@@ -2994,7 +3044,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
|
||||
}
|
||||
}
|
||||
|
||||
if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
|
||||
if (files_log_ref_write(refs, lock->ref_name, old_oid,
|
||||
&update->new_oid, update->committer_info,
|
||||
update->msg, update->flags, err)) {
|
||||
char *old_msg = strbuf_detach(err, NULL);
|
||||
@@ -3062,7 +3112,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
|
||||
for (i = 0; i < transaction->nr; i++) {
|
||||
struct ref_update *update = transaction->updates[i];
|
||||
|
||||
if ((update->flags & REF_HAVE_OLD) &&
|
||||
if (!(update->flags & REF_LOG_ONLY) &&
|
||||
(update->flags & REF_HAVE_OLD) &&
|
||||
!is_null_oid(&update->old_oid))
|
||||
BUG("initial ref transaction with old_sha1 set");
|
||||
|
||||
|
||||
@@ -662,7 +662,8 @@ enum ref_transaction_error ref_update_check_old_target(const char *referent,
|
||||
|
||||
/*
|
||||
* Check if the ref must exist, this means that the old_oid or
|
||||
* old_target is non NULL.
|
||||
* old_target is non NULL. Log-only updates never require the old state to
|
||||
* match.
|
||||
*/
|
||||
int ref_update_expects_existing_old_ref(struct ref_update *update);
|
||||
|
||||
|
||||
@@ -1102,6 +1102,20 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||
if (ret)
|
||||
return REF_TRANSACTION_ERROR_GENERIC;
|
||||
|
||||
if (u->flags & REF_LOG_USE_PROVIDED_OIDS) {
|
||||
if (!(u->flags & REF_HAVE_OLD) ||
|
||||
!(u->flags & REF_HAVE_NEW) ||
|
||||
!(u->flags & REF_LOG_ONLY)) {
|
||||
strbuf_addf(err, _("trying to write reflog for '%s'"
|
||||
"with incomplete values"), u->refname);
|
||||
return REF_TRANSACTION_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
if (queue_transaction_update(refs, tx_data, u, &u->old_oid, err))
|
||||
return REF_TRANSACTION_ERROR_GENERIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Verify that the new object ID is valid. */
|
||||
if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
|
||||
!(u->flags & REF_SKIP_OID_VERIFICATION) &&
|
||||
@@ -1186,8 +1200,6 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||
if (ret > 0) {
|
||||
/* The reference does not exist, but we expected it to. */
|
||||
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||
|
||||
|
||||
"unable to resolve reference '%s'"),
|
||||
ref_update_original_update_refname(u), u->refname);
|
||||
return REF_TRANSACTION_ERROR_NONEXISTENT_REF;
|
||||
@@ -1241,13 +1253,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||
|
||||
new_update->parent_update = u;
|
||||
|
||||
/*
|
||||
* Change the symbolic ref update to log only. Also, it
|
||||
* doesn't need to check its old OID value, as that will be
|
||||
* done when new_update is processed.
|
||||
*/
|
||||
/* Change the symbolic ref update to log only. */
|
||||
u->flags |= REF_LOG_ONLY | REF_NO_DEREF;
|
||||
u->flags &= ~REF_HAVE_OLD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1271,7 +1278,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
|
||||
ret = ref_update_check_old_target(referent->buf, u, err);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if ((u->flags & REF_HAVE_OLD) && !oideq(¤t_oid, &u->old_oid)) {
|
||||
} else if ((u->flags & (REF_LOG_ONLY | REF_HAVE_OLD)) == REF_HAVE_OLD &&
|
||||
!oideq(¤t_oid, &u->old_oid)) {
|
||||
if (is_null_oid(&u->old_oid)) {
|
||||
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||
"reference already exists"),
|
||||
|
||||
Reference in New Issue
Block a user