Merge branch 'bc/smart-http-atomic-push'

The atomic push over smart HTTP transport did not work, which has
been corrected.

* bc/smart-http-atomic-push:
  remote-curl: pass on atomic capability to remote side
This commit is contained in:
Junio C Hamano 2019-10-23 14:43:11 +09:00
commit d45d771978
5 changed files with 62 additions and 3 deletions

View File

@ -509,6 +509,11 @@ set by Git if the remote helper has the 'option' capability.
Indicate that only the objects wanted need to be fetched, not Indicate that only the objects wanted need to be fetched, not
their dependents. their dependents.
'option atomic' {'true'|'false'}::
When pushing, request the remote server to update refs in a single atomic
transaction. If successful, all refs will be updated, or none will. If the
remote side does not support this capability, the push will fail.
SEE ALSO SEE ALSO
-------- --------
linkgit:git-remote[1] linkgit:git-remote[1]

View File

@ -40,7 +40,8 @@ struct options {
push_cert : 2, push_cert : 2,
deepen_relative : 1, deepen_relative : 1,
from_promisor : 1, from_promisor : 1,
no_dependents : 1; no_dependents : 1,
atomic : 1;
}; };
static struct options options; static struct options options;
static struct string_list cas_options = STRING_LIST_INIT_DUP; static struct string_list cas_options = STRING_LIST_INIT_DUP;
@ -148,6 +149,14 @@ static int set_option(const char *name, const char *value)
else else
return -1; return -1;
return 0; return 0;
} else if (!strcmp(name, "atomic")) {
if (!strcmp(value, "true"))
options.atomic = 1;
else if (!strcmp(value, "false"))
options.atomic = 0;
else
return -1;
return 0;
} else if (!strcmp(name, "push-option")) { } else if (!strcmp(name, "push-option")) {
if (*value != '"') if (*value != '"')
string_list_append(&options.push_options, value); string_list_append(&options.push_options, value);
@ -1196,6 +1205,8 @@ static int push_git(struct discovery *heads, int nr_spec, const char **specs)
argv_array_push(&args, "--signed=yes"); argv_array_push(&args, "--signed=yes");
else if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED) else if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED)
argv_array_push(&args, "--signed=if-asked"); argv_array_push(&args, "--signed=if-asked");
if (options.atomic)
argv_array_push(&args, "--atomic");
if (options.verbosity == 0) if (options.verbosity == 0)
argv_array_push(&args, "--quiet"); argv_array_push(&args, "--quiet");
else if (options.verbosity > 1) else if (options.verbosity > 1)

View File

@ -184,11 +184,12 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat
test_config -C "$d" http.receivepack true && test_config -C "$d" http.receivepack true &&
up="$HTTPD_URL"/smart/atomic-branches.git && up="$HTTPD_URL"/smart/atomic-branches.git &&
# Tell "$up" about two branches for now # Tell "$up" about three branches for now
test_commit atomic1 && test_commit atomic1 &&
test_commit atomic2 && test_commit atomic2 &&
git branch collateral && git branch collateral &&
git push "$up" master collateral && git branch other &&
git push "$up" master collateral other &&
# collateral is a valid push, but should be failed by atomic push # collateral is a valid push, but should be failed by atomic push
git checkout collateral && git checkout collateral &&
@ -226,6 +227,41 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat
grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output grep "^ ! .*rejected.* collateral -> collateral .*atomic push failed" output
' '
test_expect_success 'push --atomic fails on server-side errors' '
# Use previously set up repository
d=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&
test_config -C "$d" http.receivepack true &&
up="$HTTPD_URL"/smart/atomic-branches.git &&
# break ref updates for other on the remote site
mkdir "$d/refs/heads/other.lock" &&
# add the new commit to other
git branch -f other collateral &&
# --atomic should cause entire push to be rejected
test_must_fail git push --atomic "$up" atomic other 2>output &&
# the new branch should not have been created upstream
test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
# upstream should still reflect atomic2, the last thing we pushed
# successfully
git rev-parse atomic2 >expected &&
# ...to other.
git -C "$d" rev-parse refs/heads/other >actual &&
test_cmp expected actual &&
# the new branch should not have been created upstream
test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
# the failed refs should be indicated to the user
grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
# the collateral failure refs should be indicated to the user
grep "^ ! .*rejected.* atomic -> atomic .*atomic transaction failed" output
'
test_expect_success 'push --all can push to empty repo' ' test_expect_success 'push --all can push to empty repo' '
d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git && d=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&
git init --bare "$d" && git init --bare "$d" &&

View File

@ -854,6 +854,10 @@ static void set_common_push_options(struct transport *transport,
die(_("helper %s does not support --signed=if-asked"), name); die(_("helper %s does not support --signed=if-asked"), name);
} }
if (flags & TRANSPORT_PUSH_ATOMIC)
if (set_helper_option(transport, TRANS_OPT_ATOMIC, "true") != 0)
die(_("helper %s does not support --atomic"), name);
if (flags & TRANSPORT_PUSH_OPTIONS) { if (flags & TRANSPORT_PUSH_OPTIONS) {
struct string_list_item *item; struct string_list_item *item;
for_each_string_list_item(item, transport->push_options) for_each_string_list_item(item, transport->push_options)

View File

@ -208,6 +208,9 @@ void transport_check_allowed(const char *type);
/* Filter objects for partial clone and fetch */ /* Filter objects for partial clone and fetch */
#define TRANS_OPT_LIST_OBJECTS_FILTER "filter" #define TRANS_OPT_LIST_OBJECTS_FILTER "filter"
/* Request atomic (all-or-nothing) updates when pushing */
#define TRANS_OPT_ATOMIC "atomic"
/** /**
* Returns 0 if the option was used, non-zero otherwise. Prints a * Returns 0 if the option was used, non-zero otherwise. Prints a
* message to stderr if the option is not used. * message to stderr if the option is not used.