mirror of
https://github.com/git/git
synced 2024-11-05 04:53:18 +00:00
Merge branch 'kn/ref-transaction-symref' into kn/update-ref-symref
* 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()`
This commit is contained in:
commit
939d49e9bd
22 changed files with 375 additions and 281 deletions
|
@ -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
|
@ -627,7 +627,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);
|
||||
|
|
|
@ -555,7 +555,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);
|
||||
}
|
||||
|
|
|
@ -1634,7 +1634,7 @@ static int update_branch(struct branch *b)
|
|||
transaction = ref_transaction_begin(&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);
|
||||
|
@ -1675,7 +1675,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;
|
||||
}
|
||||
|
|
|
@ -668,7 +668,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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -201,7 +201,7 @@ static int replace_object_oid(const char *object_ref,
|
|||
transaction = ref_transaction_begin(&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);
|
||||
|
||||
|
|
|
@ -660,6 +660,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
|||
transaction = ref_transaction_begin(&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;
|
||||
|
||||
|
|
119
refs.c
119
refs.c
|
@ -1217,6 +1217,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);
|
||||
|
@ -1228,6 +1230,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;
|
||||
|
@ -1235,16 +1238,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;
|
||||
}
|
||||
|
@ -1253,6 +1264,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)
|
||||
{
|
||||
|
@ -1278,9 +1291,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;
|
||||
}
|
||||
|
||||
|
@ -1295,7 +1310,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,
|
||||
|
@ -1308,7 +1324,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,
|
||||
|
@ -1321,6 +1338,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);
|
||||
}
|
||||
|
||||
|
@ -1335,8 +1353,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);
|
||||
|
@ -2266,25 +2284,33 @@ 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 create_symref(const char *ref_target, const char *refs_heads_master,
|
||||
const char *logmsg)
|
||||
{
|
||||
return refs_create_symref(get_main_ref_store(the_repository), ref_target,
|
||||
return refs_update_symref(get_main_ref_store(the_repository), ref_target,
|
||||
refs_heads_master, logmsg);
|
||||
}
|
||||
|
||||
|
@ -2338,10 +2364,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) {
|
||||
|
@ -2790,3 +2828,38 @@ int copy_existing_ref(const char *oldref, const char *newref, const char *logmsg
|
|||
{
|
||||
return refs_copy_existing_ref(get_main_ref_store(the_repository), oldref, newref, logmsg);
|
||||
}
|
||||
|
||||
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
|
@ -606,7 +606,7 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
|
|||
int copy_existing_ref(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);
|
||||
int create_symref(const char *refname, const char *target, const char *logmsg);
|
||||
|
||||
|
@ -648,6 +648,16 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
|
|||
* 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
|
||||
|
@ -713,7 +723,11 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
|
|||
* 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.
|
||||
|
@ -722,6 +736,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 @@ static int files_transaction_prepare(struct ref_store *ref_store,
|
|||
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 */
|
||||
|
|
|
@ -581,16 +581,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;
|
||||
|
@ -829,7 +819,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);
|
||||
}
|
||||
|
||||
|
@ -856,7 +846,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)
|
||||
|
@ -869,7 +859,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;
|
||||
}
|
||||
|
@ -907,8 +897,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;
|
||||
|
||||
/*
|
||||
|
@ -938,20 +930,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;
|
||||
|
@ -1043,7 +1040,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};
|
||||
|
||||
|
@ -1084,24 +1083,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);
|
||||
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);
|
||||
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,
|
||||
|
@ -1232,91 +1259,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 reftable_log_record log = {0};
|
||||
struct object_id new_oid;
|
||||
struct object_id old_oid;
|
||||
int ret;
|
||||
|
||||
reftable_writer_set_limits(writer, ts, ts);
|
||||
|
||||
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;
|
||||
|
||||
fill_reftable_log_record(&log);
|
||||
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 write_create_symref_arg arg = {
|
||||
.refs = refs,
|
||||
.stack = stack,
|
||||
.refname = refname,
|
||||
.target = target,
|
||||
.logmsg = logmsg,
|
||||
};
|
||||
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)
|
||||
error("unable to write symref for %s: %s", refname,
|
||||
reftable_error_str(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct write_copy_arg {
|
||||
struct reftable_ref_store *refs;
|
||||
struct reftable_stack *stack;
|
||||
|
@ -2224,7 +2166,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,
|
||||
|
||||
|
|
|
@ -616,7 +616,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);
|
||||
|
@ -1248,7 +1248,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;
|
||||
}
|
||||
|
@ -3764,8 +3764,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;
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -286,7 +286,7 @@ test_expect_success 'ref transaction: creating symbolic ref fails with F/D confl
|
|||
git init repo &&
|
||||
test_commit -C repo A &&
|
||||
cat >expect <<-EOF &&
|
||||
error: unable to write symref for refs/heads: file/directory conflict
|
||||
error: ${SQ}refs/heads/main${SQ} exists; cannot create ${SQ}refs/heads${SQ}
|
||||
EOF
|
||||
test_must_fail git -C repo symbolic-ref refs/heads refs/heads/foo 2>err &&
|
||||
test_cmp expect err
|
||||
|
|
|
@ -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
|
@ -324,7 +324,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 a new issue