refs: atomically record overwritten ref in update_symref
When updating a symref with update_symref it's currently not possible to know for sure what was the previous value that was overwritten. Extend refs_update_symref under a new function name, to record the value after the ref has been locked if the caller of refs_update_symref_extended requests it via a new variable in the function call. Make the return value of the function notify the caller, if the previous value was actually not a symbolic reference. Keep the original refs_update_symref function with the same signature, but now as a wrapper around refs_update_symref_extended. Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:

committed by
Junio C Hamano

parent
8102d10ff8
commit
d842cd1301
22
refs.c
22
refs.c
@ -2115,6 +2115,13 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct
|
|||||||
|
|
||||||
int refs_update_symref(struct ref_store *refs, const char *ref,
|
int refs_update_symref(struct ref_store *refs, const char *ref,
|
||||||
const char *target, const char *logmsg)
|
const char *target, const char *logmsg)
|
||||||
|
{
|
||||||
|
return refs_update_symref_extended(refs, ref, target, logmsg, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int refs_update_symref_extended(struct ref_store *refs, const char *ref,
|
||||||
|
const char *target, const char *logmsg,
|
||||||
|
struct strbuf *referent)
|
||||||
{
|
{
|
||||||
struct ref_transaction *transaction;
|
struct ref_transaction *transaction;
|
||||||
struct strbuf err = STRBUF_INIT;
|
struct strbuf err = STRBUF_INIT;
|
||||||
@ -2125,10 +2132,22 @@ int refs_update_symref(struct ref_store *refs, const char *ref,
|
|||||||
ref_transaction_update(transaction, ref, NULL, NULL,
|
ref_transaction_update(transaction, ref, NULL, NULL,
|
||||||
target, NULL, REF_NO_DEREF,
|
target, NULL, REF_NO_DEREF,
|
||||||
logmsg, &err) ||
|
logmsg, &err) ||
|
||||||
ref_transaction_commit(transaction, &err)) {
|
ref_transaction_prepare(transaction, &err)) {
|
||||||
ret = error("%s", err.buf);
|
ret = error("%s", err.buf);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (referent && refs_read_symbolic_ref(refs, ref, referent) == NOT_A_SYMREF) {
|
||||||
|
struct object_id oid;
|
||||||
|
if (!refs_read_ref(refs, ref, &oid)) {
|
||||||
|
strbuf_addstr(referent, oid_to_hex(&oid));
|
||||||
|
ret = NOT_A_SYMREF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ref_transaction_commit(transaction, &err))
|
||||||
|
ret = error("%s", err.buf);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
strbuf_release(&err);
|
strbuf_release(&err);
|
||||||
if (transaction)
|
if (transaction)
|
||||||
ref_transaction_free(transaction);
|
ref_transaction_free(transaction);
|
||||||
@ -2948,4 +2967,3 @@ int ref_update_expects_existing_old_ref(struct ref_update *update)
|
|||||||
return (update->flags & REF_HAVE_OLD) &&
|
return (update->flags & REF_HAVE_OLD) &&
|
||||||
(!is_null_oid(&update->old_oid) || update->old_target);
|
(!is_null_oid(&update->old_oid) || update->old_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
refs.h
4
refs.h
@ -584,6 +584,10 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
|
|||||||
int refs_update_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);
|
const char *target, const char *logmsg);
|
||||||
|
|
||||||
|
int refs_update_symref_extended(struct ref_store *refs, const char *refname,
|
||||||
|
const char *target, const char *logmsg,
|
||||||
|
struct strbuf *referent);
|
||||||
|
|
||||||
enum action_on_err {
|
enum action_on_err {
|
||||||
UPDATE_REFS_MSG_ON_ERR,
|
UPDATE_REFS_MSG_ON_ERR,
|
||||||
UPDATE_REFS_DIE_ON_ERR,
|
UPDATE_REFS_DIE_ON_ERR,
|
||||||
|
Reference in New Issue
Block a user