Skip to content

Commit 4747880

Browse files
committed
Merge branch 'kn/non-transactional-batch-updates'
Updating multiple references have only been possible in all-or-none fashion with transactions, but it can be more efficient to batch multiple updates even when some of them are allowed to fail in a best-effort manner. A new "best effort batches of updates" mode has been introduced. * kn/non-transactional-batch-updates: update-ref: add --batch-updates flag for stdin mode refs: support rejection in batch updates during F/D checks refs: implement batch reference update support refs: introduce enum-based transaction error types refs/reftable: extract code from the transaction preparation refs/files: remove duplicate duplicates check refs: move duplicate refname update check to generic layer refs/files: remove redundant check in split_symref_update()
2 parents 4c58159 + 221e8fc commit 4747880

File tree

10 files changed

+969
-523
lines changed

10 files changed

+969
-523
lines changed

Documentation/git-update-ref.adoc

Lines changed: 12 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+
git update-ref [-m <reason>] [--no-deref] [--create-reflog] <ref> <new-oid> [<old-oid>]
13+
git update-ref [-m <reason>] [--no-deref] --stdin [-z] [--batch-updates]
1214

1315
DESCRIPTION
1416
-----------
@@ -57,6 +59,14 @@ 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 `--batch-updates`, update-ref executes the updates in a batch but allows
63+
individual updates to fail due to invalid or incorrect user input, applying only
64+
the successful updates. However, system-related errors—such as I/O failures or
65+
memory issues—will result in a full failure of all batched updates. Any failed
66+
updates will be reported in the following format:
67+
68+
rejected SP (<old-oid> | <old-target>) SP (<new-oid> | <new-target>) SP <rejection-reason> LF
69+
6070
Quote fields containing whitespace as if they were strings in C source
6171
code; i.e., surrounded by double-quotes and with backslash escapes.
6272
Use 40 "0" characters or the empty string to specify a zero value. To

builtin/fetch.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ static int s_update_ref(const char *action,
687687
switch (ref_transaction_commit(our_transaction, &err)) {
688688
case 0:
689689
break;
690-
case TRANSACTION_NAME_CONFLICT:
690+
case REF_TRANSACTION_ERROR_NAME_CONFLICT:
691691
ret = STORE_REF_ERROR_DF_CONFLICT;
692692
goto out;
693693
default:

builtin/update-ref.c

Lines changed: 61 additions & 5 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] [--batch-updates]"),
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', "batch-updates", &flags, N_("batch reference updates"),
791+
REF_TRANSACTION_ALLOW_FAILURE),
738792
OPT_END(),
739793
};
740794

@@ -756,8 +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;
815+
} else if (flags & REF_TRANSACTION_ALLOW_FAILURE) {
816+
die("--batch-updates can only be used with --stdin");
761817
}
762818

763819
if (end_null)

0 commit comments

Comments
 (0)