Skip to content

Commit 9d3d1e1

Browse files
KarthikNayakgitster
authored andcommitted
update-ref: add --allow-partial flag for stdin mode
When updating multiple references through stdin, Git's update-ref command normally aborts the entire transaction if any single update fails. While this atomic behavior prevents partial updates by default, there are cases where applying successful updates while reporting failures is desirable. Add a new `--allow-partial` flag that allows the transaction to continue even when individual reference updates fail. This flag can only be used in `--stdin` mode and builds upon the partial transaction support added to the refs subsystem. When enabled, failed updates are reported in the following format: rejected SP (<old-oid> | <old-target>) SP (<new-oid> | <new-target>) SP <rejection-reason> LF Update the documentation to reflect this change and also tests to cover different scenarios where an update could be rejected. Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 72be623 commit 9d3d1e1

File tree

3 files changed

+309
-8
lines changed

3 files changed

+309
-8
lines changed

Documentation/git-update-ref.adoc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ git-update-ref - Update the object name stored in a ref safely
77

88
SYNOPSIS
99
--------
10-
[verse]
11-
'git update-ref' [-m <reason>] [--no-deref] (-d <ref> [<old-oid>] | [--create-reflog] <ref> <new-oid> [<old-oid>] | --stdin [-z])
10+
[synopsis]
11+
git update-ref [-m <reason>] [--no-deref] -d <ref> [<old-oid>]
12+
[-m <reason>] [--no-deref] [--create-reflog] <ref> <new-oid> [<old-oid>]
13+
[-m <reason>] [--no-deref] --stdin [-z] [--allow-partial]
1214

1315
DESCRIPTION
1416
-----------
@@ -57,6 +59,17 @@ performs all modifications together. Specify commands of the form:
5759
With `--create-reflog`, update-ref will create a reflog for each ref
5860
even if one would not ordinarily be created.
5961

62+
With `--allow-partial`, update-ref continues executing the transaction even if
63+
some updates fail due to invalid or incorrect user input, applying only the
64+
successful updates. Errors resulting from user-provided input are treated as
65+
non-system-related and do not cause the entire transaction to be aborted.
66+
However, system-related errors—such as I/O failures or memory issues—will still
67+
result in a full failure. Additionally, errors like F/D conflicts are batched
68+
for performance optimization and will also cause a full failure. Any failed
69+
updates will be reported in the following format:
70+
71+
rejected SP (<old-oid> | <old-target>) SP (<new-oid> | <new-target>) SP <rejection-reason> LF
72+
6073
Quote fields containing whitespace as if they were strings in C source
6174
code; i.e., surrounded by double-quotes and with backslash escapes.
6275
Use 40 "0" characters or the empty string to specify a zero value. To

builtin/update-ref.c

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "config.h"
66
#include "gettext.h"
77
#include "hash.h"
8+
#include "hex.h"
89
#include "refs.h"
910
#include "object-name.h"
1011
#include "parse-options.h"
@@ -13,7 +14,7 @@
1314
static const char * const git_update_ref_usage[] = {
1415
N_("git update-ref [<options>] -d <refname> [<old-oid>]"),
1516
N_("git update-ref [<options>] <refname> <new-oid> [<old-oid>]"),
16-
N_("git update-ref [<options>] --stdin [-z]"),
17+
N_("git update-ref [<options>] --stdin [-z] [--allow-partial]"),
1718
NULL
1819
};
1920

@@ -565,6 +566,49 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
565566
report_ok("abort");
566567
}
567568

569+
static void print_rejected_refs(const char *refname,
570+
const struct object_id *old_oid,
571+
const struct object_id *new_oid,
572+
const char *old_target,
573+
const char *new_target,
574+
enum ref_transaction_error err,
575+
void *cb_data UNUSED)
576+
{
577+
struct strbuf sb = STRBUF_INIT;
578+
const char *reason = "";
579+
580+
switch (err) {
581+
case REF_TRANSACTION_ERROR_NAME_CONFLICT:
582+
reason = "refname conflict";
583+
break;
584+
case REF_TRANSACTION_ERROR_CREATE_EXISTS:
585+
reason = "reference already exists";
586+
break;
587+
case REF_TRANSACTION_ERROR_NONEXISTENT_REF:
588+
reason = "reference does not exist";
589+
break;
590+
case REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE:
591+
reason = "incorrect old value provided";
592+
break;
593+
case REF_TRANSACTION_ERROR_INVALID_NEW_VALUE:
594+
reason = "invalid new value provided";
595+
break;
596+
case REF_TRANSACTION_ERROR_EXPECTED_SYMREF:
597+
reason = "expected symref but found regular ref";
598+
break;
599+
default:
600+
reason = "unkown failure";
601+
}
602+
603+
strbuf_addf(&sb, "rejected %s %s %s %s\n", refname,
604+
new_oid ? oid_to_hex(new_oid) : new_target,
605+
old_oid ? oid_to_hex(old_oid) : old_target,
606+
reason);
607+
608+
fwrite(sb.buf, sb.len, 1, stdout);
609+
strbuf_release(&sb);
610+
}
611+
568612
static void parse_cmd_commit(struct ref_transaction *transaction,
569613
const char *next, const char *end UNUSED)
570614
{
@@ -573,6 +617,10 @@ static void parse_cmd_commit(struct ref_transaction *transaction,
573617
die("commit: extra input: %s", next);
574618
if (ref_transaction_commit(transaction, &error))
575619
die("commit: %s", error.buf);
620+
621+
ref_transaction_for_each_rejected_update(transaction,
622+
print_rejected_refs, NULL);
623+
576624
report_ok("commit");
577625
ref_transaction_free(transaction);
578626
}
@@ -609,15 +657,15 @@ static const struct parse_cmd {
609657
{ "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED },
610658
};
611659

612-
static void update_refs_stdin(void)
660+
static void update_refs_stdin(unsigned int flags)
613661
{
614662
struct strbuf input = STRBUF_INIT, err = STRBUF_INIT;
615663
enum update_refs_state state = UPDATE_REFS_OPEN;
616664
struct ref_transaction *transaction;
617665
int i, j;
618666

619667
transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
620-
0, &err);
668+
flags, &err);
621669
if (!transaction)
622670
die("%s", err.buf);
623671

@@ -685,7 +733,7 @@ static void update_refs_stdin(void)
685733
*/
686734
state = cmd->state;
687735
transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
688-
0, &err);
736+
flags, &err);
689737
if (!transaction)
690738
die("%s", err.buf);
691739

@@ -701,6 +749,8 @@ static void update_refs_stdin(void)
701749
/* Commit by default if no transaction was requested. */
702750
if (ref_transaction_commit(transaction, &err))
703751
die("%s", err.buf);
752+
ref_transaction_for_each_rejected_update(transaction,
753+
print_rejected_refs, NULL);
704754
ref_transaction_free(transaction);
705755
break;
706756
case UPDATE_REFS_STARTED:
@@ -727,6 +777,8 @@ int cmd_update_ref(int argc,
727777
struct object_id oid, oldoid;
728778
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
729779
int create_reflog = 0;
780+
unsigned int flags = 0;
781+
730782
struct option options[] = {
731783
OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
732784
OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
@@ -735,6 +787,8 @@ int cmd_update_ref(int argc,
735787
OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
736788
OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
737789
OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create a reflog")),
790+
OPT_BIT('0', "allow-partial", &flags, N_("allow partial transactions"),
791+
REF_TRANSACTION_ALLOW_PARTIAL),
738792
OPT_END(),
739793
};
740794

@@ -756,9 +810,10 @@ int cmd_update_ref(int argc,
756810
usage_with_options(git_update_ref_usage, options);
757811
if (end_null)
758812
line_termination = '\0';
759-
update_refs_stdin();
813+
update_refs_stdin(flags);
760814
return 0;
761-
}
815+
} else if (flags & REF_TRANSACTION_ALLOW_PARTIAL)
816+
die("--allow-partial can only be used with --stdin");
762817

763818
if (end_null)
764819
usage_with_options(git_update_ref_usage, options);

0 commit comments

Comments
 (0)