From patchwork Fri Apr 26 15:24:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13644973 Received: from mail-ej1-f52.google.com (mail-ej1-f52.google.com [209.85.218.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3D9D92E639 for ; Fri, 26 Apr 2024 15:24:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145098; cv=none; b=oORrIjbLUTzpHJ/UWewv2xIDBTStjs2UKwQaLuh9dsKOZ/3Q2OD768w22s5pDRdm6Gyu39fUcF6EXlRvELVHh7wCiWJUgmMnH8kxIl9o7T3ziBoNuWgEqIKrHjinUu78BPDkD/sSY5o6zSydIwi1LE/B78cgea1ivE3HdD+IucI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145098; c=relaxed/simple; bh=fOGkuS+FGuchGv5+LYRzZHD4p47kPebyakGkr9j8efY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JFsQc0m24Oxo4RWjk9YtpUtqD5+mQs3eTHHXMS9XldhtqHHY6bFk1x3B3WtRQO5tgPzmovIjN0mHfJ/52wwczw/osK6h7fjkKJGwQwr+iZ/WBkFC4UL4UMUOlKM74wXwMMvrO/RacWGRWu/eE7cASUQ/tsE1WAOEgI5dzpc06Is= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mzeZttmQ; arc=none smtp.client-ip=209.85.218.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mzeZttmQ" Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-a55b93f5540so302373166b.1 for ; Fri, 26 Apr 2024 08:24:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1714145094; x=1714749894; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+FcK+Y3WLNSwd70h2DIKeDgZjitKHjU45fhDHh4aEwc=; b=mzeZttmQ5Ovv+SYeztxj5dxPwl2jg0lkf1kbpppzTLfZZc7NHsYtLq0Do3NyIwW0t3 K4q/Mtb2aS3mloFn1KbBjUHI/vL6sxkAtwpIxnCt8yUV/vrOkOrNx0tt+AXYhSULS0TF yGOSe4NwqmUDJveaCbN0J826N4inYouadYXzA2FqKmTKJM+KUQw+raf7/+YHE0chCjuo 4GfiQu3LPwLmJg8aO5O49f2BulEy4+PUfu6NAo7Cm6F5AIVB0K8FW3yRH2jzh72sm5Eh 0WZKmQ2LD/F8tAc3+1gdcqz/qtW95OTdZijg9VNyI6hWQ/mXplQ7vj/6DbCune1r0G37 j82g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714145094; x=1714749894; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+FcK+Y3WLNSwd70h2DIKeDgZjitKHjU45fhDHh4aEwc=; b=BbDoQ+Dt8cpzxJf29kd8OAU/s6zWLqPUCH2jhz578i1U3LkkCwyHwFA4NFZCpTHPDV cupdhdyxHi30178A9DNcKu/OYsz1jxmqfjDxIxWxPjTl6/pHxwSd/7mNuC9ioHmlyMB4 GNI0po9jenszs0wRRR4BDEL3/INpbc74iQAj9a1DWLXYtx8jsaR4YrSkEZtHqyeWSAGW LxrNa28xBk/R5RxEvX/xk43cA4VmNTSilckQBMQxbUfWSjilgl35ABuoBGwSMa8m6T5+ skRPMCMnE4t57o/ZgRZxSFV/NpqXjz3UL4c6hBlVjlVl4qpcOu2JqFbBEhz8xtbu3+VM YWCw== X-Forwarded-Encrypted: i=1; AJvYcCWTwcF243JcUhFvGH52eBN0/nWNqavQz1WPlL7p5tr9cLxA7WxM0j/T7jIGJpyXy9EB6KfZojzyYTk/CefS4mM7BnTK X-Gm-Message-State: AOJu0Yy2f/l1s/7JC5rpz6vvWSFOiT8bNgn6GMmI5/YYCpcDthcBz8Ds fNUb+aDhtNsq5/e7F3Nvd4dPtW2my97SJXAdadVpSuBUrPysVnkc X-Google-Smtp-Source: AGHT+IF7gRxP64SOn4p1LRhKHIAIq3rpklMBXWShukC4r8j7ItHSWU8n8iQpDRhWZKA/F4m1Ja6BXA== X-Received: by 2002:a17:906:3752:b0:a58:8d83:b527 with SMTP id e18-20020a170906375200b00a588d83b527mr2026956ejc.12.1714145094186; Fri, 26 Apr 2024 08:24:54 -0700 (PDT) Received: from localhost.localdomain ([185.223.147.210]) by smtp.gmail.com with ESMTPSA id z6-20020a170906434600b00a518c69c4e3sm10643868ejm.23.2024.04.26.08.24.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 08:24:53 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: christian.couder@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v4 1/7] refs: accept symref values in `ref_transaction[_add]_update` Date: Fri, 26 Apr 2024 17:24:43 +0200 Message-ID: <20240426152449.228860-2-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240426152449.228860-1-knayak@gitlab.com> References: <20240423212818.574123-1-knayak@gitlab.com> <20240426152449.228860-1-knayak@gitlab.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Karthik Nayak The `ref_transaction[_add]_update` functions obtain ref information and flags to create a `ref_update` and add it to the transaction at hand. To extend symref support in transactions, we need to also accept the old and new ref targets and process it. In this commit, let's add the required parameters to the function and modify all call sites. The two parameters added are `new_target` and `old_target`. The `new_target` is used to denote what the reference should point to when the transaction is applied. The `old_target` denotes the value the reference must have before the update. Some functions allow this parameter to be NULL, meaning that the old value of the reference is not checked. The handling logic of these parameters will be added in consequent commits as we add symref commands to the '--stdin' mode of 'git-update-ref'. Signed-off-by: Karthik Nayak --- branch.c | 2 +- builtin/fast-import.c | 5 +++-- builtin/fetch.c | 2 +- builtin/receive-pack.c | 1 + builtin/replace.c | 2 +- builtin/tag.c | 1 + builtin/update-ref.c | 1 + refs.c | 22 +++++++++++++++++----- refs.h | 17 ++++++++++++++++- refs/files-backend.c | 12 ++++++------ refs/refs-internal.h | 13 +++++++++++++ refs/reftable-backend.c | 4 ++-- sequencer.c | 9 +++++---- walker.c | 2 +- 14 files changed, 69 insertions(+), 24 deletions(-) diff --git a/branch.c b/branch.c index e4a738fc7b..48af4c3ceb 100644 --- a/branch.c +++ b/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); diff --git a/builtin/fast-import.c b/builtin/fast-import.c index dc5a9d32dd..297dfb91a1 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -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; } diff --git a/builtin/fetch.c b/builtin/fetch.c index 5857d860db..66840b7c5b 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -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; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index e8d7df14b6..b150ef39a8 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -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); diff --git a/builtin/replace.c b/builtin/replace.c index da59600ad2..7690687b0e 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -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); diff --git a/builtin/tag.c b/builtin/tag.c index 9a33cb50b4..40a65fdebc 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -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)) { diff --git a/builtin/update-ref.c b/builtin/update-ref.c index e46afbc46d..21fdbf6ac8 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -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); diff --git a/refs.c b/refs.c index 55d2e0b2cb..060a31616d 100644 --- a/refs.c +++ b/refs.c @@ -1228,6 +1228,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,6 +1236,11 @@ 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 && !is_null_oid(old_oid) && old_target) + BUG("Only one of old_oid and old_target should be non NULL"); + if (new_oid && !is_null_oid(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; @@ -1253,6 +1259,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) { @@ -1280,7 +1288,8 @@ int ref_transaction_update(struct ref_transaction *transaction, flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? 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 +1304,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 +1318,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 +1332,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 +1347,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); diff --git a/refs.h b/refs.h index d278775e08..c792e13a64 100644 --- a/refs.h +++ b/refs.h @@ -648,6 +648,15 @@ 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 + * update to point to. This takes precedence over new_oid when + * set. If the reference is a regular reference, it will be + * converted to a symbolic reference. + * + * old_target -- the reference that the reference must be pointing to. + * Will only be taken into account when the reference is a symbolic + * reference. + * * 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 +722,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 +735,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); diff --git a/refs/files-backend.c b/refs/files-backend.c index a098d14ea0..e4d0aa3d41 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -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); @@ -2309,7 +2309,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 @@ -2372,7 +2372,7 @@ 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); + NULL, NULL, update->msg); new_update->parent_update = update; @@ -2763,7 +2763,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); } } @@ -3048,7 +3048,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)) { diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 56641aa57a..3040d4797c 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -124,6 +124,18 @@ 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. + */ + const char *new_target; + + /* + * If set and the reference is a symbolic ref, check that the + * reference previously pointed to this value. + */ + 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 +185,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); /* diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 1cda48c504..6104471199 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -829,7 +829,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); } @@ -908,7 +908,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store, */ new_update = ref_transaction_add_update( transaction, referent.buf, new_flags, - &u->new_oid, &u->old_oid, u->msg); + &u->new_oid, &u->old_oid, NULL, NULL, u->msg); new_update->parent_update = u; /* diff --git a/sequencer.c b/sequencer.c index 2c19846385..af1b25692b 100644 --- a/sequencer.c +++ b/sequencer.c @@ -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; diff --git a/walker.c b/walker.c index c0fd632d92..1b3df43906 100644 --- a/walker.c +++ b/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); From patchwork Fri Apr 26 15:24:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13644974 Received: from mail-ed1-f41.google.com (mail-ed1-f41.google.com [209.85.208.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3DEAF14882E for ; Fri, 26 Apr 2024 15:24:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145098; cv=none; b=Pkr9FnPR7x8JhAwFIPftiC6ztrmS6bBFNbnjgM9fb+WrAoBFdrUg58hWwHuV1mW+xPkFN3WuSH3wKvKv91R8anC4EGx0o6rLnnw7UcDVLeaVDce7ovBwoevVpDIwV9VpTM0m/WQz7OE9/uuOrCXuoB+OetB5mMWgLQE6c+vWKVc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145098; c=relaxed/simple; bh=orGXPgisydaJqUHfbfDCBIOnxxMoAGsrT1Ao/VkQcf0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MvKlKpSPoxeQxFm9lOGRbf73gpamLkbm1szVfGxEIXmm9w+99SVSEe52YmUiaY01Mg2sZNxYEQx8em0Bw9RBtDqHyUGUKU9gUQNqgD1kkxOM88vRYHlfiZH01ZZXAOr0utodmafQDwTVJxhlXpbABNd2HV4Az2VujCkhpjwQvXU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GK5tY19Z; arc=none smtp.client-ip=209.85.208.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GK5tY19Z" Received: by mail-ed1-f41.google.com with SMTP id 4fb4d7f45d1cf-57225322312so3273282a12.1 for ; Fri, 26 Apr 2024 08:24:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1714145095; x=1714749895; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9/kSQo4mqApEZDGjq/WK5IpKtIuf94M1WAtNmj93rjQ=; b=GK5tY19Zz8INunVsUnHDSb8UGrqXJVCFvS6+6SjMH6ankvxoJ0AN5abj0CzXGChlmw vFL+ugho+lp41YwjBNW4XIaP/asKI0CgrZhYTOz1SCbOwvB8NjmNNnZUFUi5PTtRwy8t LK8PjXdjHc23i65xF39LVG0QbATsZMslt88mgFpYWdOkaOucLv2QmO0wQdYkFIhebFre rY66DKQwKAUWrx//k4n9JBlO56WBE6eFfSL9WjoqsgEG69MVQUSOB4KIVnk5zleoFpa7 4QshqirwGThiyblpuI8xQRJq8ZWUbQ0HxAqmSaPJCkXKBoPNyqCn98MB6Gc1ero+Gi+J bb7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714145095; x=1714749895; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9/kSQo4mqApEZDGjq/WK5IpKtIuf94M1WAtNmj93rjQ=; b=GGb2u6DhF+yLdiWEx6xdwmlvRpE75jEy8rz0v/VHbfrNodFRqbIAj5F6WCd2/rGUEy RKjAxoIsUdWT9sukKIzmDpfR6K7+mQZZoHmHTyOgK6t1z/LsDizGDxW1PtD3vhE/ND5J WM9U5oIo/QioCl4HS7kQkoegM5EBh1H8GMcFEFsODmfUAb4zTo5Pxh/zSBWp+LZh1drF I0SABN4Ngt5ESWKZsyeNlyVcR0oP269h0paiyQWB1WzF6IhuPfP0xmz0nJhXPZsNJigV HAXMHGlJYAIa9H3Ea/GDp5AUiiAsJnckyaK6Nn+9gXUOs1Q/KMU33yFmFSh6EytrDD/l l2Lg== X-Forwarded-Encrypted: i=1; AJvYcCUkLws/lYKDX22q34gBvxZbfY46PN+KTWihRnP/IbgDeyFz6O/jFa5koFiOeYfTLHk9Gn3nI2S1PWL9qN88Ss2gniJM X-Gm-Message-State: AOJu0YxSMTS9ALOvM7nQR6KODzeC26gwXNt+K8NdWWaKH6iBbp6Q9sOG iB+bzBJPfW3itND/gn0uXBeOgEnoW+UsgoVMMfeZDql0oBD8bynw X-Google-Smtp-Source: AGHT+IEuAC0qWi6haLNvAb7HMim1Afk3XxW1yBkifkdwKJmLTmJhiihZqfaZ/0sSF6UgDM27DfiBPA== X-Received: by 2002:a17:906:5617:b0:a52:56e8:2294 with SMTP id f23-20020a170906561700b00a5256e82294mr2205130ejq.52.1714145095405; Fri, 26 Apr 2024 08:24:55 -0700 (PDT) Received: from localhost.localdomain ([185.223.147.210]) by smtp.gmail.com with ESMTPSA id z6-20020a170906434600b00a518c69c4e3sm10643868ejm.23.2024.04.26.08.24.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 08:24:54 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: christian.couder@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v4 2/7] files-backend: extract out `create_symref_lock` Date: Fri, 26 Apr 2024 17:24:44 +0200 Message-ID: <20240426152449.228860-3-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240426152449.228860-1-knayak@gitlab.com> References: <20240423212818.574123-1-knayak@gitlab.com> <20240426152449.228860-1-knayak@gitlab.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Karthik Nayak The function `create_symref_locked` creates a symref by creating a '.lock' file and then committing the symref lock, which creates the final symref. Split this into two individual functions `create_and_commit_symref` and `create_symref_locked`. This way we can create the symref lock and commit it at different times. This will be used to provide symref support in `git-update-ref(1)`. Signed-off-by: Karthik Nayak --- refs/files-backend.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index e4d0aa3d41..2420dac2aa 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1920,26 +1920,39 @@ static void update_symref_reflog(struct files_ref_store *refs, } } -static int create_symref_locked(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) { + if (!fdopen_lock_file(&lock->lk, "w")) + return error("unable to fdopen %s: %s", + get_lock_file_path(&lock->lk), strerror(errno)); + + /* no error check; commit_ref will check ferror */ + fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target); + return 0; +} + +static int create_and_commit_symref(struct files_ref_store *refs, + struct ref_lock *lock, const char *refname, + const char *target, const char *logmsg) +{ + int ret; + 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", - get_lock_file_path(&lock->lk), strerror(errno)); + ret = create_symref_lock(refs, lock, refname, target); + if (!ret) { + update_symref_reflog(refs, lock, refname, target, logmsg); - update_symref_reflog(refs, lock, refname, target, logmsg); + if (commit_ref(lock) < 0) + return error("unable to write symref for %s: %s", refname, + strerror(errno)); + } - /* 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; } @@ -1960,7 +1973,8 @@ static int files_create_symref(struct ref_store *ref_store, return -1; } - ret = create_symref_locked(refs, lock, refname, target, logmsg); + ret = create_and_commit_symref(refs, lock, refname, target, logmsg); + unlock_ref(lock); return ret; } From patchwork Fri Apr 26 15:24:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13644976 Received: from mail-ej1-f41.google.com (mail-ej1-f41.google.com [209.85.218.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7607A14883B for ; Fri, 26 Apr 2024 15:24:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145100; cv=none; b=sOUpEcJTAC95XcINMKReQHUITJL6jMP05QLuPsFqhughy11HddBfmO2fdIo5ViCl3+uENaDxhQKmRBzEOTgDDcc2TSLUxlqVNinHIFtMrWn9lruLR0OlxMYITgZ6SjB5qUtq63ccLTFCmSitVJ5FgBKAJI7NJpOkhA7CGxhLVS8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145100; c=relaxed/simple; bh=UVArQJvEnJ4fHukVtEKyYIiZvkzF0G3oPUibIdJpKck=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ayt1Zmeg/By1kGTVgci+N1Lz7crw6BfOc1uAKOIuDB4VWy5KU17P3vp9rghYqzMwpp2zSnO02lY3VdGxo/76zDrwG530nVgICua/qWo+SuUzE6dCEfT5f7OMMy8GR6kdrcqR6azC09yG6FoEwoH1U03hlLUAXc0paRdSBPteJfQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UcdK9Og8; arc=none smtp.client-ip=209.85.218.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UcdK9Og8" Received: by mail-ej1-f41.google.com with SMTP id a640c23a62f3a-a58a36008ceso295002966b.0 for ; Fri, 26 Apr 2024 08:24:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1714145097; x=1714749897; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=MDe92oyAJ2BIIHrFol3XElNT+LWwRwR/FOoM2CPH+dE=; b=UcdK9Og8VVXiZdF2KEW9Ia4swdTgdOr3pPFuQrz0D0AxpmXiZRjJEk+FAHJp9Yshyj 6ToVM9FAB38Jw2dNudidjwrPqUtceX9XhXJaM9iSYQF4uO4MmZZfUWCH+O0JU5JXTkBD k8thoxXozdJzB/oGGfMalnOYar+qfjM16rKqQPdkL9gGwBccruYOwm5YogXP+Y3v+goE rpz8JQvn68TMzNh1xn/5raNvRltVPWSbzx4O002lNagrA8pbPvzkPA7NuYcFCDesR/+M W/qbPs3Vh18Mpqv/x9Nkm7ykWkUyYV9L3RZV6SK2IBaDIuvgBDll6FIuUezhNYTk4q+e 0+xQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714145097; x=1714749897; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=MDe92oyAJ2BIIHrFol3XElNT+LWwRwR/FOoM2CPH+dE=; b=hmkNIFWvjNIYyfWRjUmZJ4r8onK7EFpCrIuh5As4i+K8NMSKgPNG+yplwRiNObrWVE jTXbGhhodZajQT4KPBgOeB8QvHxmCsMmpC0VYrxAvplK4te/NmSvzcGMskRfunobmIjm rYlzaFwy/rmpVz2Db6pb3/qcZQNTIstCvIj0Y3k5w2SiMehjLbl4h/lXOJa0Or7Tjz89 u92mXaD0jejKWmULtUqbigJDVlgvQbMWPV9cLJ8gZBg+ogRO/scvC8bAFMy0sYo9KkVR sdtLv/ZJmG1XayOub4OY4YD6akf4NianYdIe5ppAA8Tun2CjjeZV/M3PbmpxoKnY35Vm G7Rg== X-Forwarded-Encrypted: i=1; AJvYcCVnh8BijzDmMnfVKg4BPmV9UtmR1uyQQkuTPUriL26GK+FrEG02znXYZjJO0mL9ts3SfbZUFkzvcgRBajw6T8a9bDFi X-Gm-Message-State: AOJu0YzuxdrpRcSgKBzWWQSViJ1M6jwieEo7PseHRbiA4/niJ7HYxXmB j9MCnFJvVy4LW4bEzNAxibnSIqeqP1zOo1nzm3Hw83u0cSvrCiki X-Google-Smtp-Source: AGHT+IEd4kkCF1cCXHoKXGH3Mud7GsGfpkUDtrNTs5ejAmgwb5cUpkHaXZnHeMpCCynZK8k2nnpWAA== X-Received: by 2002:a17:906:114a:b0:a52:30a3:930c with SMTP id i10-20020a170906114a00b00a5230a3930cmr2411524eja.58.1714145096568; Fri, 26 Apr 2024 08:24:56 -0700 (PDT) Received: from localhost.localdomain ([185.223.147.210]) by smtp.gmail.com with ESMTPSA id z6-20020a170906434600b00a518c69c4e3sm10643868ejm.23.2024.04.26.08.24.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 08:24:55 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: christian.couder@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v4 3/7] update-ref: add support for 'symref-verify' command Date: Fri, 26 Apr 2024 17:24:45 +0200 Message-ID: <20240426152449.228860-4-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240426152449.228860-1-knayak@gitlab.com> References: <20240423212818.574123-1-knayak@gitlab.com> <20240426152449.228860-1-knayak@gitlab.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Karthik Nayak In the previous commits, we added the required base for adding symref commands to the '--stdin' mode provided by 'git-update-ref(1)'. Using them, add a new 'symref-verify' command to verify symrefs. The 'symref-verify' command allows users to verify if a provided contains the provided without changing the . If is not provided, the command will verify that the doesn't exist. Since we're checking for symbolic refs, this command will only work with the 'no-deref' mode. This is because any dereferenced symbolic ref will point to an object and not a ref and the regular 'verify' command can be used in such situations. Add and use `ref_update_is_null_new_value`, a helper function which is used to check if there is a new_value in a reference update. The new value could either be a symref target `new_target` or a OID `new_oid`. We also add tests to test the command in both the regular stdin mode and also with the '-z' flag. We also disable the reference-transaction hook for symref-updates which will be tackled in its own commit. Add required tests for symref support in 'verify' while also adding reflog checks for the pre-existing 'verify' tests. Signed-off-by: Karthik Nayak --- Documentation/git-update-ref.txt | 7 +++ builtin/update-ref.c | 80 +++++++++++++++++++++++---- refs.c | 30 +++++++++-- refs.h | 1 + refs/files-backend.c | 43 +++++++++++++++ refs/refs-internal.h | 7 +++ refs/reftable-backend.c | 21 +++++++- t/t1400-update-ref.sh | 93 +++++++++++++++++++++++++++++++- 8 files changed, 264 insertions(+), 18 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 374a2ebd2b..9fe78b3501 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -65,6 +65,7 @@ performs all modifications together. Specify commands of the form: create SP SP LF delete SP [SP ] LF verify SP [SP ] LF + symref-verify SP [SP ] LF option SP LF start LF prepare LF @@ -86,6 +87,7 @@ quoting: create SP NUL NUL delete SP NUL [] NUL verify SP NUL [] NUL + symref-verify SP [NUL ] NUL option SP NUL start NUL prepare NUL @@ -117,6 +119,11 @@ verify:: Verify against but do not change it. If is zero or missing, the ref must not exist. +symref-verify:: + Verify symbolic against but do not change it. + If is missing, the ref must not exist. Can only be + used in `no-deref` mode. + option:: Modify the behavior of the next command naming a . The only valid option is `no-deref` to avoid dereferencing diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 21fdbf6ac8..419b28169b 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -76,6 +76,29 @@ static char *parse_refname(const char **next) return strbuf_detach(&ref, NULL); } +/* + * Wrapper around parse_refname which skips the next delimiter. + */ +static char *parse_next_refname(const char **next) +{ + if (line_termination) { + /* Without -z, consume SP and use next argument */ + if (!**next || **next == line_termination) + return NULL; + if (**next != ' ') + die("expected SP but got: %s", *next); + } else { + /* With -z, read the next NUL-terminated line */ + if (**next) + return NULL; + } + /* Skip the delimiter */ + (*next)++; + + return parse_refname(next); +} + + /* * The value being parsed is (as opposed to ; the * difference affects which error messages are generated): @@ -297,11 +320,47 @@ static void parse_cmd_verify(struct ref_transaction *transaction, die("verify %s: extra input: %s", refname, next); if (ref_transaction_verify(transaction, refname, &old_oid, - update_flags, &err)) + NULL, update_flags, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + strbuf_release(&err); +} + +static void parse_cmd_symref_verify(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf err = STRBUF_INIT; + struct object_id old_oid; + char *refname, *old_target; + + if (!(update_flags & REF_NO_DEREF)) + die("symref-verify: cannot operate with deref mode"); + + refname = parse_refname(&next); + if (!refname) + die("symref-verify: missing "); + + /* + * old_ref is optional, but we want to differentiate between + * a NULL and zero value. + */ + old_target = parse_next_refname(&next); + if (!old_target) + old_oid = *null_oid(); + + if (*next != line_termination) + die("symref-verify %s: extra input: %s", refname, next); + + if (ref_transaction_verify(transaction, refname, + old_target ? NULL : &old_oid, + old_target, update_flags, &err)) die("%s", err.buf); update_flags = default_flags; free(refname); + free(old_target); strbuf_release(&err); } @@ -380,15 +439,16 @@ static const struct parse_cmd { unsigned args; enum update_refs_state state; } command[] = { - { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, - { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, - { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, - { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, - { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, - { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, - { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, - { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, - { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, + { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, + { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, + { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, + { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, + { "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN }, + { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, + { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, + { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, + { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, + { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, }; static void update_refs_stdin(void) diff --git a/refs.c b/refs.c index 060a31616d..0e1013b5ab 100644 --- a/refs.c +++ b/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((void *)transaction->updates[i]->old_target); + free((void *)transaction->updates[i]->new_target); free(transaction->updates[i]); } free(transaction->updates); @@ -1247,9 +1249,13 @@ struct ref_update *ref_transaction_add_update( update->flags = flags; - if (flags & REF_HAVE_NEW) + if (new_target) + update->new_target = xstrdup(new_target); + if (old_target) + update->old_target = xstrdup(old_target); + if (new_oid && flags & REF_HAVE_NEW) oidcpy(&update->new_oid, new_oid); - if (flags & REF_HAVE_OLD) + if (old_oid && flags & REF_HAVE_OLD) oidcpy(&update->old_oid, old_oid); update->msg = normalize_reflog_message(msg); return update; @@ -1286,6 +1292,7 @@ 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, new_target, @@ -1325,14 +1332,17 @@ int ref_transaction_delete(struct ref_transaction *transaction, int ref_transaction_verify(struct ref_transaction *transaction, const char *refname, const struct object_id *old_oid, + const char *old_target, unsigned int flags, struct strbuf *err) { - if (!old_oid) - BUG("verify called with old_oid set to NULL"); + if (!old_target && !old_oid) + BUG("verify called with old_oid and old_target set to NULL"); + if (old_target && !(flags & REF_NO_DEREF)) + BUG("verify cannot operate on symrefs with deref mode"); return ref_transaction_update(transaction, refname, NULL, old_oid, - NULL, NULL, + NULL, old_target, flags, NULL, err); } @@ -2349,6 +2359,12 @@ static int run_transaction_hook(struct ref_transaction *transaction, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; + /* + * Skip reference transaction for symbolic refs. + */ + if (update->new_target || update->old_target) + continue; + strbuf_reset(&buf); strbuf_addf(&buf, "%s %s %s\n", oid_to_hex(&update->old_oid), @@ -2802,3 +2818,7 @@ 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); } + +int ref_update_is_null_new_value(struct ref_update *update) { + return !update->new_target && is_null_oid(&update->new_oid); +} diff --git a/refs.h b/refs.h index c792e13a64..27b9aeaf54 100644 --- a/refs.h +++ b/refs.h @@ -780,6 +780,7 @@ int ref_transaction_delete(struct ref_transaction *transaction, int ref_transaction_verify(struct ref_transaction *transaction, const char *refname, const struct object_id *old_oid, + const char *old_target, unsigned int flags, struct strbuf *err); diff --git a/refs/files-backend.c b/refs/files-backend.c index 2420dac2aa..53197fa3af 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2425,6 +2425,37 @@ static const char *original_update_refname(struct ref_update *update) return update->refname; } +/* + * Check whether the REF_HAVE_OLD and old_target values stored in + * update are consistent with ref, which is the symbolic reference's + * current value. If everything is OK, return 0; otherwise, write an + * error message to err and return -1. + */ +static int check_old_target(struct ref_update *update, char *ref, + struct strbuf *err) +{ + if (!(update->flags & REF_HAVE_OLD) || + !strcmp(update->old_target, ref)) + return 0; + + if (!strcmp(update->old_target, "")) + strbuf_addf(err, "cannot lock ref '%s': " + "reference already exists", + original_update_refname(update)); + else if (!strcmp(ref, "")) + strbuf_addf(err, "cannot lock ref '%s': " + "reference is missing but expected %s", + original_update_refname(update), + update->old_target); + else + strbuf_addf(err, "cannot lock ref '%s': " + "is at %s but expected %s", + original_update_refname(update), + ref, update->old_target); + + return -1; +} + /* * 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 @@ -2528,6 +2559,18 @@ static int lock_ref_for_update(struct files_ref_store *refs, ret = TRANSACTION_GENERIC_ERROR; goto out; } + } + + /* + * For symref verification, we need to check the reference value + * rather than the oid. If we're dealing with regular refs or we're + * verifying a dereferenced symref, we then check the oid. + */ + if (update->old_target) { + if (check_old_target(update, referent.buf, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } } else if (check_old_oid(update, &lock->old_oid, err)) { ret = TRANSACTION_GENERIC_ERROR; goto out; diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 3040d4797c..23e65f65e8 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -748,4 +748,11 @@ 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); +/* + * 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_is_null_new_value(struct ref_update *update); + #endif /* REFS_REFS_INTERNAL_H */ diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 6104471199..a2474245aa 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -938,7 +938,26 @@ 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->flags & REF_HAVE_OLD) && u->old_target) { + if (strcmp(referent.buf, u->old_target)) { + if (!strcmp(u->old_target, "")) + strbuf_addf(err, "verifying symref target: '%s': " + "provided target is empty", + original_update_refname(u)); + else if (!strcmp(referent.buf, "")) + strbuf_addf(err, "verifying symref target: '%s': " + "reference is missing but expected %s", + original_update_refname(u), + u->old_target); + else + strbuf_addf(err, "verifying symref target: '%s': " + "is at %s but expected %s", + original_update_refname(u), + referent.buf, u->old_target); + 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"), diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index ec3443cc87..34b29eeac8 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -890,17 +890,23 @@ test_expect_success 'stdin update/create/verify combination works' ' ' test_expect_success 'stdin verify succeeds for correct value' ' + test-tool ref-store main for-each-reflog-ent $m >before && git rev-parse $m >expect && echo "verify $m $m" >stdin && git update-ref --stdin actual && - test_cmp expect actual + test_cmp expect actual && + test-tool ref-store main for-each-reflog-ent $m >after && + test_cmp before after ' test_expect_success 'stdin verify succeeds for missing reference' ' + test-tool ref-store main for-each-reflog-ent $m >before && echo "verify refs/heads/missing $Z" >stdin && git update-ref --stdin after && + test_cmp before after ' test_expect_success 'stdin verify treats no value as missing' ' @@ -1641,4 +1647,87 @@ test_expect_success PIPE 'transaction flushes status updates' ' test_cmp expected actual ' +create_stdin_buf () { + if test "$1" = "-z" + then + shift + printf "$F" "$@" >stdin + else + echo "$@" >stdin + fi +} + +for type in "" "-z" +do + + test_expect_success "stdin ${type} symref-verify fails without --no-deref" ' + git symbolic-ref refs/heads/symref $a && + create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" && + test_must_fail git update-ref --stdin ${type} err && + grep "fatal: symref-verify: cannot operate with deref mode" err + ' + + test_expect_success "stdin ${type} symref-verify fails with too many arguments" ' + create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" "$a" && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: $a" err + else + grep "fatal: symref-verify refs/heads/symref: extra input: $a" err + fi + ' + + test_expect_success "stdin ${type} symref-verify succeeds for correct value" ' + git symbolic-ref refs/heads/symref >expect && + test-tool ref-store main for-each-reflog-ent refs/heads/symref >before && + create_stdin_buf ${type} "symref-verify refs/heads/symref" "$a" && + git update-ref --stdin ${type} --no-deref actual && + test_cmp expect actual && + test-tool ref-store main for-each-reflog-ent refs/heads/symref >after && + test_cmp before after + ' + + test_expect_success "stdin ${type} symref-verify no value is treated as zero value" ' + git symbolic-ref refs/heads/symref >expect && + create_stdin_buf ${type} "symref-verify refs/heads/symref" "" && + test_must_fail git update-ref --stdin ${type} --no-deref before && + create_stdin_buf ${type} "symref-verify refs/heads/missing" "$Z" && + git update-ref --stdin ${type} --no-deref after && + test_cmp before after + ' + + test_expect_success "stdin ${type} symref-verify fails for wrong value" ' + git symbolic-ref refs/heads/symref >expect && + create_stdin_buf ${type} "symref-verify refs/heads/symref" "$b" && + test_must_fail git update-ref --stdin ${type} --no-deref actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-verify fails for mistaken null value" ' + git symbolic-ref refs/heads/symref >expect && + create_stdin_buf ${type} "symref-verify refs/heads/symref" "$Z" && + test_must_fail git update-ref --stdin ${type} --no-deref actual && + test_cmp expect actual + ' + +done + test_done From patchwork Fri Apr 26 15:24:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13644977 Received: from mail-ed1-f44.google.com (mail-ed1-f44.google.com [209.85.208.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1B2D5148FF5 for ; Fri, 26 Apr 2024 15:24:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145101; cv=none; b=RZXhaQSlV6I0+9YJ7TR808tT2YIFJcq22fQ2aSVzAVlQDwEbOAqJEuCXwotKbB9FFJtreeICqXax//ziYRSLlSGcEaZ7+pc+Vm0T6PicevAQvULHGS5yKWVYoVvSIkfWr0YSNvhgq9Ro1IRFdL9VlKvPSkVfIh7OoS1a3zBzXTc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145101; c=relaxed/simple; bh=ojZCFCCg/p2r5tzHg/951PaPwVV551q3LIc1mco4WVg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=in9zXwg+MpcDC3uZIqqrlwJyhctrLlaI0gvusU5GQjj3dp+YkY9thnxF8guWIghTjsD7TK2Kq15MiU1r8ezQiad5doyXcTVZvRbJXBq2s3/024pmE+/mgPtTtK7J28nrop/jtlAnHfjqORk05/hc6b1iEbhtoSeA4/mSy6MUI8E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=P/rc2rV2; arc=none smtp.client-ip=209.85.208.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="P/rc2rV2" Received: by mail-ed1-f44.google.com with SMTP id 4fb4d7f45d1cf-5723edf0ae5so2498742a12.0 for ; Fri, 26 Apr 2024 08:24:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1714145097; x=1714749897; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=0qc1s/0+vVCSXI+Df4NlIfI3ORvyO6XaoSEzjPE8qS4=; b=P/rc2rV2/JqWbKTiJfgKXXtzF2MTJHt21n47mzX4YLpsn2Cv3RxbObycH//EXpZU98 +Y42uoFe+hoR4or0SPDO4fpdL3UlCKtkQftEHl6r5jm79cxtpG7FnpsVpTzQPLUYbv7T L9ugzjwHtaH+PvCvLghbsG/XFspTBmKNIHIhucqeNJVVJhdiHF8TXI/36sS2UakND+zM 1pmovdUvq6Gnv9lyTvusfTyWJPF2KrHofnhZLQuYMr9P0ulfkAdYBbridVzLBmSxa/HW pJoZSSVGbDVy7bx5YnkvyaLXfiIjLwlcc/UCmMLOefeG8QWquCOSxY9ov7eVjQF9D2bb T1Ng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714145097; x=1714749897; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=0qc1s/0+vVCSXI+Df4NlIfI3ORvyO6XaoSEzjPE8qS4=; b=ifhoRsxAUJqfYDzWhq4s1QuQGaXxrWhOLese4nCwbXPMdngWYVpru6Euq1pcL8PET6 SqR4m94EFVyBMw3l6ZC/Kka19n0moo447FO+hdACIxYqu92iv0p87RcPluWcyayjc482 ic5K1rTcEvcu02KYsj6ouLhKvW8WotpF5+JAeWm5cNeWU7CQFIQN7YGudvRE9hWTRWpI r11sHYJoVmw04hI1lj4EerOhJYuBLZMq2yGKrZZTOg+1AvQK7D+D3/Rw1ifKMgBF5V4O de5ip7uz55GzHU7/vYMjvmXio/52XR93/vBqqPbl7hjS9kyvlxTWxWf546dtVXIEiJHu 6Lmw== X-Forwarded-Encrypted: i=1; AJvYcCV2B8jzP1TTSr9U/t86mIg01uGPszEchpMUQkmaVV1KmX/c+C1jN1/uRRfm7222snO7FNB8qnD6AedX7B1vpomHeob3 X-Gm-Message-State: AOJu0YwPG9a9TU7v8JMVx+5Sr8st2jhOyGlfuYcHPMC79D/CvzWhmQwf z2R0/FU9Bha+n7s2nDbVJBcGLc4arSYPCGX0hqX7OOFdWQ0nJddRPorrVg== X-Google-Smtp-Source: AGHT+IH/4nEeusEfi08Poq9z0cnmoKiyUunR2LoQblysKmMCXt28lE3n2cC39P8+cg/B+vy0egYz2Q== X-Received: by 2002:a17:906:13d3:b0:a55:61cc:7337 with SMTP id g19-20020a17090613d300b00a5561cc7337mr2294163ejc.43.1714145097314; Fri, 26 Apr 2024 08:24:57 -0700 (PDT) Received: from localhost.localdomain ([185.223.147.210]) by smtp.gmail.com with ESMTPSA id z6-20020a170906434600b00a518c69c4e3sm10643868ejm.23.2024.04.26.08.24.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 08:24:56 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: christian.couder@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v4 4/7] update-ref: add support for 'symref-delete' command Date: Fri, 26 Apr 2024 17:24:46 +0200 Message-ID: <20240426152449.228860-5-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240426152449.228860-1-knayak@gitlab.com> References: <20240423212818.574123-1-knayak@gitlab.com> <20240426152449.228860-1-knayak@gitlab.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Karthik Nayak Add a new command 'symref-delete' to allow deletions of symbolic refs in a transaction via the '--stdin' mode of the 'git-update-ref' command. The 'symref-delete' command can, when given an , delete the provided only when it points to . This will only work when used with the 'no-deref' mode as it doesn't make sense to deref a symref during deletion. Signed-off-by: Karthik Nayak --- Documentation/git-update-ref.txt | 11 ++++-- builtin/fetch.c | 2 +- builtin/receive-pack.c | 3 +- builtin/update-ref.c | 33 ++++++++++++++++- refs.c | 12 ++++--- refs.h | 4 ++- refs/files-backend.c | 2 +- refs/reftable-backend.c | 2 +- t/t1400-update-ref.sh | 61 +++++++++++++++++++++++++++++++- 9 files changed, 117 insertions(+), 13 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 9fe78b3501..2924b9437e 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -65,6 +65,7 @@ performs all modifications together. Specify commands of the form: create SP SP LF delete SP [SP ] LF verify SP [SP ] LF + symref-delete SP [SP ] LF symref-verify SP [SP ] LF option SP LF start LF @@ -87,6 +88,7 @@ quoting: create SP NUL NUL delete SP NUL [] NUL verify SP NUL [] NUL + symref-delete SP [NUL ] NUL symref-verify SP [NUL ] NUL option SP NUL start NUL @@ -112,13 +114,18 @@ create:: exist. The given may not be zero. delete:: - Delete after verifying it exists with , if - given. If given, may not be zero. + Delete after verifying it exists with , if given. + If given, may not be zero. If instead, ref: + is provided, verify that the symbolic ref targets + before deleting it. verify:: Verify against but do not change it. If is zero or missing, the ref must not exist. +symref-delete:: + Delete after verifying it exists with , if given. + symref-verify:: Verify symbolic against but do not change it. If is missing, the ref must not exist. Can only be diff --git a/builtin/fetch.c b/builtin/fetch.c index 66840b7c5b..d02592efca 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1383,7 +1383,7 @@ static int prune_refs(struct display_state *display_state, if (transaction) { for (ref = stale_refs; ref; ref = ref->next) { result = ref_transaction_delete(transaction, ref->name, NULL, 0, - "fetch: prune", &err); + NULL, "fetch: prune", &err); if (result) goto cleanup; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index b150ef39a8..9a4667d57d 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1576,7 +1576,8 @@ static const char *update(struct command *cmd, struct shallow_info *si) if (ref_transaction_delete(transaction, namespaced_name, old_oid, - 0, "push", &err)) { + 0, NULL, + "push", &err)) { rp_error("%s", err.buf); ret = "failed to delete"; } else { diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 419b28169b..8fef3aed0a 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -293,7 +293,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction, if (ref_transaction_delete(transaction, refname, have_old ? &old_oid : NULL, - update_flags, msg, &err)) + update_flags, NULL, msg, &err)) die("%s", err.buf); update_flags = default_flags; @@ -301,6 +301,36 @@ static void parse_cmd_delete(struct ref_transaction *transaction, strbuf_release(&err); } + +static void parse_cmd_symref_delete(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf err = STRBUF_INIT; + char *refname, *old_target; + + if (!(update_flags & REF_NO_DEREF)) + die("symref-delete: cannot operate with deref mode"); + + refname = parse_refname(&next); + if (!refname) + die("symref-delete: missing "); + + old_target = parse_next_refname(&next); + + if (*next != line_termination) + die("symref-delete %s: extra input: %s", refname, next); + + if (ref_transaction_delete(transaction, refname, NULL, + update_flags, old_target, msg, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + free(old_target); + strbuf_release(&err); +} + + static void parse_cmd_verify(struct ref_transaction *transaction, const char *next, const char *end) { @@ -443,6 +473,7 @@ static const struct parse_cmd { { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, + { "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN }, { "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN }, { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, diff --git a/refs.c b/refs.c index 0e1013b5ab..6b7c46bfd8 100644 --- a/refs.c +++ b/refs.c @@ -979,7 +979,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg, transaction = ref_store_transaction_begin(refs, &err); if (!transaction || ref_transaction_delete(transaction, refname, old_oid, - flags, msg, &err) || + flags, NULL, msg, &err) || ref_transaction_commit(transaction, &err)) { error("%s", err.buf); ref_transaction_free(transaction); @@ -1318,14 +1318,18 @@ int ref_transaction_create(struct ref_transaction *transaction, int ref_transaction_delete(struct ref_transaction *transaction, const char *refname, const struct object_id *old_oid, - unsigned int flags, const char *msg, + unsigned int flags, + const char *old_target, + const char *msg, struct strbuf *err) { if (old_oid && is_null_oid(old_oid)) BUG("delete called with old_oid set to zeros"); + if (old_target && !(flags & REF_NO_DEREF)) + BUG("delete cannot operate on symrefs with deref mode"); return ref_transaction_update(transaction, refname, null_oid(), old_oid, - NULL, NULL, flags, + NULL, old_target, flags, msg, err); } @@ -2752,7 +2756,7 @@ int refs_delete_refs(struct ref_store *refs, const char *logmsg, for_each_string_list_item(item, refnames) { ret = ref_transaction_delete(transaction, item->string, - NULL, flags, msg, &err); + NULL, flags, NULL, msg, &err); if (ret) { warning(_("could not delete reference %s: %s"), item->string, err.buf); diff --git a/refs.h b/refs.h index 27b9aeaf54..4be4930f04 100644 --- a/refs.h +++ b/refs.h @@ -766,7 +766,9 @@ int ref_transaction_create(struct ref_transaction *transaction, int ref_transaction_delete(struct ref_transaction *transaction, const char *refname, const struct object_id *old_oid, - unsigned int flags, const char *msg, + unsigned int flags, + const char *old_target, + const char *msg, struct strbuf *err); /* diff --git a/refs/files-backend.c b/refs/files-backend.c index 53197fa3af..fc5037fe5a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2516,7 +2516,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_is_null_new_value(update)) update->flags |= REF_DELETING; if (head_ref) { diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index a2474245aa..2b2cbca8c0 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -1120,7 +1120,7 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data if (u->flags & REF_LOG_ONLY) continue; - if (u->flags & REF_HAVE_NEW && is_null_oid(&u->new_oid)) { + if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) { struct reftable_ref_record ref = { .refname = (char *)u->refname, .update_index = ts, diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 34b29eeac8..8efddac013 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1689,7 +1689,7 @@ do test_cmp before after ' - test_expect_success "stdin ${type} symref-verify no value is treated as zero value" ' + test_expect_success "stdin ${type} symref-verify fails with no value" ' git symbolic-ref refs/heads/symref >expect && create_stdin_buf ${type} "symref-verify refs/heads/symref" "" && test_must_fail git update-ref --stdin ${type} --no-deref err && + grep "fatal: symref-delete: cannot operate with deref mode" err + ' + + test_expect_success "stdin ${type} symref-delete fails with no ref" ' + create_stdin_buf ${type} "symref-delete " && + test_must_fail git update-ref --stdin ${type} --no-deref err && + grep "fatal: symref-delete: missing " err + ' + + test_expect_success "stdin ${type} symref-delete fails with too many arguments" ' + create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" "$a" && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: $a" err + else + grep "fatal: symref-delete refs/heads/symref: extra input: $a" err + fi + ' + + test_expect_success "stdin ${type} symref-delete fails with wrong old value" ' + create_stdin_buf ${type} "symref-delete refs/heads/symref" "$m" && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test_have_prereq REFTABLE + then + grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected refs/heads/main" err + else + grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}" err + fi && + git symbolic-ref refs/heads/symref >expect && + echo $a >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-delete works with right old value" ' + create_stdin_buf ${type} "symref-delete refs/heads/symref" "$a" && + git update-ref --stdin ${type} --no-deref X-Patchwork-Id: 13644978 Received: from mail-lf1-f53.google.com (mail-lf1-f53.google.com [209.85.167.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 74D87149C72 for ; Fri, 26 Apr 2024 15:25:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145102; cv=none; b=dc9OOGsGmL6uAlfWzTvF1hP1ujBWTErA7KnThgxMtpCIdJ7+MEO+IPvm6dD1WqvZHGr7g74o+426Fj7U2iD2r7REvgLaj/0EjBObAJYjTAf6+oES59F9CxQ55Oybe45wjQswR2JzfN42L9UnEzL85TxNyuqVC6i+1Yk52JEjT5A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145102; c=relaxed/simple; bh=dK/cwbzERywXwb6jmo2f6GlObeiI2M9bOX6d5J+NWq8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tZ7VKO06w3GndEtSwNBRhLQsp/mbBL+uAp7BAGlAFpNzWCiNhoLuqHkG1i2xz7S7SYbh2Lmq2skNlRGnVdwfdr8skWMYrXzPTpY7nUG7s0RlqW8+GtVjgbU0gJ4DjqCWfjxOElbg569fIJ1kL+ZlGJ3wOFRpVcKmRG7+N2OEVwc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=cVpjteXn; arc=none smtp.client-ip=209.85.167.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cVpjteXn" Received: by mail-lf1-f53.google.com with SMTP id 2adb3069b0e04-516d2b9cd69so2770238e87.2 for ; Fri, 26 Apr 2024 08:25:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1714145098; x=1714749898; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7LY7oDpRu4UrAgKcse5WCaaw5NZyqQScLMwqQxTFFsk=; b=cVpjteXnLwBt+7M0vZbH0PEaVWlGL95DbGkGfNkgfyZ/JDDnjLUQCI+EBhrbDHTOmW uYcDfjW75N2EaH4E09Kx/VkPggfCl6X3qtolKthruLL/seOyZFWEqDEDEcvrjopJfn0+ bmEdrmP/nVaamgjXrr+O2FB3bUGRqm1qBSt4ItUd8BHd4vG2Fs7jY17KotrD0PSvUjXq DvN+1eVoEIWcZRR915mEg7c2xgYs1ALosSIbyUIzB0+nN431t4ibLXbQbuemWbffDDK5 XWA/6eUJE8BZwrJYAXRiSL3qPSHIViflHMQoVXwcRvsklJrhU2nIHevDZncdlhR+HRMA QIqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714145098; x=1714749898; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7LY7oDpRu4UrAgKcse5WCaaw5NZyqQScLMwqQxTFFsk=; b=A86ST1CWeI1iwsXXJEzr2QVFDCI5Tk2tp9OyF4JfbZ+YPXc85qg8AcY+UyUmBm6WBu HpIEKTY8YKlDuCOFsLlf85jCdr0YOn7cjP6ra2iYZP2Ic7tY4/G4elIzkKY4s78NcM8f 7iZYPYCOcPz1fodMuKFeuUyJkCdK71xRNOMr6lQN3Inlgbzb3/+f6J1qB0q79hFfMF7K 1dKC3/AY2F+yz/I9eX9MW9bPUmFREPjb8Iumtyn7MggYVW9oc7ejSKq8ZsIiM63kAofK BdBrHpD31KJT1/Ck5bSHc2NKQmOD+0OZGsBULf6Sn7PhkmwJoL66DcoGgCEJ2x9DbbXq WXWQ== X-Forwarded-Encrypted: i=1; AJvYcCXHgFQezEecYiuYTbptZRtTVw4D0InZSQUUT41iG5srMckkgkCeqiPCkDWWtdR8Rhmdx334aTuieIzEf+0PztiX+tok X-Gm-Message-State: AOJu0YxaLQTyJNv7wiKzOnw7KIWCTHNZbHTeY0L/cvguzlsCD9veIt8/ ZCkt4OTLdRfZQ3ZcZZPXLGBEQT77m/ZatPmYQ/tYvybWA5eBJ62m X-Google-Smtp-Source: AGHT+IHT4ZDa8eFi6SVej5dKYumWsKpF9uK81u7oCdWB11l+PUVK72fT5Cp0DRJ0ld1SCbUisHrHug== X-Received: by 2002:ac2:4884:0:b0:517:8ad8:c64 with SMTP id x4-20020ac24884000000b005178ad80c64mr1860937lfc.21.1714145098422; Fri, 26 Apr 2024 08:24:58 -0700 (PDT) Received: from localhost.localdomain ([185.223.147.210]) by smtp.gmail.com with ESMTPSA id z6-20020a170906434600b00a518c69c4e3sm10643868ejm.23.2024.04.26.08.24.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 08:24:57 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: christian.couder@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v4 5/7] update-ref: add support for 'symref-create' command Date: Fri, 26 Apr 2024 17:24:47 +0200 Message-ID: <20240426152449.228860-6-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240426152449.228860-1-knayak@gitlab.com> References: <20240423212818.574123-1-knayak@gitlab.com> <20240426152449.228860-1-knayak@gitlab.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Karthik Nayak Add 'symref-create' command to the '--stdin' mode 'git-update-ref' to allow creation of symbolic refs in a transaction. The 'symref-create' command takes in a , which the created will point to. Also, support the 'core.prefersymlinkrefs' config, wherein if the config is set and the filesystem supports symlinks, we create the symbolic ref as a symlink. We fallback to creating a regular symref if creating the symlink is unsuccessful. Signed-off-by: Karthik Nayak --- Documentation/git-update-ref.txt | 10 ++++- builtin/clone.c | 2 +- builtin/update-ref.c | 35 ++++++++++++++++- refs.c | 9 +++-- refs.h | 1 + refs/files-backend.c | 42 +++++++++++++++++++++ refs/reftable-backend.c | 23 +++++++++-- t/t0600-reffiles-backend.sh | 32 ++++++++++++++++ t/t1400-update-ref.sh | 65 ++++++++++++++++++++++++++++++++ 9 files changed, 210 insertions(+), 9 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 2924b9437e..7a33f70767 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -65,6 +65,7 @@ performs all modifications together. Specify commands of the form: create SP SP LF delete SP [SP ] LF verify SP [SP ] LF + symref-create SP SP LF symref-delete SP [SP ] LF symref-verify SP [SP ] LF option SP LF @@ -88,6 +89,7 @@ quoting: create SP NUL NUL delete SP NUL [] NUL verify SP NUL [] NUL + symref-create SP NUL NUL symref-delete SP [NUL ] NUL symref-verify SP [NUL ] NUL option SP NUL @@ -111,7 +113,9 @@ update:: create:: Create with after verifying it does not - exist. The given may not be zero. + exist. The given may not be zero. If instead + ref: is provided, a symbolic ref is created + which targets . delete:: Delete after verifying it exists with , if given. @@ -123,6 +127,10 @@ verify:: Verify against but do not change it. If is zero or missing, the ref must not exist. +symref-create:: + Create symbolic ref with after verifying + it does not exist. Can only be used in `no-deref` mode. + symref-delete:: Delete after verifying it exists with , if given. diff --git a/builtin/clone.c b/builtin/clone.c index 74ec14542e..c0eed8e795 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -547,7 +547,7 @@ static void write_remote_refs(const struct ref *local_refs) if (!r->peer_ref) continue; if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid, - 0, NULL, &err)) + NULL, 0, NULL, &err)) die("%s", err.buf); } diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 8fef3aed0a..ae68ffde5e 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -257,7 +257,7 @@ static void parse_cmd_create(struct ref_transaction *transaction, if (*next != line_termination) die("create %s: extra input: %s", refname, next); - if (ref_transaction_create(transaction, refname, &new_oid, + if (ref_transaction_create(transaction, refname, &new_oid, NULL, update_flags | create_reflog_flag, msg, &err)) die("%s", err.buf); @@ -267,6 +267,38 @@ static void parse_cmd_create(struct ref_transaction *transaction, strbuf_release(&err); } + +static void parse_cmd_symref_create(struct ref_transaction *transaction, + const char *next, const char *end) +{ + struct strbuf err = STRBUF_INIT; + char *refname, *new_target; + + if (!(update_flags & REF_NO_DEREF)) + die("symref-create: cannot operate with deref mode"); + + refname = parse_refname(&next); + if (!refname) + die("symref-create: missing "); + + new_target = parse_next_refname(&next); + if (!new_target) + die("symref-create %s: missing ", refname); + + if (*next != line_termination) + die("symref-create %s: extra input: %s", refname, next); + + if (ref_transaction_create(transaction, refname, NULL, new_target, + update_flags | create_reflog_flag, + msg, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + free(new_target); + strbuf_release(&err); +} + static void parse_cmd_delete(struct ref_transaction *transaction, const char *next, const char *end) { @@ -473,6 +505,7 @@ static const struct parse_cmd { { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, + { "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN }, { "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN }, { "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN }, { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, diff --git a/refs.c b/refs.c index 6b7c46bfd8..42cb4126a7 100644 --- a/refs.c +++ b/refs.c @@ -1303,15 +1303,18 @@ int ref_transaction_update(struct ref_transaction *transaction, int ref_transaction_create(struct ref_transaction *transaction, const char *refname, const struct object_id *new_oid, + const char *new_target, unsigned int flags, const char *msg, struct strbuf *err) { - if (!new_oid || is_null_oid(new_oid)) { - strbuf_addf(err, "'%s' has a null OID", refname); + if ((!new_oid || is_null_oid(new_oid)) && !new_target) { + strbuf_addf(err, "'%s' has a null OID or no new target", refname); return 1; } + if (new_target && !(flags & REF_NO_DEREF)) + BUG("create cannot operate on symrefs with deref mode"); return ref_transaction_update(transaction, refname, new_oid, - null_oid(), NULL, NULL, flags, + null_oid(), new_target, NULL, flags, msg, err); } diff --git a/refs.h b/refs.h index 4be4930f04..bde8606213 100644 --- a/refs.h +++ b/refs.h @@ -752,6 +752,7 @@ int ref_transaction_update(struct ref_transaction *transaction, int ref_transaction_create(struct ref_transaction *transaction, const char *refname, const struct object_id *new_oid, + const char *new_target, unsigned int flags, const char *msg, struct strbuf *err); diff --git a/refs/files-backend.c b/refs/files-backend.c index fc5037fe5a..f5e271a442 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2610,6 +2610,27 @@ static int lock_ref_for_update(struct files_ref_store *refs, } } + if (update->new_target) { + if (create_symref_lock(refs, lock, update->refname, update->new_target)) { + 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; + } + + if ((update->flags & REF_HAVE_NEW) && !(update->flags & REF_DELETING) && !(update->flags & REF_LOG_ONLY)) { @@ -2905,6 +2926,18 @@ static int files_transaction_finish(struct ref_store *ref_store, if (update->flags & REF_NEEDS_COMMIT || update->flags & REF_LOG_ONLY) { + 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)) { + /* for dangling symrefs we gracefully set the oid to zero */ + update->new_oid = *null_oid(); + } + } + if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid, @@ -2922,6 +2955,15 @@ static int files_transaction_finish(struct ref_store *ref_store, goto cleanup; } } + + /* + * We try creating a symlink, if that succeeds we continue to the + * next updated. 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)) { diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 2b2cbca8c0..e203c697f2 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -856,7 +856,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_is_null_new_value(u)) { ret = queue_transaction_update(refs, tx_data, u, ¤t_oid, err); if (ret) @@ -1062,7 +1062,7 @@ 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_is_null_new_value(u)) { struct reftable_log_record log = {0}; struct reftable_iterator it = {0}; @@ -1104,6 +1104,12 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data should_write_log(&arg->refs->base, u->refname))) { struct reftable_log_record *log; + if (u->new_target) + if (!refs_resolve_ref_unsafe(&arg->refs->base, u->new_target, + RESOLVE_REF_READING, &u->new_oid, NULL)) + /* for dangling symrefs we gracefully set the oid to zero */ + u->new_oid = *null_oid(); + ALLOC_GROW(logs, logs_nr + 1, logs_alloc); log = &logs[logs_nr++]; memset(log, 0, sizeof(*log)); @@ -1120,7 +1126,18 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data if (u->flags & REF_LOG_ONLY) continue; - if (u->flags & REF_HAVE_NEW && ref_update_is_null_new_value(u)) { + if (u->flags & REF_HAVE_NEW && 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_is_null_new_value(u)) { struct reftable_ref_record ref = { .refname = (char *)u->refname, .update_index = ts, diff --git a/t/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh index 64214340e7..c5061c26cf 100755 --- a/t/t0600-reffiles-backend.sh +++ b/t/t0600-reffiles-backend.sh @@ -472,4 +472,36 @@ test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' ' esac ' +test_expect_success SYMLINKS 'symref transaction supports symlinks' ' + test_when_finished "git symbolic-ref -d TESTSYMREFONE" && + git update-ref refs/heads/new @ && + test_config core.prefersymlinkrefs true && + cat >stdin <<-EOF && + start + symref-create TESTSYMREFONE refs/heads/new + prepare + commit + EOF + git update-ref --no-deref --stdin stdin <<-EOF && + start + symref-create TESTSYMREFONE refs/heads/new + prepare + commit + EOF + git update-ref --no-deref --stdin actual && + echo refs/heads/new >expect && + test_cmp expect actual +' + test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 8efddac013..452fc1da50 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1787,6 +1787,71 @@ do test_must_fail git symbolic-ref -d refs/heads/symref2 ' + test_expect_success "stdin ${type} symref-create fails without --no-deref" ' + create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" && + test_must_fail git update-ref --stdin ${type} err && + grep "fatal: symref-create: cannot operate with deref mode" err + ' + + test_expect_success "stdin ${type} symref-create fails with too many arguments" ' + create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" "$a" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: $a" err + else + grep "fatal: symref-create refs/heads/symref: extra input: $a" err + fi + ' + + test_expect_success "stdin ${type} symref-create fails with no target" ' + create_stdin_buf ${type} "symref-create refs/heads/symref" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref stdin && + test_must_fail git update-ref --stdin ${type} --no-deref stdin && + git update-ref --stdin ${type} --no-deref expect && + echo $a >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} create dangling symref ref works" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "symref-create refs/heads/symref" "refs/heads/unkown" >stdin && + git update-ref --stdin ${type} --no-deref expect && + echo refs/heads/unkown >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-create does not create reflogs by default" ' + test_when_finished "git symbolic-ref -d refs/symref" && + create_stdin_buf ${type} "symref-create refs/symref" "$a" >stdin && + git update-ref --stdin ${type} --no-deref expect && + echo $a >actual && + test_cmp expect actual && + test_must_fail git reflog exists refs/symref + ' + + test_expect_success "stdin ${type} symref-create reflogs with --create-reflog" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "symref-create refs/heads/symref" "$a" >stdin && + git update-ref --create-reflog --stdin ${type} --no-deref expect && + echo $a >actual && + test_cmp expect actual && + git reflog exists refs/heads/symref + ' + done test_done From patchwork Fri Apr 26 15:24:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13644979 Received: from mail-ed1-f44.google.com (mail-ed1-f44.google.com [209.85.208.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 03B0C14D2B8 for ; Fri, 26 Apr 2024 15:25:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145103; cv=none; b=I/yEeSpX1Hg2Y/yVYZZQYbYlV9n/zGhZH7oECW+yhWcTXNlVD2OyrxQMODqkARaJVbPPRag3eJnKvIXq2v8SD7r727/s+2khOZ9Tk/XFUWN7Zas9gFTP829g8oSbJlKYtUazN/k0Maij2flnLQw9qqkkKfVL6F2wZtRkPLtUIOA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145103; c=relaxed/simple; bh=iNIkoEmQvkUJK84bg5g1ZOTuo7h5AvSc+mkvfedZIic=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cw6jtLnQuIXaNRkTIscyBLl2afAqshZcKH9he/6JT/CPFqk7G20aMbbP6s8e1Yw3e3Hl+sC2wsG5UccGXxKlDQgJygRkCGbrYlsvtA8fKF+weIEK+yUBWMjNHAQWrhmoYZ7tO+eTvEfPlDvQGwRRrUjY4yVGUub9Ajw+txfG1DU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GutGBdde; arc=none smtp.client-ip=209.85.208.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GutGBdde" Received: by mail-ed1-f44.google.com with SMTP id 4fb4d7f45d1cf-571bddddbc2so2823747a12.1 for ; Fri, 26 Apr 2024 08:25:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1714145099; x=1714749899; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+xCXqbm+FBh5J/qpYAF+6Pe+dBymrlULxx9Vzq6hlVU=; b=GutGBdde7vQbOSccBnqpu/iXgdAi+6avx8zEPCLeRx5PdBtvuK/NeAxqUqNy1Cz7ML XaBrp4GfEYiBcTRKnXtgH1d/l+R6PFqRHH/j3duVDlvWjfM30+Kr74IXBaRhmkSlzAHY 5c6qJbDsuNwTV54L1fA1ZCEAYPEt0d+ndnjaV7Dp+PSe7qECalGYcu4nJBFxG6TRJ/z8 1lFR755EZ1h8V/s1HUUwd+RASBO3el2oewgTjSoG5L+EKiuOncEyHm9vl6GNX8W8+wSg z3zq+P0QLtDRqOZSZqxRPEESxdGYWK0A3trkQUFya2H5UixsNY3udBoMxA0zNKSyINnG 2tjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714145099; x=1714749899; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+xCXqbm+FBh5J/qpYAF+6Pe+dBymrlULxx9Vzq6hlVU=; b=klCrGWcHmRLxna08X8kRgHNvdOcMBfR7kgKIFL4+EFvs2+ojMFLfSYxqR4FXxFpFzv tau/NPsZEXiMpAcMiKcH/uPJBdgutZyBmoyoV8cWHfjQLzjKkMfIo/vBOamp5DXGnOZ2 +iL4ajSzVI5YTIcoxu4kXWHgJy/0AhWvbTHWretdr8Zv9ru92CcMyS8JeYLtZ0wn2e58 fwPcRpvywXVEqcYJtdT7o1krdqFslSKb5Ah5o2wUtkdp5+EiaV0OVtIskN+1UXRQxda3 1BrtgEQQW+i3uewhqR9dMTaKuUSpTeg+PKqUet3z1LJEh1XVmfnCVY5nYpWAbIMsP+9M oAZw== X-Forwarded-Encrypted: i=1; AJvYcCX4VkT0gJm0oVjdMeMtUXo50KrW2WXQD4kLhPcXgOcj5fbdenTteEGBnbU29CrnOuxpU4iMSQeMPzEgcbXjINl86cKO X-Gm-Message-State: AOJu0YyX2RcYqih1yqKOkeTlejkm+8BvgH1Ky+ghRb7fe/wKqERqI+WE emXs6iLF52tZ32l6DCkOTRRd7coEumiE1HQHv5hDGSL+yS4bOu4S X-Google-Smtp-Source: AGHT+IGt2T9GwQnYPzQgK4OZ7OQBGgN4+K4QL4xp4rhZh4OYCcjp0RBAseV0gjKXbZ8t7ZbpLSP1uw== X-Received: by 2002:a17:906:1c48:b0:a58:9297:91a1 with SMTP id l8-20020a1709061c4800b00a58929791a1mr2225939ejg.53.1714145099252; Fri, 26 Apr 2024 08:24:59 -0700 (PDT) Received: from localhost.localdomain ([185.223.147.210]) by smtp.gmail.com with ESMTPSA id z6-20020a170906434600b00a518c69c4e3sm10643868ejm.23.2024.04.26.08.24.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 08:24:58 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: christian.couder@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v4 6/7] update-ref: add support for 'symref-update' command Date: Fri, 26 Apr 2024 17:24:48 +0200 Message-ID: <20240426152449.228860-7-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240426152449.228860-1-knayak@gitlab.com> References: <20240423212818.574123-1-knayak@gitlab.com> <20240426152449.228860-1-knayak@gitlab.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Karthik Nayak Add 'symref-update' command to the '--stdin' mode of 'git-update-ref' to allow updates of symbolic refs. The 'symref-update' command takes in a , which the will be updated to. If the doesn't exist it will be created. It also optionally takes either an `ref ` or `oid `. If the is provided, it checks to see if the targets the before the update. If is provided it checks to ensure that it is a regular ref and is the OID before the update. This by extension also means that this when a zero is provided, it ensures that the ref didn't exist before. This command will also support deref mode, to ensure that we can update dereferenced regular refs to symrefs. Signed-off-by: Karthik Nayak --- Documentation/git-update-ref.txt | 6 + builtin/update-ref.c | 91 +++++++++++++- refs/files-backend.c | 14 +-- refs/reftable-backend.c | 3 +- t/t1400-update-ref.sh | 196 +++++++++++++++++++++++++++++++ 5 files changed, 299 insertions(+), 11 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 7a33f70767..0cd4c37820 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -65,6 +65,7 @@ performs all modifications together. Specify commands of the form: create SP SP LF delete SP [SP ] LF verify SP [SP ] LF + symref-update SP SP [SP (ref SP | oid SP )] LF symref-create SP SP LF symref-delete SP [SP ] LF symref-verify SP [SP ] LF @@ -89,6 +90,7 @@ quoting: create SP NUL NUL delete SP NUL [] NUL verify SP NUL [] NUL + symref-update SP NUL [NUL (ref NUL | oid NUL )] NUL symref-create SP NUL NUL symref-delete SP [NUL ] NUL symref-verify SP [NUL ] NUL @@ -123,6 +125,10 @@ delete:: is provided, verify that the symbolic ref targets before deleting it. +symref-update:: + Set to after verifying or , + if given. Can be used to delete or create symrefs too. + verify:: Verify against but do not change it. If is zero or missing, the ref must not exist. diff --git a/builtin/update-ref.c b/builtin/update-ref.c index ae68ffde5e..5001d80ae4 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -98,6 +98,41 @@ static char *parse_next_refname(const char **next) return parse_refname(next); } +/* + * Wrapper around parse_arg which skips the next delimiter. + */ +static char *parse_next_arg(const char **next) +{ + struct strbuf arg = STRBUF_INIT; + + if (line_termination) { + /* Without -z, consume SP and use next argument */ + if (!**next || **next == line_termination) + return NULL; + if (**next != ' ') + die("expected SP but got: %s", *next); + } else { + /* With -z, read the next NUL-terminated line */ + if (**next) + return NULL; + } + /* Skip the delimiter */ + (*next)++; + + if (line_termination) { + /* Without -z, use the next argument */ + *next = parse_arg(*next, &arg); + } else { + /* With -z, use everything up to the next NUL */ + strbuf_addstr(&arg, *next); + *next += arg.len; + } + + if (arg.len) + return strbuf_detach(&arg, NULL); + return NULL; +} + /* * The value being parsed is (as opposed to ; the @@ -225,8 +260,8 @@ static void parse_cmd_update(struct ref_transaction *transaction, if (*next != line_termination) die("update %s: extra input: %s", refname, next); - if (ref_transaction_update(transaction, refname, - &new_oid, have_old ? &old_oid : NULL, + if (ref_transaction_update(transaction, refname, &new_oid, + have_old ? &old_oid : NULL, NULL, NULL, update_flags | create_reflog_flag, msg, &err)) @@ -237,6 +272,57 @@ static void parse_cmd_update(struct ref_transaction *transaction, strbuf_release(&err); } +static void parse_cmd_symref_update(struct ref_transaction *transaction, + const char *next, const char *end) +{ + char *refname, *new_target, *old_arg; + char *old_target = NULL; + struct strbuf err = STRBUF_INIT; + struct object_id old_oid; + int have_old = 0; + + refname = parse_refname(&next); + if (!refname) + die("symref-update: missing "); + + new_target = parse_next_refname(&next); + if (!new_target) + die("symref-update %s: missing ", refname); + + old_arg = parse_next_arg(&next); + if (old_arg) { + old_target = parse_next_refname(&next); + if (!old_target) + die("symref-update %s: expected old value", refname); + + if (!strcmp(old_arg, "oid") && + !repo_get_oid(the_repository, old_target, &old_oid)) { + old_target = NULL; + have_old = 1; + } else if (strcmp(old_arg, "ref")) + die("symref-update %s: invalid arg '%s' for old value", refname, old_arg); + } + + + if (*next != line_termination) + die("symref-update %s: extra input: %s", refname, next); + + + if (ref_transaction_update(transaction, refname, NULL, + have_old ? &old_oid : NULL, + new_target, old_target, + update_flags |= create_reflog_flag, + msg, &err)) + die("%s", err.buf); + + update_flags = default_flags; + free(refname); + free(old_arg); + free(old_target); + free(new_target); + strbuf_release(&err); +} + static void parse_cmd_create(struct ref_transaction *transaction, const char *next, const char *end) { @@ -505,6 +591,7 @@ static const struct parse_cmd { { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, + { "symref-update", parse_cmd_symref_update, 4, UPDATE_REFS_OPEN }, { "symref-create", parse_cmd_symref_create, 2, UPDATE_REFS_OPEN }, { "symref-delete", parse_cmd_symref_delete, 2, UPDATE_REFS_OPEN }, { "symref-verify", parse_cmd_symref_verify, 2, UPDATE_REFS_OPEN }, diff --git a/refs/files-backend.c b/refs/files-backend.c index f5e271a442..59d1ab3eeb 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2386,7 +2386,8 @@ 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, - NULL, NULL, update->msg); + update->new_target, update->old_target, + update->msg); new_update->parent_update = update; @@ -2610,7 +2611,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, } } - if (update->new_target) { + if (update->new_target && !(update->flags & REF_LOG_ONLY)) { if (create_symref_lock(refs, lock, update->refname, update->new_target)) { ret = TRANSACTION_GENERIC_ERROR; goto out; @@ -2628,12 +2629,9 @@ static int lock_ref_for_update(struct files_ref_store *refs, * phase of the transaction only needs to commit the lock. */ update->flags |= REF_NEEDS_COMMIT; - } - - - if ((update->flags & REF_HAVE_NEW) && - !(update->flags & REF_DELETING) && - !(update->flags & REF_LOG_ONLY)) { + } 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)) { /* diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index e203c697f2..a00f55802a 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -908,7 +908,8 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store, */ new_update = ref_transaction_add_update( transaction, referent.buf, new_flags, - &u->new_oid, &u->old_oid, NULL, NULL, u->msg); + &u->new_oid, &u->old_oid, u->new_target, + u->old_target, u->msg); new_update->parent_update = u; /* diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 452fc1da50..3cfef9fbe3 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1360,6 +1360,7 @@ test_expect_success 'fails with duplicate HEAD update' ' ' test_expect_success 'fails with duplicate ref update via symref' ' + test_when_finished "git symbolic-ref -d refs/heads/symref2" && git branch target2 $A && git symbolic-ref refs/heads/symref2 refs/heads/target2 && cat >stdin <<-EOF && @@ -1852,6 +1853,201 @@ do git reflog exists refs/heads/symref ' + test_expect_success "stdin ${type} symref-update fails with too many arguments" ' + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" "ref" "$a" "$a" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: $a" err + else + grep "fatal: symref-update refs/heads/symref: extra input: $a" err + fi + ' + + test_expect_success "stdin ${type} symref-update fails with wrong old value argument" ' + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" "foo" "$a" "$a" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref err && + grep "fatal: symref-update refs/heads/symref: invalid arg ${SQ}foo${SQ} for old value" err + ' + + test_expect_success "stdin ${type} symref-update creates with zero old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update creates with no old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update creates dangling" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + test_must_fail git rev-parse refs/heads/nonexistent && + create_stdin_buf ${type} "symref-update refs/heads/symref" "refs/heads/nonexistent" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update fails with wrong old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + git symbolic-ref refs/heads/symref $a && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$m" "ref" "$b" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test_have_prereq REFTABLE + then + grep "fatal: verifying symref target: ${SQ}refs/heads/symref${SQ}: is at $a but expected $b" err + else + grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}: is at $a but expected $b" err + fi && + test_must_fail git rev-parse --verify -q $c + ' + + test_expect_success "stdin ${type} symref-update updates dangling ref" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + test_must_fail git rev-parse refs/heads/nonexistent && + git symbolic-ref refs/heads/symref refs/heads/nonexistent && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update updates dangling ref with old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + test_must_fail git rev-parse refs/heads/nonexistent && + git symbolic-ref refs/heads/symref refs/heads/nonexistent && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" "ref" "refs/heads/nonexistent" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update fails update dangling ref with wrong old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + test_must_fail git rev-parse refs/heads/nonexistent && + git symbolic-ref refs/heads/symref refs/heads/nonexistent && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" "ref" "refs/heads/wrongref" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update works with right old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + git symbolic-ref refs/heads/symref $a && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$m" "ref" "$a" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update works with no old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + git symbolic-ref refs/heads/symref $a && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$m" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update fails with empty old ref-target" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + git symbolic-ref refs/heads/symref $a && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$m" "ref" "" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update creates (with deref)" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" >stdin && + git update-ref --stdin ${type} expect && + git symbolic-ref --no-recurse refs/heads/symref >actual && + test_cmp expect actual && + test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual && + grep "$Z $(git rev-parse $a)" actual + ' + + test_expect_success "stdin ${type} symref-update regular ref to symref with correct old-oid" ' + test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" && + git update-ref --no-deref refs/heads/regularref $a && + create_stdin_buf ${type} "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse $a)" >stdin && + git update-ref --stdin ${type} expect && + git symbolic-ref --no-recurse refs/heads/regularref >actual && + test_cmp expect actual && + test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual && + grep "$(git rev-parse $a) $(git rev-parse $a)" actual + ' + + test_expect_success "stdin ${type} symref-update regular ref to symref fails with wrong old-oid" ' + test_when_finished "git update-ref -d refs/heads/regularref" && + git update-ref --no-deref refs/heads/regularref $a && + create_stdin_buf ${type} "symref-update refs/heads/regularref" "$a" "oid" "$(git rev-parse refs/heads/target2)" >stdin && + test_must_fail git update-ref --stdin ${type} err && + echo $(git rev-parse $a) >expect && + git rev-parse refs/heads/regularref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update existing symref with zero old-oid" ' + test_when_finished "git symbolic-ref -d --no-recurse refs/heads/symref" && + git symbolic-ref refs/heads/symref refs/heads/target2 && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" "oid" "$Z" >stdin && + test_must_fail git update-ref --stdin ${type} err && + grep "fatal: cannot lock ref ${SQ}refs/heads/symref${SQ}: reference already exists" err && + echo refs/heads/target2 >expect && + git symbolic-ref refs/heads/symref >actual && + test_cmp expect actual + ' + + test_expect_success "stdin ${type} symref-update regular ref to symref (with deref)" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + test_when_finished "git update-ref -d --no-deref refs/heads/symref2" && + git update-ref refs/heads/symref2 $a && + git symbolic-ref --no-recurse refs/heads/symref refs/heads/symref2 && + create_stdin_buf ${type} "symref-update refs/heads/symref" "$a" >stdin && + git update-ref ${type} --stdin expect && + git symbolic-ref --no-recurse refs/heads/symref2 >actual && + test_cmp expect actual && + echo refs/heads/symref2 >expect && + git symbolic-ref --no-recurse refs/heads/symref >actual && + test_cmp expect actual && + test-tool ref-store main for-each-reflog-ent refs/heads/symref >actual && + grep "$(git rev-parse $a) $(git rev-parse $a)" actual + ' + + test_expect_success "stdin ${type} symref-update regular ref to symref" ' + test_when_finished "git symbolic-ref -d --no-recurse refs/heads/regularref" && + git update-ref --no-deref refs/heads/regularref $a && + create_stdin_buf ${type} "symref-update refs/heads/regularref" "$a" >stdin && + git update-ref ${type} --stdin expect && + git symbolic-ref --no-recurse refs/heads/regularref >actual && + test_cmp expect actual && + test-tool ref-store main for-each-reflog-ent refs/heads/regularref >actual && + grep "$(git rev-parse $a) $(git rev-parse $a)" actual + ' + done test_done From patchwork Fri Apr 26 15:24:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13644980 Received: from mail-lf1-f42.google.com (mail-lf1-f42.google.com [209.85.167.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 397071514C2 for ; Fri, 26 Apr 2024 15:25:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145104; cv=none; b=fOLmWyPY+PSdmGSRI9fPJWP5tM5aOrgF+pLChV8Ale6c0LeN0XhSK55UEDHZGP5Ao30Rw1gMf8vydYNa2XJvUsghHeyo3CCLOWNhYQRK7muGjmbF8lJR8KuTek0J/6vEt83qOzaBQ5QWvI1Acww4UzI4JRw9D9xrraAWhjQXpWs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1714145104; c=relaxed/simple; bh=yUWzQt/oTI76ZCjK/T+bIZ4N7q+xlSa5DLKiM2uUslg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jqgwxJHJVMozi1Mv2cC5MqicOLTjFd6iWRtkXDKX7+lKNXQvwK7PP4WZW/auPAy8vqjrRnDYuS+KxPSisKR3Yx+x/Woy1/5oobgPU4sC5VAsCtH10kMOq/ysjBpJP8Bdtnf8NCz9fNJMqknyU5GNdgNlEqsyBdjUbC6pfSuRr2I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ccATXRd6; arc=none smtp.client-ip=209.85.167.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ccATXRd6" Received: by mail-lf1-f42.google.com with SMTP id 2adb3069b0e04-516d1ecaf25so3119268e87.2 for ; Fri, 26 Apr 2024 08:25:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1714145100; x=1714749900; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4vZ/Rbr2oc3Q7/0WDOOreCOOxs0mpGhGeznFIqaXXPU=; b=ccATXRd6IkxnyXiNRtEdum14BkgzAusy9zb4J+wzNgQtIKbgTpPQDutATBOHcCKpQX VaQJrC8EPfHq6ztKsnzfApaefoAyY2ZuZkKe0c+ynmxKuV8N4l6zLJwazE8Cg3oSkuxz 3gr4xq0R5Xkj6gHeyryjglRZb0ifi9lT7WvKsC9sN72BeG2HYlxHmsFQRBwFgpVEeX3B lGGa4i5vYlqeq4I6col61I+uZ9eISoWwkKEVg/8ZkUkonavTBxxKFkiaSSAX1YylhAIb N190Ccj34J/rP/djC20g9Qk35SP5n2B7XcP+JC3ZUy3hiengUWk5Qnv3S+W8c4R21eNi eDbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714145100; x=1714749900; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4vZ/Rbr2oc3Q7/0WDOOreCOOxs0mpGhGeznFIqaXXPU=; b=GbSItyjFycchMHTiVOwQACmxsUZKRjPI/+l5pnXxqLYa8TapAnn1rivW3Sah4ls1fS 0MxzZ8JIgLj6edmVU9sIMAz638wqAzyVRv+cm7WkKWrEWQfza5pl3UlFgdJWeKCnwgzF t0PKNQF24YVJcrPpeJJ1h1WB+J8bPyn1DOyGlr5N8lwr6bnhDszO49SwapNxjLpd4Q7L MAs2AHUFwRTo8iPGaEq0uIT0XLyMJ4xCcZY7cfQjFaEuZg/5wkN3Uum3UzcadW5vy1nD Yi2UjV2xdHqhIzZhgued2tZ4MrLpfZsJRGgU9ZZJXklQZ1SnvTKW58aZVbCcOiHEbSeV yIvg== X-Forwarded-Encrypted: i=1; AJvYcCUQUJCFWh5sFAAtH6w/5kT5K4k7oxIJRdtBwZg0vlBd9FTllUeSRkK8SEPjruf6qpfE+rOqXDGprJitiz//lTNar6a7 X-Gm-Message-State: AOJu0Yx+9nw2izqpoSh2q5zsDFhIGIk5qIgDDADkHSG0rEakyoS0KHxv 3/18Wd5l9o9/jdGh+qJzs/jTPXx9vEu1uYEzHJsBn94FyCpbxSYc X-Google-Smtp-Source: AGHT+IFlWf+f0TQFpg0k/JqXuFGpGdXiezWmh+nKtsR6JJYSZKZJUkLhqdjaYhkuhGn0Xb2xJdRcFw== X-Received: by 2002:a19:3855:0:b0:51b:14d9:9c12 with SMTP id d21-20020a193855000000b0051b14d99c12mr1918892lfj.30.1714145100017; Fri, 26 Apr 2024 08:25:00 -0700 (PDT) Received: from localhost.localdomain ([185.223.147.210]) by smtp.gmail.com with ESMTPSA id z6-20020a170906434600b00a518c69c4e3sm10643868ejm.23.2024.04.26.08.24.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Apr 2024 08:24:59 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: christian.couder@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v4 7/7] ref: support symrefs in 'reference-transaction' hook Date: Fri, 26 Apr 2024 17:24:49 +0200 Message-ID: <20240426152449.228860-8-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240426152449.228860-1-knayak@gitlab.com> References: <20240423212818.574123-1-knayak@gitlab.com> <20240426152449.228860-1-knayak@gitlab.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Karthik Nayak The 'reference-transaction' hook runs whenever a reference update is made to the system. In the previous commits, we added symref support for various commands in `git-update-ref`. While it allowed us to now manipulate symbolic refs via `git-update-ref`, it didn't activate the 'reference-transaction' hook. Let's activate the hook for symbolic reference updates too. There is no new format described for this and we stick to the existing format of: SP SP LF but now, and could also denote references instead of objects, where the format is similar to that in 'git-update-ref', i.e. 'ref:'. While this seems to be backward incompatible, it is okay, since the only way the `reference-transaction` hook has refs in its output is when `git-update-ref` is used to manipulate symrefs. Also the documentation for reference-transaction hook always stated that support for symbolic references may be added in the future. Signed-off-by: Karthik Nayak --- Documentation/githooks.txt | 14 +++++++---- refs.c | 21 ++++++++-------- t/t1416-ref-transaction-hooks.sh | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index ee9b92c90d..0bf8ca87a6 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -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 cover symbolic references. 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: - SP SP LF + SP SP LF -where `` is the old object name passed into the reference -transaction, `` is the new object name to be stored in the +where `` is the old object name passed into the reference +transaction, `` is the new object name to be stored in the ref and `` 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, `` is the all-zeroes object name. To +to be created anew, `` is the all-zeroes object name. To distinguish these cases, you can inspect the current value of `` via `git rev-parse`. +For symbolic reference updates the `` and `` +fields could denote references instead of objects, denoted via the +`ref:` format. + 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 diff --git a/refs.c b/refs.c index 42cb4126a7..9a510744a7 100644 --- a/refs.c +++ b/refs.c @@ -2365,18 +2365,19 @@ static int run_transaction_hook(struct ref_transaction *transaction, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; + strbuf_reset(&buf); - /* - * Skip reference transaction for symbolic refs. - */ - if (update->new_target || update->old_target) - continue; + if (update->flags & REF_HAVE_OLD && update->old_target) + strbuf_addf(&buf, "ref:%s ", update->old_target); + else + strbuf_addf(&buf, "%s ", oid_to_hex(&update->old_oid)); - 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_NEW && 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) { diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh index 2092488090..0a7e86062e 100755 --- a/t/t1416-ref-transaction-hooks.sh +++ b/t/t1416-ref-transaction-hooks.sh @@ -134,4 +134,45 @@ test_expect_success 'interleaving hook calls succeed' ' test_cmp expect target-repo.git/actual ' +# This test doesn't add a check for symref 'delete' since there is a +# variation between the ref backends WRT 'delete'. In the files backend, +# 'delete' also triggers an additional transaction update on the +# packed-refs backend, which constitutes additional reflog entries. +test_expect_success 'hook gets all queued symref updates' ' + test_when_finished "rm actual" && + + git update-ref refs/heads/branch $POST_OID && + git symbolic-ref refs/heads/symref refs/heads/main && + git symbolic-ref refs/heads/symrefu refs/heads/main && + + test_hook reference-transaction <<-\EOF && + echo "$*" >>actual + while read -r line + do + printf "%s\n" "$line" + done >>actual + EOF + + cat >expect <<-EOF && + prepared + ref:refs/heads/main $ZERO_OID refs/heads/symref + $ZERO_OID ref:refs/heads/main refs/heads/symrefc + ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu + committed + ref:refs/heads/main $ZERO_OID refs/heads/symref + $ZERO_OID ref:refs/heads/main refs/heads/symrefc + ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu + EOF + + git update-ref --no-deref --stdin <<-EOF && + start + symref-verify refs/heads/symref refs/heads/main + symref-create refs/heads/symrefc refs/heads/main + symref-update refs/heads/symrefu refs/heads/branch ref refs/heads/main + prepare + commit + EOF + test_cmp expect actual +' + test_done