From patchwork Tue Apr 23 21:28:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13640736 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 D8558142909 for ; Tue, 23 Apr 2024 21:28:24 +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=1713907707; cv=none; b=Radedz1AQVFHmeNKBlSfP8V2EPKBsByF+qdHuN1Q/pe0D9e73WYeaQuGIwxS//iythw/xoVoO9xXcax4IkyaDHk6FSy/fBsYOGYWu+9ACi1QDtrAuv6fG5jn+GFDqSmn+fm7CLpDARtObIw+9g/sieROA3nQRz9hZmDNgz6WcNg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907707; c=relaxed/simple; bh=7ncCD7NRKWzUmTmtu87zEgciK5dCti1XMCJ8JHjKtoc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kmvWENqS8e0dQ+OWZyJclaabrPrM0z0ftIlwwGShIiboEctQNNCSpruo42CsCG0J9MbCchZNo0ibOqjIOQi3BQpbtwTPH4owVX+vWKIlU75Uh4bJu2thSDfJwWKxveeZn2Hg3T78pZaKB/aLpVf3ooN4sj0kwDo1Gobo15fO6OQ= 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=TnaeC5MG; 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="TnaeC5MG" Received: by mail-lf1-f53.google.com with SMTP id 2adb3069b0e04-51bab51e963so1397869e87.1 for ; Tue, 23 Apr 2024 14:28:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907703; x=1714512503; 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=poB2aZLDLjzZ7ZM+r9rBeo/EUL9hKMrepUiT0BueoCA=; b=TnaeC5MGMV4r+PiuLBnvzHJKWUfbxrt+AZ9M8RiHitc9W8iyBG86dgwNmi+vIlygjB Qp8Tv0lbk5MJAZH3BDeP5rBV1QprDBcxzFBn1LqP8G0j/uzI91Ea9PeXhp4hcpQBvUps 4WcHyClMDXYuOpYzPaHjhHxVQeC/laeLhfwQKSB0DV0lir6urhzCZAIvFRVK1JiEI3n+ wbdhWWCs0fUSnJDojQh3vzZvRnFQVkOZWcPQQhm1MolgX8SZA5ICu5XTq4M2yy8WD5S9 81XD8qB23TmhbsfYzcCKf9MveN+FLjMnFlqNsEKDWbn+aPXi3xJa8Z32hI2rcBTrt1Mw w5ww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907703; x=1714512503; 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=poB2aZLDLjzZ7ZM+r9rBeo/EUL9hKMrepUiT0BueoCA=; b=TrdgVxxD3ds08N8rA6Z2YGdzfNgZBWHVOoikgIsCdkKrWCfG8wuEGinmNX4DpcAgTW vkzS4DhDQZnt1Tr7/m2TsrpmkGptS4VvM/0n2iz9zWIIIYjm3ABncsFVDrPGCbl4CrKY nwnCmU5jZuc3rLJ6I5nFHkYLsTcGUf7Yf5ywTKS2wDDf90I7wrd4J9LIeW8h5G4TudeD qJLmcpCstzWlQrACVjewccnUFbvgidY5chfpXrlBgykwjfXlC/Sft2aM56+Dr1qrrdHd mJcalOUYYuV8ZoE/dt0tnEeuqLl5IjSgidNoYiQStlqa1xRgRCQhTV3Zxv7vFC5ubP50 tDdw== X-Forwarded-Encrypted: i=1; AJvYcCXZXT/V85iiWyUFbgZqp/qfdNh5cceCS8HfnnWWqqyWz5HItp6TeSmlA56OD8kQNTJKGzAJ+dNUBF/AMrajWEQnItaI X-Gm-Message-State: AOJu0Yzqibi/QoL+kxtY9hIo9G9X1xtXtDzJjea58dGEwRxETDRWtufp h5+FDn7tG5J+34U6u9gCmDMOWq8bSvlEu86nAm8PVXbHse9XLHL9FwKTCg== X-Google-Smtp-Source: AGHT+IGpRZPvRfrQPuarhNjQHf4+YSB7plKlQBHTZmlWvPQ7OeBIK/c5jmEf1A30o6NvUGoo5g5FXg== X-Received: by 2002:a05:6512:4810:b0:518:c3b7:bc0 with SMTP id eo16-20020a056512481000b00518c3b70bc0mr470544lfb.10.1713907702441; Tue, 23 Apr 2024 14:28:22 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:21 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 1/8] refs: accept symref values in `ref_transaction[_add]_update` Date: Tue, 23 Apr 2024 23:28:11 +0200 Message-ID: <20240423212818.574123-2-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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. Some functions allow this parameter to be NULL, meaning that the reference is not changed. 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. A copy of this value is made in the transaction. The handling logic of these parameters will be added in consequent commits as we add symref support to the existing 'git-update-ref' commands. 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 Tue Apr 23 21:28:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13640737 Received: from mail-ej1-f50.google.com (mail-ej1-f50.google.com [209.85.218.50]) (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 62E7D143881 for ; Tue, 23 Apr 2024 21:28:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907707; cv=none; b=nHFyfAt4UEZuMjG4wzTTBpanlwDlm3HHuIoOWFsbKjGrQ8N1GapSyhfXObV7tV5fqeGriUUajbKB6nJie5DalNXCQety4LUnJsZSlWc0EPjxDmUiylrM7+yK6Dke41HxCT0f6xON6yOmztUVQIz/Gku+cWBfDOhE+yxnL+e1cDY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907707; c=relaxed/simple; bh=w4pqoQmkFvDucu77CL+TWFf6zJwyuHSUQhx/CRY89NU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nxNCRVFXo+x97qjubrf0iDh2T5VZOoaWkTKrdh5XvYDoaYnlzxoD+6oLfJBnUqyY1JVNO/1vXZvLtyHXoytsw7oUdfvBzg52LCb/03SOTVBAiepnWC++TyMkZWoLxIbJLtEr9N4RuNJ+qScUEAINUOd6yBnfNYAUafSh/SEZ1dQ= 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=d5cPgxIe; arc=none smtp.client-ip=209.85.218.50 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="d5cPgxIe" Received: by mail-ej1-f50.google.com with SMTP id a640c23a62f3a-a5883518135so121225166b.3 for ; Tue, 23 Apr 2024 14:28:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907704; x=1714512504; 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=hy+xrnGgkhMGybj2Hd78neFd9IvH29GqM7hPGHzaINw=; b=d5cPgxIeaignKY61irV+fztPrXXJLeM/BbhkXQhNnT1OX7RzoYgl0ZkUOivbzOAxOF 5aGM4EkllNS4eehvMQ4Xy7D9GX37sUOpM+Q/VvMFJY/UznnC/Ff0hDCnqvhrlIIRfA+R HT5BtWsT22GthWIEwyKmB639+6BKyxa2GpEG5pqN9Hj1vFc1slbvdSpGxg20PjN8ieG5 1iHUt3S6U+3DTJ1WNAvI+HnLyWT2xHk1bLz8fbOP29R0gU9dILQr1P+xRku4qnAOnkZM Qc3oVOuY7rbnVeKUu+XHJMsQTIITwm91mRYKxhfYmCWwUI+h7xDxZUDO72dEC1Grhedd tQFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907704; x=1714512504; 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=hy+xrnGgkhMGybj2Hd78neFd9IvH29GqM7hPGHzaINw=; b=j5fh1+UFE6jgG23SeoLGvee89OACXWamjkx5PtAKkwBrf/p1sr0+V/vBWBEg+KEU1u c7cCbgI5RrpeVIFt55M8PLMSIOub8L2JRaHPDkmpgWssPw5FUrC+H3rcmfoudimFzxtv aLHY45lRXjzMpEtDy1C5hA1XikiragVJlqdAepPGSZJX+2FxnEqQ/58HL6ghTpCS1dsS OZqedTcaCQj0GgH+9mXjhjQ27/XVSZ82fnMkpfOuE+3I8lAB4pB7eBcfclUsJ+4unIWQ INWvigoqDNppZDtycxFd29AZo+pduaUJ3B4EMQPwUi8Z4A33oedFvtDNUP6VDDLka+jy nDeQ== X-Forwarded-Encrypted: i=1; AJvYcCWJuDLCJwUA6WsejU3LhvIf3+M91O+S8pOM+H4ObVfWcCahJ102oaZmTlr42ruy0X0ODcR5EhX522H4aSxRmnv43pNE X-Gm-Message-State: AOJu0Yz7ADhTJo2P25dmH2be+nPegnSmsKn1Q138FLIKzEUNVc9aacke rk+Yly182H1X9TXzPO6/uVGw6/J9+KGyiopViHDXFZLFgIOeR52Y X-Google-Smtp-Source: AGHT+IGzzw0mp8IVassIWnA6sHejJhC7RUZ2cE+3FS0tHOJH+t+ifY6omDB2vx1AcLTCkXeSGqUZXw== X-Received: by 2002:a17:906:aace:b0:a55:5a28:cd53 with SMTP id kt14-20020a170906aace00b00a555a28cd53mr382102ejb.8.1713907703457; Tue, 23 Apr 2024 14:28:23 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:23 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 2/8] update-ref: support parsing ref targets in `parse_next_oid` Date: Tue, 23 Apr 2024 23:28:12 +0200 Message-ID: <20240423212818.574123-3-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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 `parse_next_oid` is used for parsing the next oid present in the input buffer. Extend this function to also parse refnames in the form `ref:`. This will be used in the upcoming commits to add symref support to the existing update-ref commands. Since `parse_next_oid` now also parses refs apart from oids, we rename the function to a more apt name `parse_next_arg`. Signed-off-by: Karthik Nayak --- builtin/update-ref.c | 58 ++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 21fdbf6ac8..98ec356394 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -88,6 +88,11 @@ static char *parse_refname(const char **next) */ #define PARSE_SHA1_ALLOW_EMPTY 0x02 +/* + * Parse refname targets using the ref: format. + */ +#define PARSE_REFNAME_TARGETS 0x04 + /* * Parse an argument separator followed by the next argument, if any. * If there is an argument, convert it to a SHA-1, write it to sha1, @@ -95,10 +100,13 @@ static char *parse_refname(const char **next) * return 0. If there is no argument at all (not even the empty * string), return 1 and leave *next unchanged. If the value is * provided but cannot be converted to a SHA-1, die. flags can - * include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY. + * include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY and/or + * PARSE_REFNAME_TARGETS. When PARSE_REFNAME_TARGETS is set, parse + * the argument as `ref:` and store the refname into + * the target strbuf. */ -static int parse_next_oid(const char **next, const char *end, - struct object_id *oid, +static int parse_next_arg(const char **next, const char *end, + struct object_id *oid, struct strbuf *target, const char *command, const char *refname, int flags) { @@ -118,8 +126,17 @@ static int parse_next_oid(const char **next, const char *end, (*next)++; *next = parse_arg(*next, &arg); if (arg.len) { - if (repo_get_oid(the_repository, arg.buf, oid)) - goto invalid; + if (repo_get_oid(the_repository, arg.buf, oid)) { + const char *value; + if (flags & PARSE_REFNAME_TARGETS && + skip_prefix(arg.buf, "ref:", &value)) { + if (check_refname_format(value, REFNAME_ALLOW_ONELEVEL)) + die("invalid ref format: %s", value); + strbuf_addstr(target, value); + } else { + goto invalid; + } + } } else { /* Without -z, an empty value means all zeros: */ oidclr(oid); @@ -136,8 +153,17 @@ static int parse_next_oid(const char **next, const char *end, *next += arg.len; if (arg.len) { - if (repo_get_oid(the_repository, arg.buf, oid)) - goto invalid; + if (repo_get_oid(the_repository, arg.buf, oid)) { + const char *value; + if (flags & PARSE_REFNAME_TARGETS && + skip_prefix(arg.buf, "ref:", &value)) { + if (check_refname_format(value, REFNAME_ALLOW_ONELEVEL)) + die("invalid ref format: %s", value); + strbuf_addstr(target, value); + } else { + goto invalid; + } + } } else if (flags & PARSE_SHA1_ALLOW_EMPTY) { /* With -z, treat an empty value as all zeros: */ warning("%s %s: missing , treating as zero", @@ -192,12 +218,12 @@ static void parse_cmd_update(struct ref_transaction *transaction, if (!refname) die("update: missing "); - if (parse_next_oid(&next, end, &new_oid, "update", refname, - PARSE_SHA1_ALLOW_EMPTY)) + if (parse_next_arg(&next, end, &new_oid, NULL, + "update", refname, PARSE_SHA1_ALLOW_EMPTY)) die("update %s: missing ", refname); - have_old = !parse_next_oid(&next, end, &old_oid, "update", refname, - PARSE_SHA1_OLD); + have_old = !parse_next_arg(&next, end, &old_oid, NULL, + "update", refname, PARSE_SHA1_OLD); if (*next != line_termination) die("update %s: extra input: %s", refname, next); @@ -225,7 +251,7 @@ static void parse_cmd_create(struct ref_transaction *transaction, if (!refname) die("create: missing "); - if (parse_next_oid(&next, end, &new_oid, "create", refname, 0)) + if (parse_next_arg(&next, end, &new_oid, NULL, "create", refname, 0)) die("create %s: missing ", refname); if (is_null_oid(&new_oid)) @@ -256,8 +282,8 @@ static void parse_cmd_delete(struct ref_transaction *transaction, if (!refname) die("delete: missing "); - if (parse_next_oid(&next, end, &old_oid, "delete", refname, - PARSE_SHA1_OLD)) { + if (parse_next_arg(&next, end, &old_oid, NULL, + "delete", refname, PARSE_SHA1_OLD)) { have_old = 0; } else { if (is_null_oid(&old_oid)) @@ -289,8 +315,8 @@ static void parse_cmd_verify(struct ref_transaction *transaction, if (!refname) die("verify: missing "); - if (parse_next_oid(&next, end, &old_oid, "verify", refname, - PARSE_SHA1_OLD)) + if (parse_next_arg(&next, end, &old_oid, NULL, + "verify", refname, PARSE_SHA1_OLD)) oidclr(&old_oid); if (*next != line_termination) From patchwork Tue Apr 23 21:28:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13640738 Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) (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 50FA914388A for ; Tue, 23 Apr 2024 21:28:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907707; cv=none; b=dok34ONXzPy9J+M8dCezGoW4Vbbv4bbMZ+K7KQHJsAdpyc7j8kJOgDjYAmDqIcJpNetV+I1u6yMPnhHwxmjai+3VnrkZQ0C8wpFjgH9id8FUfc8RL8Ejw0XXoJqxe3JVGmSzrj0X8c88qBz0IHsPqH/efEB56NncCVrgFs76B5o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907707; c=relaxed/simple; bh=orGXPgisydaJqUHfbfDCBIOnxxMoAGsrT1Ao/VkQcf0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=g4UlQYhlPIT2wG+jkf0OFOemq0yRsQ3ZquUgzPIn8AM4vphKv3grp/Zj6iCwvy6qfIyFKWaCOhGkNLKPdN2LZAWaff/MVrnRdY/oZ7YEveS1Esruq9lr7Um49H1a+/iAnPmDm7diqDbytGWivLC+lBnHDXxUVkojFcIlfrD6ENM= 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=OS+dfB9+; arc=none smtp.client-ip=209.85.128.47 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="OS+dfB9+" Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-417f5268b12so65373845e9.1 for ; Tue, 23 Apr 2024 14:28:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907705; x=1714512505; 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=OS+dfB9+8GVpAC+ZYU95GApNjD/+OzFJq5ufynQHHINZ6asl8hN+Q1h35VkG1acRqo 1RY8Z09p9dOadEIfEgatyTPk7WZqxhqvUXlRozOJirXomWXhopudcHFlSwHVLM/loeeV K3sdVeeWA4zHpQgjnl+W4pWCfJCIzg5gBCqNykKbJcqErA+lHkURuSNpT4FbkDxPKEXt uBkDZWab3eyE+3IVs/x9aEVz6nZZIKC3z72X2CokX8f/GY9Zbz/mYvsaJ7UqxN8H2mK6 XwkFUtiPAIB6/3r9sEsf1MdTi1P/hwZRdScbfm43aTdd76tUzp192coBLFs9QXxQfArX Bhmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907705; x=1714512505; 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=GnBjG5DHit2Yn+VcE3sg8ZO8ZaG9S4yYIoKmNltu860s2rBPp8EkH2H/w8zm5Ga0YM fqtFlU4xgtFV2q6rOig/hkwRsoCEVEVovdxkn7lV+fqdCJGnuLF7tTM2UbZQEimPQpiY o9FbUS3fZeqgTthmbALuQfy8E2f+KVQuUqxlnbaH0iLFfVCFzUu4+RvPNyiOrcroEmea hLFaDOB2oup13p1llxV4iRxrNVcs355gGzP9DuClHS2rPop3ofNsDGLotP/8I+9YuUuG gKpa9AC7EHRO5BC/kAEBhlwpRufyp2gDRZCUnKCHfTGgpjuBri16IChiwNMwAP58/qg9 cN1A== X-Forwarded-Encrypted: i=1; AJvYcCUc1rZ+0y3zF4E0ncjVnbzVU/howcwGGp2/3xeDn7r6atHzVTW2pPYu7JwNGWsjkXU2fntHjRnAHvieeHAU3ydi11/n X-Gm-Message-State: AOJu0YxWuX8OOVPs+hUJIdlJ7FsdBrdUrI+vgWl2qptb9KEmuRlON0Lk x5cJKdzhNekzNrmuTu+bwcdH8NRwhjacyJQx4UGyLrRBKFHpYZRu X-Google-Smtp-Source: AGHT+IFpSI3o1EkPXU47tlppbpY/XgdoHsDy6WdvdgWidQ7iRcuSifmPgMthPONfKnLZp59kdptzgQ== X-Received: by 2002:a05:600c:19cc:b0:419:a3f:f4f6 with SMTP id u12-20020a05600c19cc00b004190a3ff4f6mr478846wmq.8.1713907704620; Tue, 23 Apr 2024 14:28:24 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:23 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 3/8] files-backend: extract out `create_symref_lock` Date: Tue, 23 Apr 2024 23:28:13 +0200 Message-ID: <20240423212818.574123-4-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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 Tue Apr 23 21:28:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13640739 Received: from mail-ej1-f44.google.com (mail-ej1-f44.google.com [209.85.218.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 9BC1A143C46 for ; Tue, 23 Apr 2024 21:28:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907709; cv=none; b=XSBRT6qoRoFQWW62ShbX3JerQek7sz5V+GPRP4JcwiI83KAcnC60oqQPLNr3lXT2vSFWX3gWUSXbw5Bvi52sNupp5T7q0lQmSuegd2J/bvNsv6ZtWwGnbsGVB9ZIr3C3gI46YVpOlT8wV53eld0JSQ9dPVoRI6945ecWrOn+UAA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907709; c=relaxed/simple; bh=d2DZQDk/BxUezkWoW1xrAGylBIcfCR6IYul729P1ppQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=S6w7R7JLsDvKKlmzJ2DRJdr6yKFJvHz6BmlToR60QXzpuoe00Q7EzIf8dfVvetfdo6Nc0yHwVW0XAl5RtthAMJa4hL+SSWuy875Qk/+nnO8DRO6wFAtJzqhYERThh9r+ikjzzK5NfoWLFKUobO4dKb8cQ06uy2nK6P2IpSiWTJU= 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=AJkpyQBo; arc=none smtp.client-ip=209.85.218.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="AJkpyQBo" Received: by mail-ej1-f44.google.com with SMTP id a640c23a62f3a-a5224dfa9adso55361866b.0 for ; Tue, 23 Apr 2024 14:28:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907706; x=1714512506; 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=v6neRkLWigvqBj+kM2jEZVGy5BYOXBwuihzOpQ18o80=; b=AJkpyQBoLNJ9jT/q6hKrsxrPoLyOB4A+tFh3o++VV3McxHe52rv/ImJVwj6bB3QXtx WvJNuU3ItH9GY2+AL94nYGrLX+CDzCfHihweCMzQmMgQrPX4K3lF58BlgnIU3Cv8bcU6 UG2ldoYwcj4FsBYISb69gkLotZ4Y86D31iHRGuEDsyKuwT7FWLfHq+lBqId6G6KuCSt5 7o5Ez3pQS5AQQe/LiMWipSsbxKnpGOAXB0xuGeai1UdQT42Es6hdmqqzKO6X51wTBPbC QhT2fbqTaZEgvHMDNQxLm5bPuzXRgZ14dOYtYKQGwq8zaGZVkrQ5awX6Oz2NMJXjeIWW Z3vA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907706; x=1714512506; 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=v6neRkLWigvqBj+kM2jEZVGy5BYOXBwuihzOpQ18o80=; b=uS6wxWSRk8R943lmZWAkxIvMk2NAQygFb8uhN4cu9wJqEV5Ax1KajzFU7K78QX0a7h XE5uGNOgqld19HtJ2Dy3RWJa+dMd9V2jH0ftllQD0IpKKEFNUQJhUDf5nC/hJc02thmN 3YqeOjI+SZE6sEcbgYNtHvt2Ez4N+2dSQxdERitFR4FovuyOkDogT62ppkuLSXhN60Xe k1tPpUhzwM0DLoqHJTJEw0OLMxPM6YfoQVFVVnEfud4b7ll8fzVf7GXUBcWGr/Id6j0F JDNwng3l29omsyGJLh1TtlRF0HQq4fwnwwgwE4PJnQ7n5lFv79ToNjvkNgYX5h8egQ6b lSwQ== X-Forwarded-Encrypted: i=1; AJvYcCUEwHLxXax2Y3+AKgSI/7FToSoq1T1nqpEMFYjyautNsqn7MXEglRA59j17ZOD7jPq3eXXJ2Jkk3zKp3irICPKf2eW9 X-Gm-Message-State: AOJu0YwBY98E7bbU5A3Vf7GYS2UzGgZlNP+U2ex0H9tziW2ASVX5oGp8 PTTAxfarcTcWQZbstl7L3mTUMsJId0dZy3O/spo+/zrVsgnPrn2t X-Google-Smtp-Source: AGHT+IFIPmHaTgLkKqJfpuHlzVbzI8U5zIE0jkHmYEAgVsKBP27RHHNgao44SXw3Yw9hToxvySJ1Eg== X-Received: by 2002:a17:907:778c:b0:a55:5958:cb00 with SMTP id ky12-20020a170907778c00b00a555958cb00mr3589707ejc.38.1713907705732; Tue, 23 Apr 2024 14:28:25 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:25 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 4/8] update-ref: support symrefs in the verify command Date: Tue, 23 Apr 2024 23:28:14 +0200 Message-ID: <20240423212818.574123-5-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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 support to the transaction commands provided by 'git-update-ref(1)'. Using them, extend the 'verify' command to support symrefs. The 'verify' command allows users to verify if a provided symbolic reference `` contains the provided `` without changing the ``. Now we alternatively allow users to provide a `ref:` instead to verify if a symref targets the provided target. Since we're checking for symbolic refs, this will only work with the 'no-deref' mode. This is because any dereferenced symbolic ref will point to an object and not a ref. Add and use `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 | 13 ++++-- builtin/update-ref.c | 14 ++++-- 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 | 79 +++++++++++++++++++++++++++++++- 8 files changed, 194 insertions(+), 14 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 374a2ebd2b..9f8c059944 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -64,7 +64,7 @@ performs all modifications together. Specify commands of the form: update SP SP [SP ] LF create SP SP LF delete SP [SP ] LF - verify SP [SP ] LF + verify SP [SP ( | ref:)] LF option SP LF start LF prepare LF @@ -85,7 +85,7 @@ quoting: update SP NUL NUL [] NUL create SP NUL NUL delete SP NUL [] NUL - verify SP NUL [] NUL + verify SP NUL [( | ref:)] NUL option SP NUL start NUL prepare NUL @@ -95,6 +95,12 @@ quoting: In this format, use 40 "0" to specify a zero value, and use the empty string to specify a missing value. +For commands which support it, substituting the value with +ref: will ensure that the targets the specified +old-target before the update. Similarly, substituting the +with ref: will ensure that the is a symbolic ref +targeting the new-target after the update. + In either format, values can be specified in any form that Git recognizes as an object name. Commands in any other format or a repeated produce an error. Command meanings are: @@ -115,7 +121,8 @@ delete:: verify:: Verify against but do not change it. If - is zero or missing, the ref must not exist. + is zero or missing, the ref must not exist. For + verifying symbolic refs, provide ref:. option:: Modify the behavior of the next command naming a . diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 98ec356394..246167e835 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -308,6 +308,7 @@ static void parse_cmd_verify(struct ref_transaction *transaction, const char *next, const char *end) { struct strbuf err = STRBUF_INIT; + struct strbuf old_target = STRBUF_INIT; char *refname; struct object_id old_oid; @@ -315,20 +316,27 @@ static void parse_cmd_verify(struct ref_transaction *transaction, if (!refname) die("verify: missing "); - if (parse_next_arg(&next, end, &old_oid, NULL, - "verify", refname, PARSE_SHA1_OLD)) + if (parse_next_arg(&next, end, &old_oid, &old_target, + "verify", refname, + PARSE_SHA1_OLD | PARSE_REFNAME_TARGETS)) oidclr(&old_oid); + if (old_target.len && !(update_flags & REF_NO_DEREF)) + die("verify %s: cannot operate on symrefs in deref mode", refname); + if (*next != line_termination) die("verify %s: extra input: %s", refname, next); - if (ref_transaction_verify(transaction, refname, &old_oid, + if (ref_transaction_verify(transaction, refname, + old_target.len ? NULL : &old_oid, + old_target.len ? old_target.buf : NULL, update_flags, &err)) die("%s", err.buf); update_flags = default_flags; free(refname); strbuf_release(&err); + strbuf_release(&old_target); } static void report_ok(const char *command) 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..1f2b63755a 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,73 @@ 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} verify symref fails without --no-deref" ' + git symbolic-ref refs/heads/symref $a && + create_stdin_buf ${type} "verify refs/heads/symref" "ref:$a" && + test_must_fail git update-ref --stdin ${type} err && + grep "fatal: verify refs/heads/symref: cannot operate on symrefs in deref mode" err + ' + + test_expect_success "stdin ${type} verify symref fails with too many arguments" ' + create_stdin_buf ${type} "verify refs/heads/symref" "ref:$a" "ref:$a" && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: ref:$a" err + else + grep "fatal: verify refs/heads/symref: extra input: ref:$a" err + fi + ' + + test_expect_success "stdin ${type} verify symref 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} "verify refs/heads/symref" "ref:$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} verify symref succeeds for missing reference" ' + test-tool ref-store main for-each-reflog-ent refs/heads/symref >before && + create_stdin_buf ${type} "verify refs/heads/missing" "$Z" && + git update-ref --stdin ${type} --no-deref after && + test_cmp before after + ' + + test_expect_success "stdin ${type} verify symref fails for wrong value" ' + git symbolic-ref refs/heads/symref >expect && + create_stdin_buf ${type} "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} verify symref fails for mistaken null value" ' + git symbolic-ref refs/heads/symref >expect && + create_stdin_buf ${type} "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 Tue Apr 23 21:28:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13640740 Received: from mail-lf1-f54.google.com (mail-lf1-f54.google.com [209.85.167.54]) (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 0043D143C6C for ; Tue, 23 Apr 2024 21:28:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907710; cv=none; b=YIYZzgH2NfNh4MeJS+q5VHX1HDt72Y2Z9t36gJtc63Fcxd6cGkVeA/p5CipO+3AirHnHaxVeEXmYm0KPFtifty6vWzeyKigg6StYuNGMd9FtaEgfa5kh/UMkt2U0fRMoOSylktBcjoBXgoP6CVNokZvMKjMIDfypeS/BoJb6JWI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907710; c=relaxed/simple; bh=7fgNZLIdgqcPVq2dFfmQnatCOPsQQS40BlWbJcqdQ5Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=S4ffuCuno0Kc0yqUD6bsIEqWFG9ZDJ6C1hBOlZ5Lvio+E1lUHVNiEZb7425us5c4fOiALSgqJQnnCTGme0UDpCVv/8mwGgYCFGY7Us5BC0Dg/sy2fqKHTrRD+E77znj9uQtKufR9r4y03krMpCVnwEElUQn0YL/dCbJPOBNd2hY= 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=Q40lmkos; arc=none smtp.client-ip=209.85.167.54 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="Q40lmkos" Received: by mail-lf1-f54.google.com with SMTP id 2adb3069b0e04-5196fe87775so6949034e87.3 for ; Tue, 23 Apr 2024 14:28:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907707; x=1714512507; 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=lFTZnhsnqX9pdSNR7VPaXflincA9/kJsG+wpqJGeIBM=; b=Q40lmkos2X/BP+4MIq/ZGHCG/m7ZOLWQaDokZZRgigbcG4tv8fU6qCeLHYwn15SbIG nMetCINcvbS8vfmKLDJNpvO4i8fa2b+lo4eqsFfaS+MhRypDeYikgvrUM1BQTzRZtbSo 5nb8mCTzoHmwYlaldTCtBMg3qMiE4oR5gnohs4Ns5bja0RO51g+GqRJzVVcJLRfdGArH FEKnVMxntPxz5yohF95rXVZ6RmJpb0xB+ReCWenEHEcmVXshpRyCwlrYXu+psXpLwTtM rFutlnIEAPXh5fpM1DfB4bwBN0acbEt/ePbk8t63gufEbh708m41t4+qbxoGrpxAYghA 4AVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907707; x=1714512507; 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=lFTZnhsnqX9pdSNR7VPaXflincA9/kJsG+wpqJGeIBM=; b=G/z81R7V2n62f0BgltvL2q55xiB+1DFKtjkgT6eOho6EZ9/WRJPvHXYQV5PLzEVZQY rFx0l0yynDqLQbA3tEi4Yu/EGr26E87l0keAD4ffKM5KQ/nDmKBocgASZKdF1vUWwjQ/ G4hRz9CdlZDzEhZq/srH5KsUc9tn15VNG39HZLzC/Y9ImyFTzSrSNAPQmee2kU+5YOdl mZCxxL8hxL7AZ4wHYTq3geOFV+krSdJycabohOSdOIaklVJMTI5leB/douIAgYuqnVXw O0BNkgYFmSXYqshhAQ9kDo5exRbpVgmRzlyt/BiQmVw5abLBGG9+hKNwlSFN1rXFIYyx bLIg== X-Forwarded-Encrypted: i=1; AJvYcCWfFSssglQVB0qcS/Z/A7fbjSfkQEIKsknEQoy6cxuvK8sqyPsBa49NCAWJZWL7qe2IJbTAeEF5Gf8+DRMd7Y9g7noG X-Gm-Message-State: AOJu0YyP5IoRVZ/rJ0tKX53rVVHRMSMcK59EzxobIfxG5dzEJg2EdE0G 4dVv2fF4uKDK7vOd/xxuJdKi9rV2ZD4LZfVWeJ6QaA0eXYZKrE3l X-Google-Smtp-Source: AGHT+IEFIxra6paoucAfFg8l9J7gA7FvfkOX+yL8WHmnq2DFKCsuvrOjU0m0nZryOnzm24+egMIgwg== X-Received: by 2002:ac2:44a8:0:b0:51b:a710:d6c7 with SMTP id c8-20020ac244a8000000b0051ba710d6c7mr537528lfm.14.1713907706913; Tue, 23 Apr 2024 14:28:26 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:26 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 5/8] update-ref: support symrefs in the delete command Date: Tue, 23 Apr 2024 23:28:15 +0200 Message-ID: <20240423212818.574123-6-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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 'delete' command in 'git-update-ref' allows users to delete `` after verifying it exists with ``, if given. Extend this command to alternatively take in `ref:` which is used to verify if the symbolic ref targets the provided `` before deletion. 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 | 10 +++++--- builtin/fetch.c | 2 +- builtin/receive-pack.c | 3 ++- builtin/update-ref.c | 18 +++++++++---- refs.c | 12 ++++++--- refs.h | 4 ++- refs/files-backend.c | 2 +- refs/reftable-backend.c | 2 +- t/t1400-update-ref.sh | 44 ++++++++++++++++++++++++++++++++ 9 files changed, 79 insertions(+), 18 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 9f8c059944..f28b026cd7 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -63,7 +63,7 @@ performs all modifications together. Specify commands of the form: update SP SP [SP ] LF create SP SP LF - delete SP [SP ] LF + delete SP [SP ( | ref:)] LF verify SP [SP ( | ref:)] LF option SP LF start LF @@ -84,7 +84,7 @@ quoting: update SP NUL NUL [] NUL create SP NUL NUL - delete SP NUL [] NUL + delete SP NUL [( | ref:)] NUL verify SP NUL [( | ref:)] NUL option SP NUL start NUL @@ -116,8 +116,10 @@ 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 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 246167e835..cee7a5ebc0 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -274,6 +274,7 @@ static void parse_cmd_delete(struct ref_transaction *transaction, const char *next, const char *end) { struct strbuf err = STRBUF_INIT; + struct strbuf old_target = STRBUF_INIT; char *refname; struct object_id old_oid; int have_old; @@ -282,26 +283,33 @@ static void parse_cmd_delete(struct ref_transaction *transaction, if (!refname) die("delete: missing "); - if (parse_next_arg(&next, end, &old_oid, NULL, - "delete", refname, PARSE_SHA1_OLD)) { + if (parse_next_arg(&next, end, &old_oid, &old_target, + "delete", refname, PARSE_SHA1_OLD | + PARSE_REFNAME_TARGETS)) { have_old = 0; } else { - if (is_null_oid(&old_oid)) + if (!old_target.len && is_null_oid(&old_oid)) die("delete %s: zero ", refname); - have_old = 1; + have_old = 1 && !old_target.len; } + if (old_target.len && !(update_flags & REF_NO_DEREF)) + die("delete %s: cannot operate on symrefs in deref mode", refname); + if (*next != line_termination) die("delete %s: extra input: %s", refname, next); if (ref_transaction_delete(transaction, refname, have_old ? &old_oid : NULL, - update_flags, msg, &err)) + update_flags, + old_target.len ? old_target.buf : NULL, + msg, &err)) die("%s", err.buf); update_flags = default_flags; free(refname); strbuf_release(&err); + strbuf_release(&old_target); } static void parse_cmd_verify(struct ref_transaction *transaction, 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 1f2b63755a..cd1ad0d2ec 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1714,6 +1714,50 @@ do test_cmp expect actual ' + test_expect_success "stdin ${type} delete symref fails without --no-deref" ' + git symbolic-ref refs/heads/symref $a && + create_stdin_buf ${type} "delete refs/heads/symref" "ref:$a" && + test_must_fail git update-ref --stdin ${type} err && + grep "fatal: delete refs/heads/symref: cannot operate on symrefs in deref mode" err + ' + + test_expect_success "stdin ${type} delete symref fails with no ref" ' + create_stdin_buf ${type} "delete " && + test_must_fail git update-ref --stdin ${type} --no-deref err && + grep "fatal: delete: missing " err + ' + + test_expect_success "stdin ${type} delete symref fails with too many arguments" ' + create_stdin_buf ${type} "delete refs/heads/symref" "ref:$a" "ref:$a" && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: ref:$a" err + else + grep "fatal: delete refs/heads/symref: extra input: ref:$a" err + fi + ' + + test_expect_success "stdin ${type} delete symref fails with wrong old value" ' + create_stdin_buf ${type} "delete refs/heads/symref" "ref:$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} delete symref works with right old value" ' + create_stdin_buf ${type} "delete refs/heads/symref" "ref:$a" && + git update-ref --stdin ${type} --no-deref X-Patchwork-Id: 13640741 Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) (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 ACCEA14388A for ; Tue, 23 Apr 2024 21:28:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907711; cv=none; b=UQLwzLPOLeq6rQR0C8t4HKE8WOahY0jP3xlHzmvhe731kf8HKAesfhZkPZB4PZ93lRADbJT6PtHdUwHLl15BssWWhy/Z7ydA8Cm0Q4VTsfNHYsX+CwuKLm0102wtywTvumGL+erJsNhOrOYQYJFKzaXATtHdSqTE3mhKV1eLpHY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907711; c=relaxed/simple; bh=VRbHz0Pk8vng7mWVl5m9H/enNsPzTaBvJATBgsbOkOA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GDA0K8fadbFcEv0HR+fXw7fiBjJk7Aya+Pb/PeSWR/fdjmTksGdAQPpxNl5WuToolT3hkEDeivTEeZFM8Zbwsm+3SvKH0yHh2ZC0tQZVTRdfr7euNevFRvVeIUDu9a23QqV3YOwIlk/x+IGHNh9iQX5r1kUnU95qy3cOmmqhegw= 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=ixcNr7vD; arc=none smtp.client-ip=209.85.128.45 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="ixcNr7vD" Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-41a442c9dcbso22233735e9.2 for ; Tue, 23 Apr 2024 14:28:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907708; x=1714512508; 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=QFfcFyYCArqf76DF05vZMUVj7hzWciHBal1mxu6DOaA=; b=ixcNr7vDU/k2cT2bSdttyzHvCmo5sDoWS3UTWEpDC65RAV/94A/V9DMWJjZYnASSjM lU+RaunWsDe9CEWxa+HNxcfGLuWDCD8dYTmn7g6N/e9VoTucLBTHw4NtxT4X2JEATmZf y8pJHYRpQVp9hZ7pStUEipa8pijLGQ/fM2jIrO5zm94KBSWFJD3lLuqGQMmSi9+fXcfN XWhOFOPpjki3WFD1E5ALvxIt4+OVOuo8wqSffuGCGB8BqDvarrh7zM/VTOcW8sO3ryK3 sn4XsG6UTc48pJthuW3qG98xi5B08ujIeKjA44c4ZaQJYJMbZFUVWNLImAOYyDUYGMr6 SA1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907708; x=1714512508; 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=QFfcFyYCArqf76DF05vZMUVj7hzWciHBal1mxu6DOaA=; b=oN7NcNkakhkr0AyJDbYgzQZk42qMq0iVqY0RhCEtQ1aMq5HZX6bw2WimwsoV4Esrjw fGd4kjJQNAWlUoAhy/AGNDqqrrSxJ1GYNBljNicfU207bNs/79MCNfzl1QKDtcRSnUQK bcJARiTOK0/CRBzSw3xO7LMWHOjVG+Rv0eRZdVVd8ZXh2tYMhzBI5/+umKmBTDNZzWg9 3rGffrhuUNWH06HNr+KQYDxLSY089gkg4Z7xjwOvEJh9OelLxID3avyqBB8csNZTMO38 lEX1EHNENXwaxgO3yydfWDDAvjt7ZXO4ecKfUT1FCugngbOS8wVvp+yPkkBVO/jeDVK9 ju/Q== X-Forwarded-Encrypted: i=1; AJvYcCUV37JGL7xuSFbHr+JtPkg528/JGdLJpBGf8J5Y8E0ElYt8527OMEY0dmUqNkdd9x8v0smiSNbDEv2HLX41u1L+OPgK X-Gm-Message-State: AOJu0YxnV4CTAL/jJUHEKPSzcB0Br1niT5rM508zKld80Rw9KJGYO89Z ZYlmtrjhji6UcrMVBDtfDYcesS6vw/6kanBmrGPmVXxCSJR2snyCW7VBzQ== X-Google-Smtp-Source: AGHT+IEYXvXf6Odd/GK76yPBW4Zlm4toS3QvfuulTOQDzFpK1lB9YbSSwpFOOLsqPim2GO08u0T4jw== X-Received: by 2002:a05:6000:156f:b0:343:71c2:287e with SMTP id 15-20020a056000156f00b0034371c2287emr293949wrz.59.1713907707898; Tue, 23 Apr 2024 14:28:27 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:27 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 6/8] update-ref: support symrefs in the create command Date: Tue, 23 Apr 2024 23:28:16 +0200 Message-ID: <20240423212818.574123-7-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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 'create' commands in 'git-update-ref' allows users to create `` with `` after verifying it does not exist. Extend this command to alternatively take in `ref:` which is used to create a symref with `` as its target. 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 | 8 +++-- builtin/clone.c | 2 +- builtin/update-ref.c | 14 ++++++-- 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 | 55 ++++++++++++++++++++++++++++++++ 9 files changed, 173 insertions(+), 13 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index f28b026cd7..1202769178 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -62,7 +62,7 @@ With `--stdin`, update-ref reads instructions from standard input and performs all modifications together. Specify commands of the form: update SP SP [SP ] LF - create SP SP LF + create SP SP ( | ref:) LF delete SP [SP ( | ref:)] LF verify SP [SP ( | ref:)] LF option SP LF @@ -83,7 +83,7 @@ Alternatively, use `-z` to specify in NUL-terminated format, without quoting: update SP NUL NUL [] NUL - create SP NUL NUL + create SP NUL ( | ref:) NUL delete SP NUL [( | ref:)] NUL verify SP NUL [( | ref:)] NUL option SP NUL @@ -113,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. 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 cee7a5ebc0..afab706cd7 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -244,6 +244,7 @@ static void parse_cmd_create(struct ref_transaction *transaction, const char *next, const char *end) { struct strbuf err = STRBUF_INIT; + struct strbuf new_target = STRBUF_INIT; char *refname; struct object_id new_oid; @@ -251,16 +252,22 @@ static void parse_cmd_create(struct ref_transaction *transaction, if (!refname) die("create: missing "); - if (parse_next_arg(&next, end, &new_oid, NULL, "create", refname, 0)) + if (parse_next_arg(&next, end, &new_oid, &new_target, + "create", refname, PARSE_REFNAME_TARGETS)) die("create %s: missing ", refname); - if (is_null_oid(&new_oid)) + if (!new_target.len && is_null_oid(&new_oid)) die("create %s: zero ", refname); + if (new_target.len && !(update_flags & REF_NO_DEREF)) + die("create %s: cannot create symrefs in deref mode", refname); + 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_target.len ? NULL : &new_oid , + new_target.len ? new_target.buf : NULL, update_flags | create_reflog_flag, msg, &err)) die("%s", err.buf); @@ -268,6 +275,7 @@ static void parse_cmd_create(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); + strbuf_release(&new_target); } static void parse_cmd_delete(struct ref_transaction *transaction, 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..1291242940 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 + create TESTSYMREFONE ref:refs/heads/new + prepare + commit + EOF + git update-ref --no-deref --stdin stdin <<-EOF && + start + create TESTSYMREFONE ref: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 cd1ad0d2ec..e85d08ce5c 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1758,6 +1758,61 @@ do test_must_fail git rev-parse --verify -q $b ' + test_expect_success "stdin ${type} create symref fails without --no-deref" ' + create_stdin_buf ${type} "create refs/heads/symref" "ref:$a" && + test_must_fail git update-ref --stdin ${type} err && + grep "fatal: create refs/heads/symref: cannot create symrefs in deref mode" err + ' + + test_expect_success "stdin ${type} create symref fails with too many arguments" ' + create_stdin_buf ${type} "create refs/heads/symref" "ref:$a" "ref:$a" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: ref:$a" err + else + grep "fatal: create refs/heads/symref: extra input: ref:$a" err + fi + ' + + test_expect_success "stdin ${type} create symref ref works" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "create refs/heads/symref" "ref:$a" >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} "create refs/heads/symref" "ref: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} create symref does not create reflogs by default" ' + test_when_finished "git symbolic-ref -d refs/symref" && + create_stdin_buf ${type} "create refs/symref" "ref:$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} create symref reflogs with --create-reflog" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "create refs/heads/symref" "ref:$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 Tue Apr 23 21:28:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13640742 Received: from mail-ed1-f47.google.com (mail-ed1-f47.google.com [209.85.208.47]) (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 99D321442EA for ; Tue, 23 Apr 2024 21:28:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907712; cv=none; b=IquZVw5Upt7ho/UrO+VqR3uNoONgpknfZs0B4Ky/5FRS6ieJQYAYmmr2VFjKjFaL6pyqYjubPMAP0yWirZyn0RHR/SGkz07jT3PPXzQTN+9hlBSyIWGnu0sdKCGXMP9O2jSEy+B2pMi2u0N8+CvOQQcNjZIdG4DgcPScC4b16LI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907712; c=relaxed/simple; bh=xCkH4kMNYtG7gRFuIn97PvbIOVIifywzwZqOuuJd1wg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bq3For7AlZq/Wc/h4UZaV58A1kENTqfWj/aITYXdmcIURk0kXOHOPIJbjXbG4G47tSQi4/jLpUPIvYyS1RhbRD3THH+gAgXYGuAeUh3yyKla9d+x9H9poJMMasXgoxDwc6ukWwZ2DpUS11ba0w9tA8EtmiBZGfGUKBtpIAvic9E= 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=lsngS6dP; arc=none smtp.client-ip=209.85.208.47 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="lsngS6dP" Received: by mail-ed1-f47.google.com with SMTP id 4fb4d7f45d1cf-56e1bbdb362so6643980a12.1 for ; Tue, 23 Apr 2024 14:28:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907709; x=1714512509; 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=MUB6n1NsU2/7GWgTVD6aANG6lX22V95O/7uyuMDt2LI=; b=lsngS6dPPzmb0Txarryv9X0ydrEB27VRZVYn7bvP8ltkMtd8uGmxMBwo4USgeC2M5P Cd793+ghufjuAuuK3mZ9Bb3UlbSoxnme4iI92TJ7tNdFrCiLMY7gORfOBoprAyg80z17 7eKXdl4RYKCUeQJcSFCYIDbdBym5Ssrnhu/LXdw2iH2iTUFD+BFFKehUqqjXqf4VajYh wtALmlh/cCRwCIZinyNUuhFCKY9oRsutPRzqlNqpIx4nTYXYzTyTMAVzVqm8Isx/OR11 UA9M0SZcP3xfaG345Z2mZEVAtBfaMxxd8xhYeHO0Mw6O8gUB4Mj96Tm5KGe4Ee3JKNuO sS2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907709; x=1714512509; 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=MUB6n1NsU2/7GWgTVD6aANG6lX22V95O/7uyuMDt2LI=; b=t2U+caZhIr3HriWY4oxbYbtBXBlrwkXrWfQn74qrojV52dC9WNi+0XEJo1gq1gOkQ8 m8s7s0EhTR/FQ325yXLWcHbr0esK/hVcJahOJjtJLQx9xqOLDqXtUN5Of1BpVylmWNZf gQHLUj63psLs4mDPYAKQxlxZVYE74bPrfbV46lWOTxFFF/d64XcLu9wilU/AaDJqfzU0 kox7jYzrB5a0DsO7rqoL8ufY06RzK7i7vIjSg0xSOHa7kHjm5jrFlhJ074Hhz20TA7kf 00GLLw8b+/9XCVB3c5ct+lqi6dQuzG0KLKMM45hq7BGDgWDRbmcuFqNTy5oSpSDgtPiM oToQ== X-Forwarded-Encrypted: i=1; AJvYcCU7RMgDq5EYNQw6EiTtz1kuruvzKmp2j8fGFTOrCl2vyCIam69MshuOmIJpNpbXAmIH6JbpqFBLbxjoIcDCVIAksO2+ X-Gm-Message-State: AOJu0YxEZnJm1DXrIDytUp9ly2QUzBiSviyOWkX3B6u+NyMLkPLmqhwX Mb0KnyiVW9EU58X0RKeDdgBCxtzDpZGgyM2N1EDCnUQs5mumzjYQ X-Google-Smtp-Source: AGHT+IFkAyJ2xLrAPOgSChFJUnUbY8BmzY4+vY9LTeg+Yo8UP+zrLVCDRilf2LzSBCYNyst0izMKCA== X-Received: by 2002:a17:906:2412:b0:a58:7493:2cc6 with SMTP id z18-20020a170906241200b00a5874932cc6mr456687eja.34.1713907708740; Tue, 23 Apr 2024 14:28:28 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:28 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 7/8] update-ref: support symrefs in the update command Date: Tue, 23 Apr 2024 23:28:17 +0200 Message-ID: <20240423212818.574123-8-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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 'update' command in 'git-update-ref' allows users to set `` to `` after verifying ``, if given. Extend this command to alternatively take in `ref:` which is used to update to/a symref with `` as its target. And take in `ref:` which is used to verify that the symref points to `` before the update. With this the 'update' command can also now be used to: - create symrefs - change a regular ref to a symref - change a symref to a regular ref 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 | 10 +- builtin/update-ref.c | 21 ++-- refs/files-backend.c | 14 ++- refs/reftable-backend.c | 3 +- t/t1400-update-ref.sh | 168 +++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 18 deletions(-) diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index 1202769178..79e29fead6 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -61,7 +61,7 @@ still contains . With `--stdin`, update-ref reads instructions from standard input and performs all modifications together. Specify commands of the form: - update SP SP [SP ] LF + update SP SP ( | ref:) [SP ( | ref:)] LF create SP SP ( | ref:) LF delete SP [SP ( | ref:)] LF verify SP [SP ( | ref:)] LF @@ -82,7 +82,7 @@ specify a missing value, omit the value and its preceding SP entirely. Alternatively, use `-z` to specify in NUL-terminated format, without quoting: - update SP NUL NUL [] NUL + update SP NUL ( | ref:) NUL [( | ref:)] NUL create SP NUL ( | ref:) NUL delete SP NUL [( | ref:)] NUL verify SP NUL [( | ref:)] NUL @@ -109,7 +109,11 @@ update:: Set to after verifying , if given. Specify a zero to ensure the ref does not exist after the update and/or a zero to make sure the - ref does not exist before the update. + ref does not exist before the update. If ref: + is provided, we verify that the is an existing symbolic + ref which targets . If ref: is given, + the update ensures is a symbolic ref which targets + . create:: Create with after verifying it does not diff --git a/builtin/update-ref.c b/builtin/update-ref.c index afab706cd7..175579148f 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -210,6 +210,8 @@ static void parse_cmd_update(struct ref_transaction *transaction, const char *next, const char *end) { struct strbuf err = STRBUF_INIT; + struct strbuf new_target = STRBUF_INIT; + struct strbuf old_target = STRBUF_INIT; char *refname; struct object_id new_oid, old_oid; int have_old; @@ -218,19 +220,24 @@ static void parse_cmd_update(struct ref_transaction *transaction, if (!refname) die("update: missing "); - if (parse_next_arg(&next, end, &new_oid, NULL, - "update", refname, PARSE_SHA1_ALLOW_EMPTY)) + if (parse_next_arg(&next, end, &new_oid, + &new_target, "update", refname, + PARSE_SHA1_ALLOW_EMPTY | PARSE_REFNAME_TARGETS)) die("update %s: missing ", refname); - have_old = !parse_next_arg(&next, end, &old_oid, NULL, - "update", refname, PARSE_SHA1_OLD); + have_old = !parse_next_arg(&next, end, &old_oid, + &old_target, "update", refname, + PARSE_SHA1_OLD | PARSE_REFNAME_TARGETS); + have_old = have_old & !old_target.len; 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, - NULL, NULL, + new_target.len ? NULL : &new_oid, + have_old ? &old_oid : NULL, + new_target.len ? new_target.buf : NULL, + old_target.len ? old_target.buf : NULL, update_flags | create_reflog_flag, msg, &err)) die("%s", err.buf); @@ -238,6 +245,8 @@ static void parse_cmd_update(struct ref_transaction *transaction, update_flags = default_flags; free(refname); strbuf_release(&err); + strbuf_release(&old_target); + strbuf_release(&new_target); } static void parse_cmd_create(struct ref_transaction *transaction, 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 e85d08ce5c..5b2d23da37 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 && @@ -1813,6 +1814,173 @@ do git reflog exists refs/heads/symref ' + test_expect_success "stdin ${type} update symref fails with too many arguments" ' + create_stdin_buf ${type} "update refs/heads/symref" "ref:$a" "ref:$a" "ref:$a" >stdin && + test_must_fail git update-ref --stdin ${type} --no-deref err && + if test "$type" = "-z" + then + grep "fatal: unknown command: ref:$a" err + else + grep "fatal: update refs/heads/symref: extra input: ref:$a" err + fi + ' + + test_expect_success "stdin ${type} update creates symref with zero old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "update refs/heads/symref" "ref:$a" "$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} update creates symref with empty old value" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "update refs/heads/symref" "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} update symref 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} "update refs/heads/symref" "ref:$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} update symref 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} "update refs/heads/symref" "ref:$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} update creates symref (with deref)" ' + test_when_finished "git symbolic-ref -d refs/heads/symref" && + create_stdin_buf ${type} "update refs/heads/symref" "ref:$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} 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} "update refs/heads/regularref" "ref:$a" "$(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} 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} "update refs/heads/regularref" "ref:$a" "$(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} 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} "update refs/heads/symref" "ref:$a" "$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} update existing symref to regular ref" ' + test_when_finished "git update-ref -d refs/heads/symref" && + git symbolic-ref refs/heads/symref refs/heads/target2 && + create_stdin_buf ${type} "update refs/heads/symref" "$(git rev-parse $a)" "ref:refs/heads/target2" >stdin && + git update-ref --stdin ${type} --no-deref expect && + git rev-parse 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 refs/heads/target2) $(git rev-parse $a)" actual + ' + done +test_expect_success "stdin update 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 && + echo "update refs/heads/symref" "ref:$a" >stdin && + git update-ref --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 -z update 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 && + printf "$F" "update refs/heads/symref" "ref:$a" "" >stdin && + git update-ref --stdin -z 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 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 && + echo "update refs/heads/regularref" "ref:$a" >stdin && + git update-ref --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 +' + +test_expect_success "stdin -z 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 && + printf "$F" "update refs/heads/regularref" "ref:$a" "" >stdin && + git update-ref --stdin -z 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_done From patchwork Tue Apr 23 21:28:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: karthik nayak X-Patchwork-Id: 13640743 Received: from mail-ej1-f44.google.com (mail-ej1-f44.google.com [209.85.218.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 83E5C144303 for ; Tue, 23 Apr 2024 21:28:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907713; cv=none; b=Y+tLHN4rAXfpue3/WG76G2RkYW45NKGLibo4K4AEXLlSAptdAbX4YB06DUloBVradCtFOVthW4Xkx3oUAaFpRfxVZI51mvKbwhRtym+EZ60mOgGKDN6QVapc74iCdUPwGX0nJ7LVN4T/SRQXKKY18lTgMaqPWKJ8b6ZfsulqzIQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1713907713; c=relaxed/simple; bh=6r0yQ70bduEocKoDsIHvb8qrURky95sisuMv7OYIrZ4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C7Tb8GnPCg+KDZxCOj4cyhMjomm7TmWQpgfuDYdnaUTW/7/YQAhJ3dEf7kVDT1JdEM8jarstguK+AXbDwdkvU6lalBKn4ZJfwPtiaop7S1l3p4qPrRjjfbzQIu3gTf9Owk9r60wRr1CAvx4KDRZ5TildASUTPGhFssK1GZftJPE= 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=XPitFhs+; arc=none smtp.client-ip=209.85.218.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="XPitFhs+" Received: by mail-ej1-f44.google.com with SMTP id a640c23a62f3a-a519e1b0e2dso712857766b.2 for ; Tue, 23 Apr 2024 14:28:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713907710; x=1714512510; 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=Q42Vdyip1qfE4ddcpzduG9+YyyuKi5i6SWIVglG/zv0=; b=XPitFhs+0SQbwvM9+MbUS3jXhXHZHqe8uqU1rLWSlDSQdo5y87BFy/1E2y5zdENs+i 6gQi+f56hcM2r4i+e28UsvASIQu4b/h6GNTtAnqihBs6ZjY8LtWap3iBRYLygOMPe01w 3ZjBZafgKF0HQRes6BuBZbpsoYP+JRm9NZUALHlbxibzaJTvPzOOWtKo1HQ8lRocEgw6 uDiZO5/fEm4ll95OYdGvV/rni6kowLBcbJd6DWUdsskaC7qFV051uZ06B7ow1weL+1gI tdFC6R/1me4DkHrXbEj2kH2P96eTf5Rt+BgmHkGRBrjSuo8HjWULJOFXYYQfUNaohLhM pNDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713907710; x=1714512510; 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=Q42Vdyip1qfE4ddcpzduG9+YyyuKi5i6SWIVglG/zv0=; b=ULYGRqZs8cMI59iNXN+8sLpPf6dC4bG6/Y19tLBXdwiNFPwr+Rg97dvMOK0G7zOQqc gFYo5dFhLBzmV7EGIr2OiZFebCLCM+ppwN8z6RIiX7BkfMLRPCzqeymYjP+z7fAM49pp nMJjz7BelVBYlD/w+vEtiRw2xQ2d8+UL8Euzk7MW3VJK4vTiQc1akmOHbQVbN6iYTigD D1MkfQNURI081PV0jAUAgTKldTayIqID4BxxlT9OWN88Ql3+A0rt4BmVuEwMS0JC4STv qGiFEQ29vBOVzhLmyJz2SXD6+3uHbsjGuXrhuPHGOL5+Q2i5ZD8+fHGwAwvRPiMWiKMl EHfA== X-Forwarded-Encrypted: i=1; AJvYcCUHIODe1XLjEDRiCj5iXdhM6N6xSYX99TXVtC0Izk4Lzu+LUSYL/GODSxeWfLCeW+5N7Sc4jGIlScnLznNo6U6tRQZe X-Gm-Message-State: AOJu0YyCMWf10C/LBSh+IZJqZQKQJ18jzhpeh5f1RlOLQWQ15P1/NA9D Kh43sWUknpOTGu3NXmb5Noq0TX1hmviTC2yRJXtZAGjdPofU7gMxDOA1uQ== X-Google-Smtp-Source: AGHT+IE1MX+X1dJasoxI/HOZEpmZvfM0GHAOu8ia3w/G8D7QOBB5m8MqYIBORo45DBxUF2HCKfe9ig== X-Received: by 2002:a17:906:2412:b0:a55:5a0a:a74b with SMTP id z18-20020a170906241200b00a555a0aa74bmr379043eja.33.1713907709797; Tue, 23 Apr 2024 14:28:29 -0700 (PDT) Received: from laptop.fritz.box ([2a02:2455:826e:4900:23ba:342:e06:b489]) by smtp.gmail.com with ESMTPSA id f17-20020a17090660d100b00a587236e646sm1864275ejk.174.2024.04.23.14.28.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 23 Apr 2024 14:28:29 -0700 (PDT) From: Karthik Nayak X-Google-Original-From: Karthik Nayak To: karthik.188@gmail.com Cc: chris.torek@gmail.com, git@vger.kernel.org, gitster@pobox.com, ps@pks.im Subject: [PATCH v3 8/8] ref: support symrefs in 'reference-transaction' hook Date: Tue, 23 Apr 2024 23:28:18 +0200 Message-ID: <20240423212818.574123-9-knayak@gitlab.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240423212818.574123-1-knayak@gitlab.com> References: <20240412095908.1134387-1-knayak@gitlab.com> <20240423212818.574123-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..abd4777819 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 + verify refs/heads/symref ref:refs/heads/main + create refs/heads/symrefc ref:refs/heads/main + update refs/heads/symrefu ref:refs/heads/branch ref:refs/heads/main + prepare + commit + EOF + test_cmp expect actual +' + test_done