update-ref: handle rejections while adding updates

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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed May 4, 2026 at 19:44 UTC e31a10418a4c2270651bab326f4715892db9c3ee
1 file changed +98 -39
builtin/update-ref.c
+98 -39
@@ -25,6 +25,15 @@ static unsigned int default_flags;
25 static unsigned create_reflog_flag;
26 static const char *msg;
27
28 +struct command_options {
29 + /*
30 + * Individual updates are allowed to fail without causing
31 + * update-ref to exit. This is set when using the
32 + * '--batch-updates' flag.
33 + */
34 + bool allow_update_failures;
35 +};
36 +
37 /*
38 * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
39 * and append the result to arg. Return a pointer to the terminator.
@@ -257,6 +266,31 @@ static void print_rejected_refs(const char *refname,
266 strbuf_release(&sb);
267 }
268
269 +/*
270 + * Handle transaction errors. If we're using batches updates, we want to only
271 + * die for generic errors and print the remaining to the user.
272 + */
273 +static void handle_ref_transaction_error(const char *refname,
274 + struct object_id *new_oid,
275 + struct object_id *old_oid,
276 + const char *new_target,
277 + const char *old_target,
278 + enum ref_transaction_error tx_err,
279 + struct strbuf *err,
280 + struct command_options *opts)
281 +{
282 + if (!tx_err)
283 + return;
284 +
285 + if (tx_err != REF_TRANSACTION_ERROR_GENERIC && opts->allow_update_failures) {
286 + print_rejected_refs(refname, old_oid, new_oid, old_target,
287 + new_target, tx_err, err->buf, NULL);
288 + return;
289 + }
290 +
291 + die("%s", err->buf);
292 +}
293 +
294 /*
295 * The following five parse_cmd_*() functions parse the corresponding
296 * command. In each case, next points at the character following the
@@ -268,11 +302,13 @@ static void print_rejected_refs(const char *refname,
302 */
303
304 static void parse_cmd_update(struct ref_transaction *transaction,
271 - const char *next, const char *end)
305 + const char *next, const char *end,
306 + struct command_options *opts)
307 {
308 struct strbuf err = STRBUF_INIT;
309 char *refname;
310 struct object_id new_oid, old_oid;
311 + enum ref_transaction_error tx_err;
312 int have_old;
313
314 refname = parse_refname(&next);
@@ -289,12 +325,14 @@ static void parse_cmd_update(struct ref_transaction *transaction,
325 if (*next != line_termination)
326 die("update %s: extra input: %s", refname, next);
327
292 - if (ref_transaction_update(transaction, refname,
293 - &new_oid, have_old ? &old_oid : NULL,
294 - NULL, NULL,
295 - update_flags | create_reflog_flag,
296 - msg, &err))
297 - die("%s", err.buf);
328 + tx_err = ref_transaction_update(transaction, refname,
329 + &new_oid, have_old ? &old_oid : NULL,
330 + NULL, NULL,
331 + update_flags | create_reflog_flag,
332 + msg, &err);
333 + handle_ref_transaction_error(refname, &new_oid, have_old ? &old_oid : NULL,
334 + NULL, NULL, tx_err, &err, opts);
335 +
336
337 update_flags = default_flags;
338 free(refname);
@@ -302,9 +340,11 @@ static void parse_cmd_update(struct ref_transaction *transaction,
340 }
341
342 static void parse_cmd_symref_update(struct ref_transaction *transaction,
305 - const char *next, const char *end UNUSED)
343 + const char *next, const char *end UNUSED,
344 + struct command_options *opts)
345 {
346 char *refname, *new_target, *old_arg;
347 + enum ref_transaction_error tx_err;
348 char *old_target = NULL;
349 struct strbuf err = STRBUF_INIT;
350 struct object_id old_oid;
@@ -341,13 +381,15 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
381 if (*next != line_termination)
382 die("symref-update %s: extra input: %s", refname, next);
383
344 - if (ref_transaction_update(transaction, refname, NULL,
345 - have_old_oid ? &old_oid : NULL,
346 - new_target,
347 - have_old_oid ? NULL : old_target,
348 - update_flags | create_reflog_flag,
349 - msg, &err))
350 - die("%s", err.buf);
384 + tx_err = ref_transaction_update(transaction, refname, NULL,
385 + have_old_oid ? &old_oid : NULL,
386 + new_target,
387 + have_old_oid ? NULL : old_target,
388 + update_flags | create_reflog_flag,
389 + msg, &err);
390 + handle_ref_transaction_error(refname, NULL, have_old_oid ? &old_oid : NULL,
391 + new_target, have_old_oid ? NULL : old_target,
392 + tx_err, &err, opts);
393
394 update_flags = default_flags;
395 free(refname);
@@ -358,11 +400,13 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
400 }
401
402 static void parse_cmd_create(struct ref_transaction *transaction,
361 - const char *next, const char *end)
403 + const char *next, const char *end,
404 + struct command_options *opts)
405 {
406 struct strbuf err = STRBUF_INIT;
407 char *refname;
408 struct object_id new_oid;
409 + enum ref_transaction_error tx_err;
410
411 refname = parse_refname(&next);
412 if (!refname)
@@ -377,22 +421,24 @@ static void parse_cmd_create(struct ref_transaction *transaction,
421 if (*next != line_termination)
422 die("create %s: extra input: %s", refname, next);
423
380 - if (ref_transaction_create(transaction, refname, &new_oid, NULL,
381 - update_flags | create_reflog_flag,
382 - msg, &err))
383 - die("%s", err.buf);
424 + tx_err = ref_transaction_create(transaction, refname, &new_oid, NULL,
425 + update_flags | create_reflog_flag,
426 + msg, &err);
427 + handle_ref_transaction_error(refname, &new_oid, NULL, NULL, NULL, tx_err,
428 + &err, opts);
429
430 update_flags = default_flags;
431 free(refname);
432 strbuf_release(&err);
433 }
434
390 -
435 static void parse_cmd_symref_create(struct ref_transaction *transaction,
392 - const char *next, const char *end UNUSED)
436 + const char *next, const char *end UNUSED,
437 + struct command_options *opts)
438 {
439 struct strbuf err = STRBUF_INIT;
440 char *refname, *new_target;
441 + enum ref_transaction_error tx_err;
442
443 refname = parse_refname(&next);
444 if (!refname)
@@ -405,10 +451,11 @@ static void parse_cmd_symref_create(struct ref_transaction *transaction,
451 if (*next != line_termination)
452 die("symref-create %s: extra input: %s", refname, next);
453
408 - if (ref_transaction_create(transaction, refname, NULL, new_target,
409 - update_flags | create_reflog_flag,
410 - msg, &err))
411 - die("%s", err.buf);
454 + tx_err = ref_transaction_create(transaction, refname, NULL, new_target,
455 + update_flags | create_reflog_flag,
456 + msg, &err);
457 + handle_ref_transaction_error(refname, NULL, NULL, new_target, NULL,
458 + tx_err, &err, opts);
459
460 update_flags = default_flags;
461 free(refname);
@@ -417,7 +464,8 @@ static void parse_cmd_symref_create(struct ref_transaction *transaction,
464 }
465
466 static void parse_cmd_delete(struct ref_transaction *transaction,
420 - const char *next, const char *end)
467 + const char *next, const char *end,
468 + struct command_options *opts UNUSED)
469 {
470 struct strbuf err = STRBUF_INIT;
471 char *refname;
@@ -450,9 +498,9 @@ static void parse_cmd_delete(struct ref_transaction *transaction,
498 strbuf_release(&err);
499 }
500
453 -
501 static void parse_cmd_symref_delete(struct ref_transaction *transaction,
455 - const char *next, const char *end UNUSED)
502 + const char *next, const char *end UNUSED,
503 + struct command_options *opts UNUSED)
504 {
505 struct strbuf err = STRBUF_INIT;
506 char *refname, *old_target;
@@ -479,9 +527,9 @@ static void parse_cmd_symref_delete(struct ref_transaction *transaction,
527 strbuf_release(&err);
528 }
529
482 -
530 static void parse_cmd_verify(struct ref_transaction *transaction,
484 - const char *next, const char *end)
531 + const char *next, const char *end,
532 + struct command_options *opts UNUSED)
533 {
534 struct strbuf err = STRBUF_INIT;
535 char *refname;
@@ -508,7 +556,8 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
556 }
557
558 static void parse_cmd_symref_verify(struct ref_transaction *transaction,
511 - const char *next, const char *end UNUSED)
559 + const char *next, const char *end UNUSED,
560 + struct command_options *opts UNUSED)
561 {
562 struct strbuf err = STRBUF_INIT;
563 struct object_id old_oid;
@@ -550,7 +599,8 @@ static void report_ok(const char *command)
599 }
600
601 static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
553 - const char *next, const char *end UNUSED)
602 + const char *next, const char *end UNUSED,
603 + struct command_options *opts UNUSED)
604 {
605 const char *rest;
606 if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination)
@@ -560,7 +610,8 @@ static void parse_cmd_option(struct ref_transaction *transaction UNUSED,
610 }
611
612 static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
563 - const char *next, const char *end UNUSED)
613 + const char *next, const char *end UNUSED,
614 + struct command_options *opts UNUSED)
615 {
616 if (*next != line_termination)
617 die("start: extra input: %s", next);
@@ -568,7 +619,8 @@ static void parse_cmd_start(struct ref_transaction *transaction UNUSED,
619 }
620
621 static void parse_cmd_prepare(struct ref_transaction *transaction,
571 - const char *next, const char *end UNUSED)
622 + const char *next, const char *end UNUSED,
623 + struct command_options *opts UNUSED)
624 {
625 struct strbuf error = STRBUF_INIT;
626 if (*next != line_termination)
@@ -579,7 +631,8 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
631 }
632
633 static void parse_cmd_abort(struct ref_transaction *transaction,
582 - const char *next, const char *end UNUSED)
634 + const char *next, const char *end UNUSED,
635 + struct command_options *opts UNUSED)
636 {
637 struct strbuf error = STRBUF_INIT;
638 if (*next != line_termination)
@@ -590,7 +643,8 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
643 }
644
645 static void parse_cmd_commit(struct ref_transaction *transaction,
593 - const char *next, const char *end UNUSED)
646 + const char *next, const char *end UNUSED,
647 + struct command_options *opts UNUSED)
648 {
649 struct strbuf error = STRBUF_INIT;
650 if (*next != line_termination)
@@ -618,7 +672,8 @@ enum update_refs_state {
672
673 static const struct parse_cmd {
674 const char *prefix;
621 - void (*fn)(struct ref_transaction *, const char *, const char *);
675 + void (*fn)(struct ref_transaction *, const char *, const char *,
676 + struct command_options *);
677 unsigned args;
678 enum update_refs_state state;
679 } command[] = {
@@ -644,6 +699,10 @@ static void update_refs_stdin(unsigned int flags)
699 struct ref_transaction *transaction;
700 int i, j;
701
702 + struct command_options opts = {
703 + .allow_update_failures = flags & REF_TRANSACTION_ALLOW_FAILURE,
704 + };
705 +
706 transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
707 flags, &err);
708 if (!transaction)
@@ -721,7 +780,7 @@ static void update_refs_stdin(unsigned int flags)
780 }
781
782 cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args,
724 - input.buf + input.len);
783 + input.buf + input.len, &opts);
784 }
785
786 switch (state) {