summaryrefslogtreecommitdiffstats
path: root/refs.c
diff options
context:
space:
mode:
authorBence Ferdinandy <bence@ferdinandy.com>2024-11-22 13:28:45 +0100
committerJunio C Hamano <gitster@pobox.com>2024-11-25 03:46:35 +0100
commitd842cd130122c6f3d3019d467040570d7d001f7a (patch)
tree89e96dc4ee82cbcf252d10b23eaddd77c9b9f9cc /refs.c
parentrefs: standardize output of refs_read_symbolic_ref (diff)
downloadgit-d842cd130122c6f3d3019d467040570d7d001f7a.tar.xz
git-d842cd130122c6f3d3019d467040570d7d001f7a.zip
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>
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c22
1 files changed, 20 insertions, 2 deletions
diff --git a/refs.c b/refs.c
index 5f729ed412..d80efd58f0 100644
--- a/refs.c
+++ b/refs.c
@@ -2116,6 +2116,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;
int ret = 0;
@@ -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);
}
-