From: Karthik Nayak <karthik.188@gmail.com>
To: git@vger.kernel.org
Cc: Karthik Nayak <karthik.188@gmail.com>, ps@pks.im, toon@iotcl.com
Subject: [PATCH v3 6/9] update-ref: handle rejections while adding updates
Date: Mon, 27 Apr 2026 12:42:07 +0200 [thread overview]
Message-ID: <20260427-refs-move-to-generic-layer-v3-6-e4638dfb7897@gmail.com> (raw)
In-Reply-To: <20260427-refs-move-to-generic-layer-v3-0-e4638dfb7897@gmail.com>
When using git-update-ref(1) with the '--batch-updates' flag, updates
rejected by the reference backend are displayed to the user while other
updates are applied. This only applies during the commit phase of the
transaction.
In the following commits, we'll also extend `ref_transaction_update()`
to reject updates before a transaction is prepared/committed. In
preparation, modify the code in update-ref to also handle non-generic
rejections from `ref_transaction_update()`. This involves propagating
information to each of the commands on whether updates are allowed to be
rejected, and also checking for rejections and only dying for generic
failures.
Errors encountered during updates will be shown to the user immediately
unlike other errors encountered only when the transaction is
prepared/committed. As the verification of object IDs and peeled tag
objects will move into `ref_transaction_update()` in the following
commit, this means that those errors will be shown to the user before
other errors, this changes the order of errors, but the functionality
remains the same.
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
builtin/update-ref.c | 106 +++++++++++++++++++++++++++++++++++++--------------
1 file changed, 77 insertions(+), 29 deletions(-)
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 5259cc7226..348b7fec94 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -25,6 +25,15 @@ static unsigned int default_flags;
static unsigned create_reflog_flag;
static const char *msg;
+struct command_options {
+ /*
+ * Individual updates are allowed to fail without causing
+ * update-ref to exit. This is set when using the
+ * '--batch-updates' flag.
+ */
+ bool allow_update_failures;
+};
+
/*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument
* and append the result to arg. Return a pointer to the terminator.
@@ -268,11 +277,13 @@ static void print_rejected_refs(const char *refname,
*/
static void parse_cmd_update(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end,
+ struct command_options *opts)
{
struct strbuf err = STRBUF_INIT;
char *refname;
struct object_id new_oid, old_oid;
+ enum ref_transaction_error tx_err;
int have_old;
refname = parse_refname(&next);
@@ -289,22 +300,35 @@ static void parse_cmd_update(struct ref_transaction *transaction,
if (*next != line_termination)
die("update %s: extra input: %s", refname, next);
- if (ref_transaction_update(transaction, refname,
- &new_oid, have_old ? &old_oid : NULL,
- NULL, NULL,
- update_flags | create_reflog_flag,
- msg, &err))
+ tx_err = ref_transaction_update(transaction, refname,
+ &new_oid, have_old ? &old_oid : NULL,
+ NULL, NULL,
+ update_flags | create_reflog_flag,
+ msg, &err);
+
+ /*
+ * Generic errors are non-recoverable, so we cannot skip the update
+ * or mark it as rejected.
+ */
+ if (tx_err == REF_TRANSACTION_ERROR_GENERIC)
die("%s", err.buf);
+ if (tx_err && opts->allow_update_failures)
+ print_rejected_refs(refname, have_old ? &old_oid : NULL,
+ &new_oid, NULL, NULL, tx_err, err.buf,
+ NULL);
+
update_flags = default_flags;
free(refname);
strbuf_release(&err);
}
static void parse_cmd_symref_update(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts)
{
char *refname, *new_target, *old_arg;
+ enum ref_transaction_error tx_err;
char *old_target = NULL;
struct strbuf err = STRBUF_INIT;
struct object_id old_oid;
@@ -341,14 +365,25 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
if (*next != line_termination)
die("symref-update %s: extra input: %s", refname, next);
- if (ref_transaction_update(transaction, refname, NULL,
- have_old_oid ? &old_oid : NULL,
- new_target,
- have_old_oid ? NULL : old_target,
- update_flags | create_reflog_flag,
- msg, &err))
+ tx_err = ref_transaction_update(transaction, refname, NULL,
+ have_old_oid ? &old_oid : NULL,
+ new_target,
+ have_old_oid ? NULL : old_target,
+ update_flags | create_reflog_flag,
+ msg, &err);
+
+ /*
+ * Generic errors are non-recoverable, so we cannot skip the update
+ * or mark it as rejected.
+ */
+ if (tx_err == REF_TRANSACTION_ERROR_GENERIC)
die("%s", err.buf);
+ if (tx_err && opts->allow_update_failures)
+ print_rejected_refs(refname, have_old_oid ? &old_oid : NULL,
+ NULL, have_old_oid ? NULL : old_target,
+ new_target, tx_err, err.buf, NULL);
+
update_flags = default_flags;
free(refname);
free(old_arg);
@@ -358,7 +393,8 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
}
static void parse_cmd_create(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end,
+ struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname;
@@ -387,9 +423,9 @@ static void parse_cmd_create(struct ref_transaction *transaction,
strbuf_release(&err);
}
-
static void parse_cmd_symref_create(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname, *new_target;
@@ -417,7 +453,8 @@ static void parse_cmd_symref_create(struct ref_transaction *transaction,
}
static void parse_cmd_delete(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end,
+ struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname;
@@ -450,9 +487,9 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
strbuf_release(&err);
}
-
static void parse_cmd_symref_delete(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname, *old_target;
@@ -479,9 +516,9 @@ static void parse_cmd_symref_delete(struct ref_transaction *transaction,
strbuf_release(&err);
}
-
static void parse_cmd_verify(struct ref_transaction *transaction,
- const char *next, const char *end)
+ const char *next, const char *end,
+ struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
char *refname;
@@ -508,7 +545,8 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
}
static void parse_cmd_symref_verify(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
struct strbuf err = STRBUF_INIT;
struct object_id old_oid;
@@ -550,7 +588,8 @@ static void report_ok(const char *command)
}
static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
const char *rest;
if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
@@ -560,7 +599,8 @@ static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
}
static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
if (*next != line_termination)
die("start: extra input: %s", next);
@@ -568,7 +608,8 @@ static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
}
static void parse_cmd_prepare(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
struct strbuf error = STRBUF_INIT;
if (*next != line_termination)
@@ -579,7 +620,8 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
}
static void parse_cmd_abort(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
struct strbuf error = STRBUF_INIT;
if (*next != line_termination)
@@ -590,7 +632,8 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
}
static void parse_cmd_commit(struct ref_transaction *transaction,
- const char *next, const char *end UNUSED)
+ const char *next, const char *end UNUSED,
+ struct command_options *opts UNUSED)
{
struct strbuf error = STRBUF_INIT;
if (*next != line_termination)
@@ -618,7 +661,8 @@ enum update_refs_state {
static const struct parse_cmd {
const char *prefix;
- void (*fn)(struct ref_transaction *, const char *, const char *);
+ void (*fn)(struct ref_transaction *, const char *, const char *,
+ struct command_options *);
unsigned args;
enum update_refs_state state;
} command[] = {
@@ -644,6 +688,10 @@ static void update_refs_stdin(unsigned int flags)
struct ref_transaction *transaction;
int i, j;
+ struct command_options opts = {
+ .allow_update_failures = flags & REF_TRANSACTION_ALLOW_FAILURE,
+ };
+
transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
flags, &err);
if (!transaction)
@@ -721,7 +769,7 @@ static void update_refs_stdin(unsigned int flags)
}
cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args,
- input.buf + input.len);
+ input.buf + input.len, &opts);
}
switch (state) {
--
2.53.GIT
next prev parent reply other threads:[~2026-04-27 10:42 UTC|newest]
Thread overview: 68+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-20 10:11 [PATCH 0/8] refs: move some of the generic logic out of the backends Karthik Nayak
2026-04-20 10:11 ` [PATCH 1/8] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 12:20 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 2/8] refs: extract out reflog config to generic layer Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 13:13 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 3/8] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 13:14 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 4/8] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 13:16 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 5/8] update-ref: handle rejections while adding updates Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 14:13 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 6/8] refs: move object parsing to the generic layer Karthik Nayak
2026-04-22 11:15 ` Patrick Steinhardt
2026-04-22 15:03 ` Karthik Nayak
2026-04-20 10:12 ` [PATCH 7/8] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-04-20 10:12 ` [PATCH 8/8] refs: use peeled tag values in reference backends Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 0/9] refs: move some of the generic logic out of the backends Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 1/9] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 2/9] refs: introduce `ref_store_init_options` Karthik Nayak
2026-04-23 8:52 ` Patrick Steinhardt
2026-04-24 9:34 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 3/9] refs: extract out reflog config to generic layer Karthik Nayak
2026-04-23 8:52 ` Patrick Steinhardt
2026-04-23 8:40 ` [PATCH v2 4/9] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-04-24 11:01 ` Toon Claes
2026-04-23 8:40 ` [PATCH v2 5/9] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 6/9] update-ref: handle rejections while adding updates Karthik Nayak
2026-04-23 8:52 ` Patrick Steinhardt
2026-04-24 9:35 ` Karthik Nayak
2026-04-24 11:22 ` Toon Claes
2026-04-27 8:47 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 7/9] refs: move object parsing to the generic layer Karthik Nayak
2026-04-24 12:06 ` Toon Claes
2026-04-27 9:32 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 8/9] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-04-24 16:44 ` Toon Claes
2026-04-27 9:33 ` Karthik Nayak
2026-04-23 8:40 ` [PATCH v2 9/9] refs: use peeled tag values in reference backends Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 0/9] refs: move some of the generic logic out of the backends Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 1/9] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 2/9] refs: introduce `ref_store_init_options` Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 3/9] refs: extract out reflog config to generic layer Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 4/9] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 5/9] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-04-27 10:42 ` Karthik Nayak [this message]
2026-04-29 12:24 ` [PATCH v3 6/9] update-ref: handle rejections while adding updates Toon Claes
2026-04-30 9:52 ` Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 7/9] refs: move object parsing to the generic layer Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 8/9] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-04-27 10:42 ` [PATCH v3 9/9] refs: use peeled tag values in reference backends Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 0/9] refs: move some of the generic logic out of the backends Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 1/9] refs: remove unused typedef 'ref_transaction_commit_fn' Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 2/9] refs: introduce `ref_store_init_options` Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 3/9] refs: extract out reflog config to generic layer Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 4/9] refs: return `ref_transaction_error` from `ref_transaction_update()` Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 5/9] update-ref: move `print_rejected_refs()` up Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 6/9] update-ref: handle rejections while adding updates Karthik Nayak
2026-05-05 5:52 ` Patrick Steinhardt
2026-05-05 8:23 ` Karthik Nayak
2026-05-06 19:44 ` Toon Claes
2026-05-04 17:44 ` [PATCH v4 7/9] refs: move object parsing to the generic layer Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 8/9] refs: add peeled object ID to the `ref_update` struct Karthik Nayak
2026-05-04 17:44 ` [PATCH v4 9/9] refs: use peeled tag values in reference backends Karthik Nayak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260427-refs-move-to-generic-layer-v3-6-e4638dfb7897@gmail.com \
--to=karthik.188@gmail.com \
--cc=git@vger.kernel.org \
--cc=ps@pks.im \
--cc=toon@iotcl.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox