Merge branch 'kn/ref-transaction-symref'
Updates to symbolic refs can now be made as a part of ref transaction. * kn/ref-transaction-symref: refs: remove `create_symref` and associated dead code refs: rename `refs_create_symref()` to `refs_update_symref()` refs: use transaction in `refs_create_symref()` refs: add support for transactional symref updates refs: move `original_update_refname` to 'refs.c' refs: support symrefs in 'reference-transaction' hook files-backend: extract out `create_symref_lock()` refs: accept symref values in `ref_transaction_update()`maint
commit
4beb7a3b06
|
@ -486,7 +486,7 @@ reference-transaction
|
|||
This hook is invoked by any Git command that performs reference
|
||||
updates. It executes whenever a reference transaction is prepared,
|
||||
committed or aborted and may thus get called multiple times. The hook
|
||||
does not cover symbolic references (but that may change in the future).
|
||||
also supports symbolic reference updates.
|
||||
|
||||
The hook takes exactly one argument, which is the current state the
|
||||
given reference transaction is in:
|
||||
|
@ -503,16 +503,20 @@ given reference transaction is in:
|
|||
For each reference update that was added to the transaction, the hook
|
||||
receives on standard input a line of the format:
|
||||
|
||||
<old-oid> SP <new-oid> SP <ref-name> LF
|
||||
<old-value> SP <new-value> SP <ref-name> LF
|
||||
|
||||
where `<old-oid>` is the old object name passed into the reference
|
||||
transaction, `<new-oid>` is the new object name to be stored in the
|
||||
where `<old-value>` is the old object name passed into the reference
|
||||
transaction, `<new-value>` is the new object name to be stored in the
|
||||
ref and `<ref-name>` is the full name of the ref. When force updating
|
||||
the reference regardless of its current value or when the reference is
|
||||
to be created anew, `<old-oid>` is the all-zeroes object name. To
|
||||
to be created anew, `<old-value>` is the all-zeroes object name. To
|
||||
distinguish these cases, you can inspect the current value of
|
||||
`<ref-name>` via `git rev-parse`.
|
||||
|
||||
For symbolic reference updates the `<old_value>` and `<new-value>`
|
||||
fields could denote references instead of objects. A reference will be
|
||||
denoted with a 'ref:' prefix, like `ref:<ref-target>`.
|
||||
|
||||
The exit status of the hook is ignored for any state except for the
|
||||
"prepared" state. In the "prepared" state, a non-zero exit status will
|
||||
cause the transaction to be aborted. The hook will not be called with
|
||||
|
|
2
branch.c
2
branch.c
|
@ -628,7 +628,7 @@ void create_branch(struct repository *r,
|
|||
if (!transaction ||
|
||||
ref_transaction_update(transaction, ref.buf,
|
||||
&oid, forcing ? NULL : null_oid(),
|
||||
0, msg, &err) ||
|
||||
NULL, NULL, 0, msg, &err) ||
|
||||
ref_transaction_commit(transaction, &err))
|
||||
die("%s", err.buf);
|
||||
ref_transaction_free(transaction);
|
||||
|
|
|
@ -559,7 +559,7 @@ static int replace_each_worktree_head_symref(struct worktree **worktrees,
|
|||
continue;
|
||||
|
||||
refs = get_worktree_ref_store(worktrees[i]);
|
||||
if (refs_create_symref(refs, "HEAD", newref, logmsg))
|
||||
if (refs_update_symref(refs, "HEAD", newref, logmsg))
|
||||
ret = error(_("HEAD of working tree %s is not updated"),
|
||||
worktrees[i]->path);
|
||||
}
|
||||
|
|
|
@ -1011,7 +1011,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
|
|||
describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
|
||||
}
|
||||
} else if (new_branch_info->path) { /* Switch branches. */
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0)
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", new_branch_info->path, msg.buf) < 0)
|
||||
die(_("unable to update HEAD"));
|
||||
if (!opts->quiet) {
|
||||
if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
|
||||
|
@ -1472,7 +1472,7 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)
|
|||
if (!opts->new_branch)
|
||||
die(_("You are on a branch yet to be born"));
|
||||
strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
|
||||
status = refs_create_symref(get_main_ref_store(the_repository),
|
||||
status = refs_update_symref(get_main_ref_store(the_repository),
|
||||
"HEAD", branch_ref.buf, "checkout -b");
|
||||
strbuf_release(&branch_ref);
|
||||
if (!opts->quiet)
|
||||
|
|
|
@ -653,7 +653,7 @@ static void update_remote_refs(const struct ref *refs,
|
|||
struct strbuf head_ref = STRBUF_INIT;
|
||||
strbuf_addstr(&head_ref, branch_top);
|
||||
strbuf_addstr(&head_ref, "HEAD");
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), head_ref.buf,
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), head_ref.buf,
|
||||
remote_head_points_at->peer_ref->name,
|
||||
msg) < 0)
|
||||
die(_("unable to update %s"), head_ref.buf);
|
||||
|
@ -667,7 +667,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
|
|||
const char *head;
|
||||
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
|
||||
/* Local default branch link */
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0)
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", our->name, NULL) < 0)
|
||||
die(_("unable to update HEAD"));
|
||||
if (!option_bare) {
|
||||
refs_update_ref(get_main_ref_store(the_repository),
|
||||
|
@ -696,7 +696,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
|
|||
* Unborn head from remote; same as "our" case above except
|
||||
* that we have no ref to update.
|
||||
*/
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0)
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", unborn, NULL) < 0)
|
||||
die(_("unable to update HEAD"));
|
||||
if (!option_bare)
|
||||
install_branch_config(0, head, remote_name, unborn);
|
||||
|
|
|
@ -1636,7 +1636,7 @@ static int update_branch(struct branch *b)
|
|||
&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, b->name, &b->oid, &old_oid,
|
||||
0, msg, &err) ||
|
||||
NULL, NULL, 0, msg, &err) ||
|
||||
ref_transaction_commit(transaction, &err)) {
|
||||
ref_transaction_free(transaction);
|
||||
error("%s", err.buf);
|
||||
|
@ -1678,7 +1678,8 @@ static void dump_tags(void)
|
|||
strbuf_addf(&ref_name, "refs/tags/%s", t->name);
|
||||
|
||||
if (ref_transaction_update(transaction, ref_name.buf,
|
||||
&t->oid, NULL, 0, msg, &err)) {
|
||||
&t->oid, NULL, NULL, NULL,
|
||||
0, msg, &err)) {
|
||||
failure |= error("%s", err.buf);
|
||||
goto cleanup;
|
||||
}
|
||||
|
|
|
@ -672,7 +672,7 @@ static int s_update_ref(const char *action,
|
|||
|
||||
ret = ref_transaction_update(transaction, ref->name, &ref->new_oid,
|
||||
check_old ? &ref->old_oid : NULL,
|
||||
0, msg, &err);
|
||||
NULL, NULL, 0, msg, &err);
|
||||
if (ret) {
|
||||
ret = STORE_REF_ERROR_OTHER;
|
||||
goto out;
|
||||
|
|
|
@ -981,7 +981,7 @@ static int merge(int argc, const char **argv, const char *prefix)
|
|||
die(_("a notes merge into %s is already in-progress at %s"),
|
||||
default_notes_ref(), wt->path);
|
||||
free_worktrees(worktrees);
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", default_notes_ref(), NULL))
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", default_notes_ref(), NULL))
|
||||
die(_("failed to store link to current notes ref (%s)"),
|
||||
default_notes_ref());
|
||||
fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
|
||||
|
|
|
@ -1595,6 +1595,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
|
|||
if (ref_transaction_update(transaction,
|
||||
namespaced_name,
|
||||
new_oid, old_oid,
|
||||
NULL, NULL,
|
||||
0, "push",
|
||||
&err)) {
|
||||
rp_error("%s", err.buf);
|
||||
|
|
|
@ -240,7 +240,7 @@ static int add(int argc, const char **argv, const char *prefix)
|
|||
strbuf_reset(&buf2);
|
||||
strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
|
||||
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote add"))
|
||||
return error(_("Could not setup master '%s'"), master);
|
||||
}
|
||||
|
||||
|
@ -845,7 +845,7 @@ static int mv(int argc, const char **argv, const char *prefix)
|
|||
strbuf_reset(&buf3);
|
||||
strbuf_addf(&buf3, "remote: renamed %s to %s",
|
||||
item->string, buf.buf);
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
|
||||
die(_("creating '%s' failed"), buf.buf);
|
||||
display_progress(progress, ++refs_renamed_nr);
|
||||
}
|
||||
|
@ -1423,7 +1423,7 @@ static int set_head(int argc, const char **argv, const char *prefix)
|
|||
/* make sure it's valid */
|
||||
if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
|
||||
result |= error(_("Not a valid ref: %s"), buf2.buf);
|
||||
else if (refs_create_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
|
||||
else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
|
||||
result |= error(_("Could not setup %s"), buf.buf);
|
||||
else if (opt_a)
|
||||
printf("%s/HEAD set to %s\n", argv[0], head_name);
|
||||
|
|
|
@ -202,7 +202,7 @@ static int replace_object_oid(const char *object_ref,
|
|||
&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, ref.buf, repl, &prev,
|
||||
0, NULL, &err) ||
|
||||
NULL, NULL, 0, NULL, &err) ||
|
||||
ref_transaction_commit(transaction, &err))
|
||||
res = error("%s", err.buf);
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
|
|||
die("Refusing to point HEAD outside of refs/");
|
||||
if (check_refname_format(argv[1], REFNAME_ALLOW_ONELEVEL) < 0)
|
||||
die("Refusing to set '%s' to invalid ref '%s'", argv[0], argv[1]);
|
||||
ret = !!refs_create_symref(get_main_ref_store(the_repository),
|
||||
ret = !!refs_update_symref(get_main_ref_store(the_repository),
|
||||
argv[0], argv[1], msg);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -680,6 +680,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
|||
&err);
|
||||
if (!transaction ||
|
||||
ref_transaction_update(transaction, ref.buf, &object, &prev,
|
||||
NULL, NULL,
|
||||
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
|
||||
reflog_msg.buf, &err) ||
|
||||
ref_transaction_commit(transaction, &err)) {
|
||||
|
|
|
@ -204,6 +204,7 @@ static void parse_cmd_update(struct ref_transaction *transaction,
|
|||
|
||||
if (ref_transaction_update(transaction, refname,
|
||||
&new_oid, have_old ? &old_oid : NULL,
|
||||
NULL, NULL,
|
||||
update_flags | create_reflog_flag,
|
||||
msg, &err))
|
||||
die("%s", err.buf);
|
||||
|
|
|
@ -517,7 +517,7 @@ static int add_worktree(const char *path, const char *refname,
|
|||
ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
|
||||
NULL, 0, UPDATE_REFS_MSG_ON_ERR);
|
||||
else
|
||||
ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
|
||||
ret = refs_update_symref(wt_refs, "HEAD", symref.buf, NULL);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
|
|
117
refs.c
117
refs.c
|
@ -1180,6 +1180,8 @@ void ref_transaction_free(struct ref_transaction *transaction)
|
|||
|
||||
for (i = 0; i < transaction->nr; i++) {
|
||||
free(transaction->updates[i]->msg);
|
||||
free((char *)transaction->updates[i]->new_target);
|
||||
free((char *)transaction->updates[i]->old_target);
|
||||
free(transaction->updates[i]);
|
||||
}
|
||||
free(transaction->updates);
|
||||
|
@ -1191,6 +1193,7 @@ struct ref_update *ref_transaction_add_update(
|
|||
const char *refname, unsigned int flags,
|
||||
const struct object_id *new_oid,
|
||||
const struct object_id *old_oid,
|
||||
const char *new_target, const char *old_target,
|
||||
const char *msg)
|
||||
{
|
||||
struct ref_update *update;
|
||||
|
@ -1198,16 +1201,24 @@ struct ref_update *ref_transaction_add_update(
|
|||
if (transaction->state != REF_TRANSACTION_OPEN)
|
||||
BUG("update called for transaction that is not open");
|
||||
|
||||
if (old_oid && old_target)
|
||||
BUG("only one of old_oid and old_target should be non NULL");
|
||||
if (new_oid && new_target)
|
||||
BUG("only one of new_oid and new_target should be non NULL");
|
||||
|
||||
FLEX_ALLOC_STR(update, refname, refname);
|
||||
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
|
||||
transaction->updates[transaction->nr++] = update;
|
||||
|
||||
update->flags = flags;
|
||||
|
||||
if (flags & REF_HAVE_NEW)
|
||||
update->new_target = xstrdup_or_null(new_target);
|
||||
update->old_target = xstrdup_or_null(old_target);
|
||||
if ((flags & REF_HAVE_NEW) && new_oid)
|
||||
oidcpy(&update->new_oid, new_oid);
|
||||
if (flags & REF_HAVE_OLD)
|
||||
if ((flags & REF_HAVE_OLD) && old_oid)
|
||||
oidcpy(&update->old_oid, old_oid);
|
||||
|
||||
update->msg = normalize_reflog_message(msg);
|
||||
return update;
|
||||
}
|
||||
|
@ -1216,6 +1227,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
|||
const char *refname,
|
||||
const struct object_id *new_oid,
|
||||
const struct object_id *old_oid,
|
||||
const char *new_target,
|
||||
const char *old_target,
|
||||
unsigned int flags, const char *msg,
|
||||
struct strbuf *err)
|
||||
{
|
||||
|
@ -1241,9 +1254,11 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
|||
flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
|
||||
|
||||
flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0);
|
||||
flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0);
|
||||
|
||||
ref_transaction_add_update(transaction, refname, flags,
|
||||
new_oid, old_oid, msg);
|
||||
new_oid, old_oid, new_target,
|
||||
old_target, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1258,7 +1273,8 @@ int ref_transaction_create(struct ref_transaction *transaction,
|
|||
return 1;
|
||||
}
|
||||
return ref_transaction_update(transaction, refname, new_oid,
|
||||
null_oid(), flags, msg, err);
|
||||
null_oid(), NULL, NULL, flags,
|
||||
msg, err);
|
||||
}
|
||||
|
||||
int ref_transaction_delete(struct ref_transaction *transaction,
|
||||
|
@ -1271,7 +1287,8 @@ int ref_transaction_delete(struct ref_transaction *transaction,
|
|||
BUG("delete called with old_oid set to zeros");
|
||||
return ref_transaction_update(transaction, refname,
|
||||
null_oid(), old_oid,
|
||||
flags, msg, err);
|
||||
NULL, NULL, flags,
|
||||
msg, err);
|
||||
}
|
||||
|
||||
int ref_transaction_verify(struct ref_transaction *transaction,
|
||||
|
@ -1284,6 +1301,7 @@ int ref_transaction_verify(struct ref_transaction *transaction,
|
|||
BUG("verify called with old_oid set to NULL");
|
||||
return ref_transaction_update(transaction, refname,
|
||||
NULL, old_oid,
|
||||
NULL, NULL,
|
||||
flags, NULL, err);
|
||||
}
|
||||
|
||||
|
@ -1298,8 +1316,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
|
|||
|
||||
t = ref_store_transaction_begin(refs, &err);
|
||||
if (!t ||
|
||||
ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
|
||||
&err) ||
|
||||
ref_transaction_update(t, refname, new_oid, old_oid, NULL, NULL,
|
||||
flags, msg, &err) ||
|
||||
ref_transaction_commit(t, &err)) {
|
||||
ret = 1;
|
||||
ref_transaction_free(t);
|
||||
|
@ -2181,19 +2199,27 @@ int peel_iterated_oid(const struct object_id *base, struct object_id *peeled)
|
|||
return peel_object(base, peeled) ? -1 : 0;
|
||||
}
|
||||
|
||||
int refs_create_symref(struct ref_store *refs,
|
||||
const char *ref_target,
|
||||
const char *refs_heads_master,
|
||||
const char *logmsg)
|
||||
int refs_update_symref(struct ref_store *refs, const char *ref,
|
||||
const char *target, const char *logmsg)
|
||||
{
|
||||
char *msg;
|
||||
int retval;
|
||||
struct ref_transaction *transaction;
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
int ret = 0;
|
||||
|
||||
msg = normalize_reflog_message(logmsg);
|
||||
retval = refs->be->create_symref(refs, ref_target, refs_heads_master,
|
||||
msg);
|
||||
free(msg);
|
||||
return retval;
|
||||
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)) {
|
||||
ret = error("%s", err.buf);
|
||||
}
|
||||
|
||||
strbuf_release(&err);
|
||||
if (transaction)
|
||||
ref_transaction_free(transaction);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ref_update_reject_duplicates(struct string_list *refnames,
|
||||
|
@ -2246,10 +2272,22 @@ static int run_transaction_hook(struct ref_transaction *transaction,
|
|||
struct ref_update *update = transaction->updates[i];
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "%s %s %s\n",
|
||||
oid_to_hex(&update->old_oid),
|
||||
oid_to_hex(&update->new_oid),
|
||||
update->refname);
|
||||
|
||||
if (!(update->flags & REF_HAVE_OLD))
|
||||
strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
|
||||
else if (update->old_target)
|
||||
strbuf_addf(&buf, "ref:%s ", update->old_target);
|
||||
else
|
||||
strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid));
|
||||
|
||||
if (!(update->flags & REF_HAVE_NEW))
|
||||
strbuf_addf(&buf, "%s ", oid_to_hex(null_oid()));
|
||||
else if (update->new_target)
|
||||
strbuf_addf(&buf, "ref:%s ", update->new_target);
|
||||
else
|
||||
strbuf_addf(&buf, "%s ", oid_to_hex(&update->new_oid));
|
||||
|
||||
strbuf_addf(&buf, "%s\n", update->refname);
|
||||
|
||||
if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
|
||||
if (errno != EPIPE) {
|
||||
|
@ -2634,3 +2672,38 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
|
|||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const char *ref_update_original_update_refname(struct ref_update *update)
|
||||
{
|
||||
while (update->parent_update)
|
||||
update = update->parent_update;
|
||||
|
||||
return update->refname;
|
||||
}
|
||||
|
||||
int ref_update_has_null_new_value(struct ref_update *update)
|
||||
{
|
||||
return !update->new_target && is_null_oid(&update->new_oid);
|
||||
}
|
||||
|
||||
int ref_update_check_old_target(const char *referent, struct ref_update *update,
|
||||
struct strbuf *err)
|
||||
{
|
||||
if (!update->old_target)
|
||||
BUG("called without old_target set");
|
||||
|
||||
if (!strcmp(referent, update->old_target))
|
||||
return 0;
|
||||
|
||||
if (!strcmp(referent, ""))
|
||||
strbuf_addf(err, "verifying symref target: '%s': "
|
||||
"reference is missing but expected %s",
|
||||
ref_update_original_update_refname(update),
|
||||
update->old_target);
|
||||
else
|
||||
strbuf_addf(err, "verifying symref target: '%s': "
|
||||
"is at %s but expected %s",
|
||||
ref_update_original_update_refname(update),
|
||||
referent, update->old_target);
|
||||
return -1;
|
||||
}
|
||||
|
|
20
refs.h
20
refs.h
|
@ -563,7 +563,7 @@ int refs_rename_ref(struct ref_store *refs, const char *oldref,
|
|||
int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
|
||||
const char *newref, const char *logmsg);
|
||||
|
||||
int refs_create_symref(struct ref_store *refs, const char *refname,
|
||||
int refs_update_symref(struct ref_store *refs, const char *refname,
|
||||
const char *target, const char *logmsg);
|
||||
|
||||
enum action_on_err {
|
||||
|
@ -603,6 +603,16 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
|
|||
* before the update. A copy of this value is made in the
|
||||
* transaction.
|
||||
*
|
||||
* new_target -- the target reference that the reference will be
|
||||
* updated to point to. If the reference is a regular reference,
|
||||
* it will be converted to a symbolic reference. Cannot be set
|
||||
* together with `new_oid`. A copy of this value is made in the
|
||||
* transaction.
|
||||
*
|
||||
* old_target -- the reference that the reference must be pointing to.
|
||||
* Canont be set together with `old_oid`. A copy of this value is
|
||||
* made in the transaction.
|
||||
*
|
||||
* flags -- flags affecting the update, passed to
|
||||
* update_ref_lock(). Possible flags: REF_NO_DEREF,
|
||||
* REF_FORCE_CREATE_REFLOG. See those constants for more
|
||||
|
@ -668,7 +678,11 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
|
|||
* beforehand. The old value is checked after the lock is taken to
|
||||
* prevent races. If the old value doesn't agree with old_oid, the
|
||||
* whole transaction fails. If old_oid is NULL, then the previous
|
||||
* value is not checked.
|
||||
* value is not checked. If `old_target` is not NULL, treat the reference
|
||||
* as a symbolic ref and validate that its target before the update is
|
||||
* `old_target`. If the `new_target` is not NULL, then the reference
|
||||
* will be updated to a symbolic ref which targets `new_target`.
|
||||
* Together, these allow us to update between regular refs and symrefs.
|
||||
*
|
||||
* See the above comment "Reference transaction updates" for more
|
||||
* information.
|
||||
|
@ -677,6 +691,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
|
|||
const char *refname,
|
||||
const struct object_id *new_oid,
|
||||
const struct object_id *old_oid,
|
||||
const char *new_target,
|
||||
const char *old_target,
|
||||
unsigned int flags, const char *msg,
|
||||
struct strbuf *err);
|
||||
|
||||
|
|
13
refs/debug.c
13
refs/debug.c
|
@ -131,18 +131,6 @@ static int debug_pack_refs(struct ref_store *ref_store, struct pack_refs_opts *o
|
|||
return res;
|
||||
}
|
||||
|
||||
static int debug_create_symref(struct ref_store *ref_store,
|
||||
const char *ref_name, const char *target,
|
||||
const char *logmsg)
|
||||
{
|
||||
struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
|
||||
int res = drefs->refs->be->create_symref(drefs->refs, ref_name, target,
|
||||
logmsg);
|
||||
trace_printf_key(&trace_refs, "create_symref: %s -> %s \"%s\": %d\n", ref_name,
|
||||
target, logmsg, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int debug_rename_ref(struct ref_store *ref_store, const char *oldref,
|
||||
const char *newref, const char *logmsg)
|
||||
{
|
||||
|
@ -441,7 +429,6 @@ struct ref_storage_be refs_be_debug = {
|
|||
.initial_transaction_commit = debug_initial_transaction_commit,
|
||||
|
||||
.pack_refs = debug_pack_refs,
|
||||
.create_symref = debug_create_symref,
|
||||
.rename_ref = debug_rename_ref,
|
||||
.copy_ref = debug_copy_ref,
|
||||
|
||||
|
|
|
@ -1198,7 +1198,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_oid(), &r->oid, NULL, NULL, NULL);
|
||||
if (ref_transaction_commit(transaction, &err))
|
||||
goto cleanup;
|
||||
|
||||
|
@ -1292,7 +1292,7 @@ static int files_pack_refs(struct ref_store *ref_store,
|
|||
* packed-refs transaction:
|
||||
*/
|
||||
if (ref_transaction_update(transaction, iter->refname,
|
||||
iter->oid, NULL,
|
||||
iter->oid, NULL, NULL, NULL,
|
||||
REF_NO_DEREF, NULL, &err))
|
||||
die("failure preparing to create packed reference %s: %s",
|
||||
iter->refname, err.buf);
|
||||
|
@ -1903,66 +1903,23 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void update_symref_reflog(struct files_ref_store *refs,
|
||||
struct ref_lock *lock, const char *refname,
|
||||
const char *target, const char *logmsg)
|
||||
static int create_symref_lock(struct files_ref_store *refs,
|
||||
struct ref_lock *lock, const char *refname,
|
||||
const char *target, struct strbuf *err)
|
||||
{
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
struct object_id new_oid;
|
||||
|
||||
if (logmsg &&
|
||||
refs_resolve_ref_unsafe(&refs->base, target,
|
||||
RESOLVE_REF_READING, &new_oid, NULL) &&
|
||||
files_log_ref_write(refs, refname, &lock->old_oid,
|
||||
&new_oid, logmsg, 0, &err)) {
|
||||
error("%s", err.buf);
|
||||
strbuf_release(&err);
|
||||
}
|
||||
}
|
||||
|
||||
static int create_symref_locked(struct files_ref_store *refs,
|
||||
struct ref_lock *lock, const char *refname,
|
||||
const char *target, const char *logmsg)
|
||||
{
|
||||
if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
|
||||
update_symref_reflog(refs, lock, refname, target, logmsg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!fdopen_lock_file(&lock->lk, "w"))
|
||||
return error("unable to fdopen %s: %s",
|
||||
if (!fdopen_lock_file(&lock->lk, "w")) {
|
||||
strbuf_addf(err, "unable to fdopen %s: %s",
|
||||
get_lock_file_path(&lock->lk), strerror(errno));
|
||||
|
||||
update_symref_reflog(refs, lock, refname, target, logmsg);
|
||||
|
||||
/* no error check; commit_ref will check ferror */
|
||||
fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target);
|
||||
if (commit_ref(lock) < 0)
|
||||
return error("unable to write symref for %s: %s", refname,
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int files_create_symref(struct ref_store *ref_store,
|
||||
const char *refname, const char *target,
|
||||
const char *logmsg)
|
||||
{
|
||||
struct files_ref_store *refs =
|
||||
files_downcast(ref_store, REF_STORE_WRITE, "create_symref");
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
struct ref_lock *lock;
|
||||
int ret;
|
||||
|
||||
lock = lock_ref_oid_basic(refs, refname, &err);
|
||||
if (!lock) {
|
||||
error("%s", err.buf);
|
||||
strbuf_release(&err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = create_symref_locked(refs, lock, refname, target, logmsg);
|
||||
unlock_ref(lock);
|
||||
return ret;
|
||||
if (fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target) < 0) {
|
||||
strbuf_addf(err, "unable to write to %s: %s",
|
||||
get_lock_file_path(&lock->lk), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int files_reflog_exists(struct ref_store *ref_store,
|
||||
|
@ -2309,7 +2266,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,
|
||||
update->msg);
|
||||
NULL, NULL, update->msg);
|
||||
|
||||
/*
|
||||
* Add "HEAD". This insertion is O(N) in the transaction
|
||||
|
@ -2371,8 +2328,9 @@ static int split_symref_update(struct ref_update *update,
|
|||
|
||||
new_update = ref_transaction_add_update(
|
||||
transaction, referent, new_flags,
|
||||
&update->new_oid, &update->old_oid,
|
||||
update->msg);
|
||||
update->new_target ? NULL : &update->new_oid,
|
||||
update->old_target ? NULL : &update->old_oid,
|
||||
update->new_target, update->old_target, update->msg);
|
||||
|
||||
new_update->parent_update = update;
|
||||
|
||||
|
@ -2400,17 +2358,6 @@ static int split_symref_update(struct ref_update *update,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the refname under which update was originally requested.
|
||||
*/
|
||||
static const char *original_update_refname(struct ref_update *update)
|
||||
{
|
||||
while (update->parent_update)
|
||||
update = update->parent_update;
|
||||
|
||||
return update->refname;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the REF_HAVE_OLD and old_oid values stored in update
|
||||
* are consistent with oid, which is the reference's current value. If
|
||||
|
@ -2427,16 +2374,16 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
|
|||
if (is_null_oid(&update->old_oid))
|
||||
strbuf_addf(err, "cannot lock ref '%s': "
|
||||
"reference already exists",
|
||||
original_update_refname(update));
|
||||
ref_update_original_update_refname(update));
|
||||
else if (is_null_oid(oid))
|
||||
strbuf_addf(err, "cannot lock ref '%s': "
|
||||
"reference is missing but expected %s",
|
||||
original_update_refname(update),
|
||||
ref_update_original_update_refname(update),
|
||||
oid_to_hex(&update->old_oid));
|
||||
else
|
||||
strbuf_addf(err, "cannot lock ref '%s': "
|
||||
"is at %s but expected %s",
|
||||
original_update_refname(update),
|
||||
ref_update_original_update_refname(update),
|
||||
oid_to_hex(oid),
|
||||
oid_to_hex(&update->old_oid));
|
||||
|
||||
|
@ -2471,7 +2418,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
|
|||
|
||||
files_assert_main_repository(refs, "lock_ref_for_update");
|
||||
|
||||
if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
|
||||
if ((update->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(update))
|
||||
update->flags |= REF_DELETING;
|
||||
|
||||
if (head_ref) {
|
||||
|
@ -2490,7 +2437,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
|
|||
|
||||
reason = strbuf_detach(err, NULL);
|
||||
strbuf_addf(err, "cannot lock ref '%s': %s",
|
||||
original_update_refname(update), reason);
|
||||
ref_update_original_update_refname(update), reason);
|
||||
free(reason);
|
||||
goto out;
|
||||
}
|
||||
|
@ -2510,11 +2457,18 @@ static int lock_ref_for_update(struct files_ref_store *refs,
|
|||
if (update->flags & REF_HAVE_OLD) {
|
||||
strbuf_addf(err, "cannot lock ref '%s': "
|
||||
"error reading reference",
|
||||
original_update_refname(update));
|
||||
ref_update_original_update_refname(update));
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto out;
|
||||
}
|
||||
} else if (check_old_oid(update, &lock->old_oid, err)) {
|
||||
}
|
||||
|
||||
if (update->old_target) {
|
||||
if (ref_update_check_old_target(referent.buf, update, err)) {
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto out;
|
||||
}
|
||||
} else if (check_old_oid(update, &lock->old_oid, err)) {
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2535,7 +2489,17 @@ static int lock_ref_for_update(struct files_ref_store *refs,
|
|||
} else {
|
||||
struct ref_update *parent_update;
|
||||
|
||||
if (check_old_oid(update, &lock->old_oid, err)) {
|
||||
/*
|
||||
* Even if the ref is a regular ref, if `old_target` is set, we
|
||||
* check the referent value. Ideally `old_target` should only
|
||||
* be set for symrefs, but we're strict about its usage.
|
||||
*/
|
||||
if (update->old_target) {
|
||||
if (ref_update_check_old_target(referent.buf, update, err)) {
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto out;
|
||||
}
|
||||
} else if (check_old_oid(update, &lock->old_oid, err)) {
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2553,9 +2517,28 @@ static int lock_ref_for_update(struct files_ref_store *refs,
|
|||
}
|
||||
}
|
||||
|
||||
if ((update->flags & REF_HAVE_NEW) &&
|
||||
!(update->flags & REF_DELETING) &&
|
||||
!(update->flags & REF_LOG_ONLY)) {
|
||||
if (update->new_target && !(update->flags & REF_LOG_ONLY)) {
|
||||
if (create_symref_lock(refs, lock, update->refname,
|
||||
update->new_target, err)) {
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (close_ref_gently(lock)) {
|
||||
strbuf_addf(err, "couldn't close '%s.lock'",
|
||||
update->refname);
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once we have created the symref lock, the commit
|
||||
* phase of the transaction only needs to commit the lock.
|
||||
*/
|
||||
update->flags |= REF_NEEDS_COMMIT;
|
||||
} else if ((update->flags & REF_HAVE_NEW) &&
|
||||
!(update->flags & REF_DELETING) &&
|
||||
!(update->flags & REF_LOG_ONLY)) {
|
||||
if (!(update->type & REF_ISSYMREF) &&
|
||||
oideq(&lock->old_oid, &update->new_oid)) {
|
||||
/*
|
||||
|
@ -2763,7 +2746,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2818,6 +2801,43 @@ cleanup:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int parse_and_write_reflog(struct files_ref_store *refs,
|
||||
struct ref_update *update,
|
||||
struct ref_lock *lock,
|
||||
struct strbuf *err)
|
||||
{
|
||||
if (update->new_target) {
|
||||
/*
|
||||
* We want to get the resolved OID for the target, to ensure
|
||||
* that the correct value is added to the reflog.
|
||||
*/
|
||||
if (!refs_resolve_ref_unsafe(&refs->base, update->new_target,
|
||||
RESOLVE_REF_READING,
|
||||
&update->new_oid, NULL)) {
|
||||
/*
|
||||
* TODO: currently we skip creating reflogs for dangling
|
||||
* symref updates. It would be nice to capture this as
|
||||
* zero oid updates however.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
|
||||
&update->new_oid, update->msg, update->flags, err)) {
|
||||
char *old_msg = strbuf_detach(err, NULL);
|
||||
|
||||
strbuf_addf(err, "cannot update the ref '%s': %s",
|
||||
lock->ref_name, old_msg);
|
||||
free(old_msg);
|
||||
unlock_ref(lock);
|
||||
update->backend_data = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int files_transaction_finish(struct ref_store *ref_store,
|
||||
struct ref_transaction *transaction,
|
||||
struct strbuf *err)
|
||||
|
@ -2848,23 +2868,20 @@ static int files_transaction_finish(struct ref_store *ref_store,
|
|||
|
||||
if (update->flags & REF_NEEDS_COMMIT ||
|
||||
update->flags & REF_LOG_ONLY) {
|
||||
if (files_log_ref_write(refs,
|
||||
lock->ref_name,
|
||||
&lock->old_oid,
|
||||
&update->new_oid,
|
||||
update->msg, update->flags,
|
||||
err)) {
|
||||
char *old_msg = strbuf_detach(err, NULL);
|
||||
|
||||
strbuf_addf(err, "cannot update the ref '%s': %s",
|
||||
lock->ref_name, old_msg);
|
||||
free(old_msg);
|
||||
unlock_ref(lock);
|
||||
update->backend_data = NULL;
|
||||
if (parse_and_write_reflog(refs, update, lock, err)) {
|
||||
ret = TRANSACTION_GENERIC_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We try creating a symlink, if that succeeds we continue to the
|
||||
* next update. If not, we try and create a regular symref.
|
||||
*/
|
||||
if (update->new_target && prefer_symlink_refs)
|
||||
if (!create_ref_symlink(lock, update->new_target))
|
||||
continue;
|
||||
|
||||
if (update->flags & REF_NEEDS_COMMIT) {
|
||||
clear_loose_ref_cache(refs);
|
||||
if (commit_ref(lock)) {
|
||||
|
@ -3048,7 +3065,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
|
|||
ref_transaction_add_update(packed_transaction, update->refname,
|
||||
update->flags & ~REF_HAVE_OLD,
|
||||
&update->new_oid, &update->old_oid,
|
||||
NULL);
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
|
||||
|
@ -3291,7 +3308,6 @@ struct ref_storage_be refs_be_files = {
|
|||
.initial_transaction_commit = files_initial_transaction_commit,
|
||||
|
||||
.pack_refs = files_pack_refs,
|
||||
.create_symref = files_create_symref,
|
||||
.rename_ref = files_rename_ref,
|
||||
.copy_ref = files_copy_ref,
|
||||
|
||||
|
|
|
@ -1714,7 +1714,6 @@ struct ref_storage_be refs_be_packed = {
|
|||
.initial_transaction_commit = packed_initial_transaction_commit,
|
||||
|
||||
.pack_refs = packed_pack_refs,
|
||||
.create_symref = NULL,
|
||||
.rename_ref = NULL,
|
||||
.copy_ref = NULL,
|
||||
|
||||
|
|
|
@ -124,6 +124,19 @@ struct ref_update {
|
|||
*/
|
||||
struct object_id old_oid;
|
||||
|
||||
/*
|
||||
* If set, point the reference to this value. This can also be
|
||||
* used to convert regular references to become symbolic refs.
|
||||
* Cannot be set together with `new_oid`.
|
||||
*/
|
||||
const char *new_target;
|
||||
|
||||
/*
|
||||
* If set, check that the reference previously pointed to this
|
||||
* value. Cannot be set together with `old_oid`.
|
||||
*/
|
||||
const char *old_target;
|
||||
|
||||
/*
|
||||
* One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG,
|
||||
* REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags.
|
||||
|
@ -173,6 +186,7 @@ struct ref_update *ref_transaction_add_update(
|
|||
const char *refname, unsigned int flags,
|
||||
const struct object_id *new_oid,
|
||||
const struct object_id *old_oid,
|
||||
const char *new_target, const char *old_target,
|
||||
const char *msg);
|
||||
|
||||
/*
|
||||
|
@ -552,10 +566,6 @@ typedef int ref_transaction_commit_fn(struct ref_store *refs,
|
|||
|
||||
typedef int pack_refs_fn(struct ref_store *ref_store,
|
||||
struct pack_refs_opts *opts);
|
||||
typedef int create_symref_fn(struct ref_store *ref_store,
|
||||
const char *ref_target,
|
||||
const char *refs_heads_master,
|
||||
const char *logmsg);
|
||||
typedef int rename_ref_fn(struct ref_store *ref_store,
|
||||
const char *oldref, const char *newref,
|
||||
const char *logmsg);
|
||||
|
@ -676,7 +686,6 @@ struct ref_storage_be {
|
|||
ref_transaction_commit_fn *initial_transaction_commit;
|
||||
|
||||
pack_refs_fn *pack_refs;
|
||||
create_symref_fn *create_symref;
|
||||
rename_ref_fn *rename_ref;
|
||||
copy_ref_fn *copy_ref;
|
||||
|
||||
|
@ -735,4 +744,25 @@ void base_ref_store_init(struct ref_store *refs, struct repository *repo,
|
|||
*/
|
||||
struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_store *store);
|
||||
|
||||
/*
|
||||
* Return the refname under which update was originally requested.
|
||||
*/
|
||||
const char *ref_update_original_update_refname(struct ref_update *update);
|
||||
|
||||
/*
|
||||
* Helper function to check if the new value is null, this
|
||||
* takes into consideration that the update could be a regular
|
||||
* ref or a symbolic ref.
|
||||
*/
|
||||
int ref_update_has_null_new_value(struct ref_update *update);
|
||||
|
||||
/*
|
||||
* Check whether the old_target values stored in update are consistent
|
||||
* with the referent, which is the symbolic reference's current value.
|
||||
* If everything is OK, return 0; otherwise, write an error message to
|
||||
* err and return -1.
|
||||
*/
|
||||
int ref_update_check_old_target(const char *referent, struct ref_update *update,
|
||||
struct strbuf *err);
|
||||
|
||||
#endif /* REFS_REFS_INTERNAL_H */
|
||||
|
|
|
@ -579,16 +579,6 @@ static int reftable_be_read_symbolic_ref(struct ref_store *ref_store,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the refname under which update was originally requested.
|
||||
*/
|
||||
static const char *original_update_refname(struct ref_update *update)
|
||||
{
|
||||
while (update->parent_update)
|
||||
update = update->parent_update;
|
||||
return update->refname;
|
||||
}
|
||||
|
||||
struct reftable_transaction_update {
|
||||
struct ref_update *update;
|
||||
struct object_id current_oid;
|
||||
|
@ -827,7 +817,7 @@ 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, u->msg);
|
||||
&u->new_oid, &u->old_oid, NULL, NULL, u->msg);
|
||||
string_list_insert(&affected_refnames, new_update->refname);
|
||||
}
|
||||
|
||||
|
@ -854,7 +844,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
|
|||
* There is no need to write the reference deletion
|
||||
* when the reference in question doesn't exist.
|
||||
*/
|
||||
if (u->flags & REF_HAVE_NEW && !is_null_oid(&u->new_oid)) {
|
||||
if ((u->flags & REF_HAVE_NEW) && !ref_update_has_null_new_value(u)) {
|
||||
ret = queue_transaction_update(refs, tx_data, u,
|
||||
¤t_oid, err);
|
||||
if (ret)
|
||||
|
@ -867,7 +857,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
|
|||
/* The reference does not exist, but we expected it to. */
|
||||
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||
"unable to resolve reference '%s'"),
|
||||
original_update_refname(u), u->refname);
|
||||
ref_update_original_update_refname(u), u->refname);
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
@ -905,8 +895,10 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
|
|||
* intertwined with the locking in files-backend.c.
|
||||
*/
|
||||
new_update = ref_transaction_add_update(
|
||||
transaction, referent.buf, new_flags,
|
||||
&u->new_oid, &u->old_oid, u->msg);
|
||||
transaction, referent.buf, new_flags,
|
||||
&u->new_oid, &u->old_oid, u->new_target,
|
||||
u->old_target, u->msg);
|
||||
|
||||
new_update->parent_update = u;
|
||||
|
||||
/*
|
||||
|
@ -936,20 +928,25 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
|
|||
* individual refs. But the error messages match what the files
|
||||
* backend returns, which keeps our tests happy.
|
||||
*/
|
||||
if (u->flags & REF_HAVE_OLD && !oideq(¤t_oid, &u->old_oid)) {
|
||||
if (u->old_target) {
|
||||
if (ref_update_check_old_target(referent.buf, u, err)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
} else if ((u->flags & 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"),
|
||||
original_update_refname(u));
|
||||
"reference already exists"),
|
||||
ref_update_original_update_refname(u));
|
||||
else if (is_null_oid(¤t_oid))
|
||||
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||
"reference is missing but expected %s"),
|
||||
original_update_refname(u),
|
||||
"reference is missing but expected %s"),
|
||||
ref_update_original_update_refname(u),
|
||||
oid_to_hex(&u->old_oid));
|
||||
else
|
||||
strbuf_addf(err, _("cannot lock ref '%s': "
|
||||
"is at %s but expected %s"),
|
||||
original_update_refname(u),
|
||||
"is at %s but expected %s"),
|
||||
ref_update_original_update_refname(u),
|
||||
oid_to_hex(¤t_oid),
|
||||
oid_to_hex(&u->old_oid));
|
||||
ret = -1;
|
||||
|
@ -1047,7 +1044,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
|
|||
* - `core.logAllRefUpdates` tells us to create the reflog for
|
||||
* the given ref.
|
||||
*/
|
||||
if (u->flags & REF_HAVE_NEW && !(u->type & REF_ISSYMREF) && is_null_oid(&u->new_oid)) {
|
||||
if ((u->flags & REF_HAVE_NEW) &&
|
||||
!(u->type & REF_ISSYMREF) &&
|
||||
ref_update_has_null_new_value(u)) {
|
||||
struct reftable_log_record log = {0};
|
||||
struct reftable_iterator it = {0};
|
||||
|
||||
|
@ -1088,24 +1087,52 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
|
|||
(u->flags & REF_FORCE_CREATE_REFLOG ||
|
||||
should_write_log(&arg->refs->base, u->refname))) {
|
||||
struct reftable_log_record *log;
|
||||
int create_reflog = 1;
|
||||
|
||||
ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
|
||||
log = &logs[logs_nr++];
|
||||
memset(log, 0, sizeof(*log));
|
||||
if (u->new_target) {
|
||||
if (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target,
|
||||
RESOLVE_REF_READING, &u->new_oid, NULL)) {
|
||||
/*
|
||||
* TODO: currently we skip creating reflogs for dangling
|
||||
* symref updates. It would be nice to capture this as
|
||||
* zero oid updates however.
|
||||
*/
|
||||
create_reflog = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fill_reftable_log_record(log, &committer_ident);
|
||||
log->update_index = ts;
|
||||
log->refname = xstrdup(u->refname);
|
||||
memcpy(log->value.update.new_hash, u->new_oid.hash, GIT_MAX_RAWSZ);
|
||||
memcpy(log->value.update.old_hash, tx_update->current_oid.hash, GIT_MAX_RAWSZ);
|
||||
log->value.update.message =
|
||||
xstrndup(u->msg, arg->refs->write_options.block_size / 2);
|
||||
if (create_reflog) {
|
||||
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;
|
||||
log->refname = xstrdup(u->refname);
|
||||
memcpy(log->value.update.new_hash,
|
||||
u->new_oid.hash, GIT_MAX_RAWSZ);
|
||||
memcpy(log->value.update.old_hash,
|
||||
tx_update->current_oid.hash, GIT_MAX_RAWSZ);
|
||||
log->value.update.message =
|
||||
xstrndup(u->msg, arg->refs->write_options.block_size / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (u->flags & REF_LOG_ONLY)
|
||||
continue;
|
||||
|
||||
if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) {
|
||||
if (u->new_target) {
|
||||
struct reftable_ref_record ref = {
|
||||
.refname = (char *)u->refname,
|
||||
.value_type = REFTABLE_REF_SYMREF,
|
||||
.value.symref = (char *)u->new_target,
|
||||
.update_index = ts,
|
||||
};
|
||||
|
||||
ret = reftable_writer_add_ref(writer, &ref);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
} else if ((u->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(u)) {
|
||||
struct reftable_ref_record ref = {
|
||||
.refname = (char *)u->refname,
|
||||
.update_index = ts,
|
||||
|
@ -1237,110 +1264,6 @@ struct write_create_symref_arg {
|
|||
const char *logmsg;
|
||||
};
|
||||
|
||||
static int write_create_symref_table(struct reftable_writer *writer, void *cb_data)
|
||||
{
|
||||
struct write_create_symref_arg *create = cb_data;
|
||||
uint64_t ts = reftable_stack_next_update_index(create->stack);
|
||||
struct reftable_ref_record ref = {
|
||||
.refname = (char *)create->refname,
|
||||
.value_type = REFTABLE_REF_SYMREF,
|
||||
.value.symref = (char *)create->target,
|
||||
.update_index = ts,
|
||||
};
|
||||
struct ident_split committer_ident = {0};
|
||||
struct reftable_log_record log = {0};
|
||||
struct object_id new_oid;
|
||||
struct object_id old_oid;
|
||||
const char *committer_info;
|
||||
int ret;
|
||||
|
||||
reftable_writer_set_limits(writer, ts, ts);
|
||||
|
||||
ret = refs_verify_refname_available(&create->refs->base, create->refname,
|
||||
NULL, NULL, create->err);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = reftable_writer_add_ref(writer, &ref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Note that it is important to try and resolve the reference before we
|
||||
* write the log entry. This is because `should_write_log()` will munge
|
||||
* `core.logAllRefUpdates`, which is undesirable when we create a new
|
||||
* repository because it would be written into the config. As HEAD will
|
||||
* not resolve for new repositories this ordering will ensure that this
|
||||
* never happens.
|
||||
*/
|
||||
if (!create->logmsg ||
|
||||
!refs_resolve_ref_unsafe(&create->refs->base, create->target,
|
||||
RESOLVE_REF_READING, &new_oid, NULL) ||
|
||||
!should_write_log(&create->refs->base, create->refname))
|
||||
return 0;
|
||||
|
||||
committer_info = git_committer_info(0);
|
||||
if (split_ident_line(&committer_ident, committer_info, strlen(committer_info)))
|
||||
BUG("failed splitting committer info");
|
||||
|
||||
fill_reftable_log_record(&log, &committer_ident);
|
||||
log.refname = xstrdup(create->refname);
|
||||
log.update_index = ts;
|
||||
log.value.update.message = xstrndup(create->logmsg,
|
||||
create->refs->write_options.block_size / 2);
|
||||
memcpy(log.value.update.new_hash, new_oid.hash, GIT_MAX_RAWSZ);
|
||||
if (refs_resolve_ref_unsafe(&create->refs->base, create->refname,
|
||||
RESOLVE_REF_READING, &old_oid, NULL))
|
||||
memcpy(log.value.update.old_hash, old_oid.hash, GIT_MAX_RAWSZ);
|
||||
|
||||
ret = reftable_writer_add_log(writer, &log);
|
||||
reftable_log_record_release(&log);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reftable_be_create_symref(struct ref_store *ref_store,
|
||||
const char *refname,
|
||||
const char *target,
|
||||
const char *logmsg)
|
||||
{
|
||||
struct reftable_ref_store *refs =
|
||||
reftable_be_downcast(ref_store, REF_STORE_WRITE, "create_symref");
|
||||
struct reftable_stack *stack = stack_for(refs, refname, &refname);
|
||||
struct strbuf err = STRBUF_INIT;
|
||||
struct write_create_symref_arg arg = {
|
||||
.refs = refs,
|
||||
.stack = stack,
|
||||
.refname = refname,
|
||||
.target = target,
|
||||
.logmsg = logmsg,
|
||||
.err = &err,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = refs->err;
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
ret = reftable_stack_reload(stack);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
ret = reftable_stack_add(stack, &write_create_symref_table, &arg);
|
||||
|
||||
done:
|
||||
assert(ret != REFTABLE_API_ERROR);
|
||||
if (ret) {
|
||||
if (err.len)
|
||||
error("%s", err.buf);
|
||||
else
|
||||
error("unable to write symref for %s: %s", refname,
|
||||
reftable_error_str(ret));
|
||||
}
|
||||
|
||||
strbuf_release(&err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct write_copy_arg {
|
||||
struct reftable_ref_store *refs;
|
||||
struct reftable_stack *stack;
|
||||
|
@ -2255,7 +2178,6 @@ struct ref_storage_be refs_be_reftable = {
|
|||
.initial_transaction_commit = reftable_be_initial_transaction_commit,
|
||||
|
||||
.pack_refs = reftable_be_pack_refs,
|
||||
.create_symref = reftable_be_create_symref,
|
||||
.rename_ref = reftable_be_rename_ref,
|
||||
.copy_ref = reftable_be_copy_ref,
|
||||
|
||||
|
|
2
reset.c
2
reset.c
|
@ -72,7 +72,7 @@ static int update_refs(const struct reset_head_opts *opts,
|
|||
switch_to_branch, oid, NULL, 0,
|
||||
UPDATE_REFS_MSG_ON_ERR);
|
||||
if (!ret)
|
||||
ret = refs_create_symref(get_main_ref_store(the_repository),
|
||||
ret = refs_update_symref(get_main_ref_store(the_repository),
|
||||
"HEAD", switch_to_branch,
|
||||
reflog_head);
|
||||
}
|
||||
|
|
11
sequencer.c
11
sequencer.c
|
@ -666,7 +666,7 @@ static int fast_forward_to(struct repository *r,
|
|||
if (!transaction ||
|
||||
ref_transaction_update(transaction, "HEAD",
|
||||
to, unborn && !is_rebase_i(opts) ?
|
||||
null_oid() : from,
|
||||
null_oid() : from, NULL, NULL,
|
||||
0, sb.buf, &err) ||
|
||||
ref_transaction_commit(transaction, &err)) {
|
||||
ref_transaction_free(transaction);
|
||||
|
@ -1301,7 +1301,7 @@ int update_head_with_reflog(const struct commit *old_head,
|
|||
if (!transaction ||
|
||||
ref_transaction_update(transaction, "HEAD", new_head,
|
||||
old_head ? &old_head->object.oid : null_oid(),
|
||||
0, sb.buf, err) ||
|
||||
NULL, NULL, 0, sb.buf, err) ||
|
||||
ref_transaction_commit(transaction, err)) {
|
||||
ret = -1;
|
||||
}
|
||||
|
@ -3835,8 +3835,9 @@ static int do_label(struct repository *r, const char *name, int len)
|
|||
} else if (repo_get_oid(r, "HEAD", &head_oid)) {
|
||||
error(_("could not read HEAD"));
|
||||
ret = -1;
|
||||
} else if (ref_transaction_update(transaction, ref_name.buf, &head_oid,
|
||||
NULL, 0, msg.buf, &err) < 0 ||
|
||||
} else if (ref_transaction_update(transaction, ref_name.buf,
|
||||
&head_oid, NULL, NULL, NULL,
|
||||
0, msg.buf, &err) < 0 ||
|
||||
ref_transaction_commit(transaction, &err)) {
|
||||
error("%s", err.buf);
|
||||
ret = -1;
|
||||
|
@ -5043,7 +5044,7 @@ cleanup_head_ref:
|
|||
}
|
||||
msg = reflog_message(opts, "finish", "returning to %s",
|
||||
head_ref.buf);
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", head_ref.buf, msg)) {
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", head_ref.buf, msg)) {
|
||||
res = error(_("could not update HEAD to %s"),
|
||||
head_ref.buf);
|
||||
goto cleanup_head_ref;
|
||||
|
|
2
setup.c
2
setup.c
|
@ -2067,7 +2067,7 @@ void create_reference_database(unsigned int ref_storage_format,
|
|||
die(_("invalid initial branch name: '%s'"),
|
||||
initial_branch);
|
||||
|
||||
if (refs_create_symref(get_main_ref_store(the_repository), "HEAD", ref, NULL) < 0)
|
||||
if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", ref, NULL) < 0)
|
||||
exit(1);
|
||||
free(ref);
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv)
|
|||
const char *target = notnull(*argv++, "target");
|
||||
const char *logmsg = *argv++;
|
||||
|
||||
return refs_create_symref(refs, refname, target, logmsg);
|
||||
return refs_update_symref(refs, refname, target, logmsg);
|
||||
}
|
||||
|
||||
static struct flag_definition transaction_flags[] = {
|
||||
|
|
|
@ -134,4 +134,27 @@ test_expect_success 'interleaving hook calls succeed' '
|
|||
test_cmp expect target-repo.git/actual
|
||||
'
|
||||
|
||||
test_expect_success 'hook captures git-symbolic-ref updates' '
|
||||
test_when_finished "rm actual" &&
|
||||
|
||||
test_hook reference-transaction <<-\EOF &&
|
||||
echo "$*" >>actual
|
||||
while read -r line
|
||||
do
|
||||
printf "%s\n" "$line"
|
||||
done >>actual
|
||||
EOF
|
||||
|
||||
git symbolic-ref refs/heads/symref refs/heads/main &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
prepared
|
||||
$ZERO_OID ref:refs/heads/main refs/heads/symref
|
||||
committed
|
||||
$ZERO_OID ref:refs/heads/main refs/heads/symref
|
||||
EOF
|
||||
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
2
walker.c
2
walker.c
|
@ -326,7 +326,7 @@ int walker_fetch(struct walker *walker, int targets, char **target,
|
|||
strbuf_reset(&refname);
|
||||
strbuf_addf(&refname, "refs/%s", write_ref[i]);
|
||||
if (ref_transaction_update(transaction, refname.buf,
|
||||
oids + i, NULL, 0,
|
||||
oids + i, NULL, NULL, NULL, 0,
|
||||
msg ? msg : "fetch (unknown)",
|
||||
&err)) {
|
||||
error("%s", err.buf);
|
||||
|
|
Loading…
Reference in New Issue