]> git.ipfire.org Git - thirdparty/git.git/commitdiff
refs: atomically record overwritten ref in update_symref
authorBence Ferdinandy <bence@ferdinandy.com>
Fri, 22 Nov 2024 12:28:45 +0000 (13:28 +0100)
committerJunio C Hamano <gitster@pobox.com>
Mon, 25 Nov 2024 02:46:35 +0000 (11:46 +0900)
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>
refs.c
refs.h

diff --git a/refs.c b/refs.c
index 5f729ed4124f7fe8fa9df7fd1f1ce951abefc585..d80efd58f0aab1b3877324fe7bf62a883f577a2d 100644 (file)
--- a/refs.c
+++ b/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,
                       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 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,
                                   target, NULL, REF_NO_DEREF,
                                   logmsg, &err) ||
-           ref_transaction_commit(transaction, &err)) {
+           ref_transaction_prepare(transaction, &err)) {
                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);
        if (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) &&
                (!is_null_oid(&update->old_oid) || update->old_target);
 }
-
diff --git a/refs.h b/refs.h
index 22c2b45b8bb08f57d291aedd57cba3bb8e90bf14..5c46ac9f3435ccf9cbed034e42c6dd583d9f346e 100644 (file)
--- a/refs.h
+++ b/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,
                       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 {
        UPDATE_REFS_MSG_ON_ERR,
        UPDATE_REFS_DIE_ON_ERR,