{"thread":{"id":"52036","subject":"[PATCH] remote-curl: pass on atomic capability to remote side","startedAt":"2019-10-15T01:08:13Z","lastAt":"2019-10-17T07:07:46Z","messageCount":5,"participants":["brian m. carlson","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"384052","messageId":"20191015010759.2259-1-sandals@crustytoothpaste.net","threadId":"52036","inReplyTo":null,"subject":"[PATCH] remote-curl: pass on atomic capability to remote side","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-10-15T01:07:59Z","receivedAt":"2019-10-15T01:08:13Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"When pushing more than one reference with the --atomic option, the\nserver is supposed to perform a single atomic transaction to update the\nreferences, leaving them either all to succeed or all to fail.  This\nworks fine when pushing locally or over SSH, but when pushing over HTTP,\nwe fail to pass the atomic capability to the remote side.  In fact, we\nhave not reported this capability to any remote helpers during the life\nof the feature.\n\nNow normally, things happen to work nevertheless, since we actually\ncheck for most types of failures, such as non-fast-forward updates, on\nthe client side, and just abort the entire attempt.  However, if the\nserver side reports a problem, such as the inability to lock a ref, the\ntransaction isn't atomic, because we haven't passed the appropriate\ncapability over and the remote side has no way of knowing that we wanted\natomic behavior.\n\nFix this by passing the option from the transport code through to remote\nhelpers, and from the HTTP remote helper down to send-pack.  With this\nchange, we can detect if the server side rejects the push and report\nback appropriately.  Note the difference in the messages: the remote\nside reports \"atomic transaction failed\", while our own checking rejects\npushes with the message \"atomic push failed\".\n\nDocument the atomic option in the remote helper documentation, so other\nimplementers can implement it if they like.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\nI discovered this at work when implementing support for atomic pushes.\nOur equivalent of the pre-receive hook is not all-or-nothing, and\npassing the atomic capability through to our backend worked for SSH, but\nnot HTTP.  I discovered with GIT_TRACE_PACKET=1 that we didn't pass the\natomic capability through during HTTP, so I thought I'd send a patch.\n\nAs I mentioned in the commit message, to my knowledge, this\nfunctionality has been broken since the atomic capability was introduced\ncirca 2.4.0.\n\n Documentation/gitremote-helpers.txt |  5 ++++\n remote-curl.c                       | 13 +++++++++-\n t/t5541-http-push-smart.sh          | 40 +++++++++++++++++++++++++++--\n transport-helper.c                  |  4 +++\n transport.h                         |  3 +++\n 5 files changed, 62 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt\nindex a5c3c04371..670d72c174 100644\n--- a/Documentation/gitremote-helpers.txt\n+++ b/Documentation/gitremote-helpers.txt\n@@ -509,6 +509,11 @@ set by Git if the remote helper has the 'option' capability.\n \tIndicate that only the objects wanted need to be fetched, not\n \ttheir dependents.\n \n+'option atomic' {'true'|'false'}::\n+  When pushing, request the remote server to update refs in a single atomic\n+  transaction.  If successful, all refs will be updated, or none will.  If the\n+  remote side does not support this capability, the push will fail.\n+\n SEE ALSO\n --------\n linkgit:git-remote[1]\ndiff --git a/remote-curl.c b/remote-curl.c\nindex 051f26629d..5232ed84b6 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -40,7 +40,8 @@ struct options {\n \t\tpush_cert : 2,\n \t\tdeepen_relative : 1,\n \t\tfrom_promisor : 1,\n-\t\tno_dependents : 1;\n+\t\tno_dependents : 1,\n+\t\tatomic : 1;\n };\n static struct options options;\n static struct string_list cas_options = STRING_LIST_INIT_DUP;\n@@ -148,6 +149,14 @@ static int set_option(const char *name, const char *value)\n \t\telse\n \t\t\treturn -1;\n \t\treturn 0;\n+\t} else if (!strcmp(name, \"atomic\")) {\n+\t\tif (!strcmp(value, \"true\"))\n+\t\t\toptions.atomic = 1;\n+\t\telse if (!strcmp(value, \"false\"))\n+\t\t\toptions.atomic = 0;\n+\t\telse\n+\t\t\treturn -1;\n+\t\treturn 0;\n \t} else if (!strcmp(name, \"push-option\")) {\n \t\tif (*value != '\"')\n \t\t\tstring_list_append(&options.push_options, value);\n@@ -1196,6 +1205,8 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)\n \t\targv_array_push(&args, \"--signed=yes\");\n \telse if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED)\n \t\targv_array_push(&args, \"--signed=if-asked\");\n+\tif (options.atomic)\n+\t\targv_array_push(&args, \"--atomic\");\n \tif (options.verbosity == 0)\n \t\targv_array_push(&args, \"--quiet\");\n \telse if (options.verbosity > 1)\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex 92bac43257..4c970787b0 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -184,11 +184,12 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat\n \ttest_config -C \"$d\" http.receivepack true &&\n \tup=\"$HTTPD_URL\"/smart/atomic-branches.git &&\n \n-\t# Tell \"$up\" about two branches for now\n+\t# Tell \"$up\" about three branches for now\n \ttest_commit atomic1 &&\n \ttest_commit atomic2 &&\n \tgit branch collateral &&\n-\tgit push \"$up\" master collateral &&\n+\tgit branch other &&\n+\tgit push \"$up\" master collateral other &&\n \n \t# collateral is a valid push, but should be failed by atomic push\n \tgit checkout collateral &&\n@@ -226,6 +227,41 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat\n \tgrep \"^ ! .*rejected.* collateral -> collateral .*atomic push failed\" output\n '\n \n+test_expect_success 'push --atomic fails on server-side errors' '\n+\t# Use previously set up repository\n+\td=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&\n+\ttest_config -C \"$d\" http.receivepack true &&\n+\tup=\"$HTTPD_URL\"/smart/atomic-branches.git &&\n+\n+\t# break ref updates for other on the remote site\n+\tmkdir \"$d/refs/heads/other.lock\" &&\n+\n+\t# add the new commit to other\n+\tgit branch -f other collateral &&\n+\n+\t# --atomic should cause entire push to be rejected\n+\ttest_must_fail git push --atomic \"$up\" atomic other 2>output  &&\n+\n+\t# the new branch should not have been created upstream\n+\ttest_must_fail git -C \"$d\" show-ref --verify refs/heads/atomic &&\n+\n+\t# upstream should still reflect atomic2, the last thing we pushed\n+\t# successfully\n+\tgit rev-parse atomic2 >expected &&\n+\t# ...to other.\n+\tgit -C \"$d\" rev-parse refs/heads/other >actual &&\n+\ttest_cmp expected actual &&\n+\n+\t# the new branch should not have been created upstream\n+\ttest_must_fail git -C \"$d\" show-ref --verify refs/heads/atomic &&\n+\n+\t# the failed refs should be indicated to the user\n+\tgrep \"^ ! .*rejected.* other -> other .*atomic transaction failed\" output &&\n+\n+\t# the collateral failure refs should be indicated to the user\n+\tgrep \"^ ! .*rejected.* atomic -> atomic .*atomic transaction failed\" output\n+'\n+\n test_expect_success 'push --all can push to empty repo' '\n \td=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&\n \tgit init --bare \"$d\" &&\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 9e1279b928..a9d690297e 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -854,6 +854,10 @@ static void set_common_push_options(struct transport *transport,\n \t\t\tdie(_(\"helper %s does not support --signed=if-asked\"), name);\n \t}\n \n+\tif (flags & TRANSPORT_PUSH_ATOMIC)\n+\t\tif (set_helper_option(transport, TRANS_OPT_ATOMIC, \"true\") != 0)\n+\t\t\tdie(_(\"helper %s does not support --atomic\"), name);\n+\n \tif (flags & TRANSPORT_PUSH_OPTIONS) {\n \t\tstruct string_list_item *item;\n \t\tfor_each_string_list_item(item, transport->push_options)\ndiff --git a/transport.h b/transport.h\nindex 0b5f7806f6..e0131daab9 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -208,6 +208,9 @@ void transport_check_allowed(const char *type);\n /* Filter objects for partial clone and fetch */\n #define TRANS_OPT_LIST_OBJECTS_FILTER \"filter\"\n \n+/* Request atomic (all-or-nothing) updates when pushing */\n+#define TRANS_OPT_ATOMIC \"atomic\"\n+\n /**\n  * Returns 0 if the option was used, non-zero otherwise. Prints a\n  * message to stderr if the option is not used.\n"},{"id":"384106","messageId":"20191015164028.GA4710@sigill.intra.peff.net","threadId":"52036","inReplyTo":"20191015010759.2259-1-sandals@crustytoothpaste.net","subject":"Re: [PATCH] remote-curl: pass on atomic capability to remote side","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2019-10-15T16:40:29Z","receivedAt":"2019-10-15T16:40:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Oct 15, 2019 at 01:07:59AM +0000, brian m. carlson wrote:\n\n> Fix this by passing the option from the transport code through to remote\n> helpers, and from the HTTP remote helper down to send-pack.  With this\n> change, we can detect if the server side rejects the push and report\n> back appropriately.  Note the difference in the messages: the remote\n> side reports \"atomic transaction failed\", while our own checking rejects\n> pushes with the message \"atomic push failed\".\n\nGood find. The patch looks good to me, except for one minor style nit in\nthe documentation (see below).\n\n> Document the atomic option in the remote helper documentation, so other\n> implementers can implement it if they like.\n\nI wondered what would happen for existing helpers that do not implement\nthe option, but the behavior here:\n\n> +\tif (flags & TRANSPORT_PUSH_ATOMIC)\n> +\t\tif (set_helper_option(transport, TRANS_OPT_ATOMIC, \"true\") != 0)\n> +\t\t\tdie(_(\"helper %s does not support --atomic\"), name);\n> +\n\nlooks like the right thing.\n\n> As I mentioned in the commit message, to my knowledge, this\n> functionality has been broken since the atomic capability was introduced\n> circa 2.4.0.\n\nYeah, I tried this with v2.4.0 and it had the same problem (plus it's\nvery clear from your patch that it's not a regression, but just that\nnobody bothered to implement it in the first place).\n\n> diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt\n> index a5c3c04371..670d72c174 100644\n> --- a/Documentation/gitremote-helpers.txt\n> +++ b/Documentation/gitremote-helpers.txt\n> @@ -509,6 +509,11 @@ set by Git if the remote helper has the 'option' capability.\n>  \tIndicate that only the objects wanted need to be fetched, not\n>  \ttheir dependents.\n>  \n> +'option atomic' {'true'|'false'}::\n> +  When pushing, request the remote server to update refs in a single atomic\n> +  transaction.  If successful, all refs will be updated, or none will.  If the\n> +  remote side does not support this capability, the push will fail.\n> +\n\nThis is implemented with a single space, but the rest of the option\nbodies are indented with a tab. Asciidoc seems to format it identically\neither way, though.\n\n-Peff\n"},{"id":"384218","messageId":"20191016234306.tqaszjc2sfxqg3zr@camp.crustytoothpaste.net","threadId":"52036","inReplyTo":"20191015164028.GA4710@sigill.intra.peff.net","subject":"Re: [PATCH] remote-curl: pass on atomic capability to remote side","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-10-16T23:43:06Z","receivedAt":"2019-10-16T23:43:14Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2019-10-15 at 16:40:29, Jeff King wrote:\n> On Tue, Oct 15, 2019 at 01:07:59AM +0000, brian m. carlson wrote:\n> > diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt\n> > index a5c3c04371..670d72c174 100644\n> > --- a/Documentation/gitremote-helpers.txt\n> > +++ b/Documentation/gitremote-helpers.txt\n> > @@ -509,6 +509,11 @@ set by Git if the remote helper has the 'option' capability.\n> >  \tIndicate that only the objects wanted need to be fetched, not\n> >  \ttheir dependents.\n> >  \n> > +'option atomic' {'true'|'false'}::\n> > +  When pushing, request the remote server to update refs in a single atomic\n> > +  transaction.  If successful, all refs will be updated, or none will.  If the\n> > +  remote side does not support this capability, the push will fail.\n> > +\n> \n> This is implemented with a single space, but the rest of the option\n> bodies are indented with a tab. Asciidoc seems to format it identically\n> either way, though.\n\nYeah, my default editor configuration for AsciiDoc is two spaces.  I\nnoticed that Junio's already picked it up for next, but I'll send a v2\nwith this fixed in case he wants to merge the fixed version to master\ninstead.\n\nIf it's more convenient, I can send a follow-up patch fixing the whitespace.\n-- \nbrian m. carlson: Houston, Texas, US\nOpenPGP: https://keybase.io/bk2204\n"},{"id":"384219","messageId":"20191016234534.1836-1-sandals@crustytoothpaste.net","threadId":"52036","inReplyTo":"20191015010759.2259-1-sandals@crustytoothpaste.net","subject":"[PATCH v2] remote-curl: pass on atomic capability to remote side","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2019-10-16T23:45:34Z","receivedAt":"2019-10-16T23:45:45Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"When pushing more than one reference with the --atomic option, the\nserver is supposed to perform a single atomic transaction to update the\nreferences, leaving them either all to succeed or all to fail.  This\nworks fine when pushing locally or over SSH, but when pushing over HTTP,\nwe fail to pass the atomic capability to the remote side.  In fact, we\nhave not reported this capability to any remote helpers during the life\nof the feature.\n\nNow normally, things happen to work nevertheless, since we actually\ncheck for most types of failures, such as non-fast-forward updates, on\nthe client side, and just abort the entire attempt.  However, if the\nserver side reports a problem, such as the inability to lock a ref, the\ntransaction isn't atomic, because we haven't passed the appropriate\ncapability over and the remote side has no way of knowing that we wanted\natomic behavior.\n\nFix this by passing the option from the transport code through to remote\nhelpers, and from the HTTP remote helper down to send-pack.  With this\nchange, we can detect if the server side rejects the push and report\nback appropriately.  Note the difference in the messages: the remote\nside reports \"atomic transaction failed\", while our own checking rejects\npushes with the message \"atomic push failed\".\n\nDocument the atomic option in the remote helper documentation, so other\nimplementers can implement it if they like.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\nChanges from v1:\n* Use tabs instead of spaces in docs.\n\n Documentation/gitremote-helpers.txt |  5 ++++\n remote-curl.c                       | 13 +++++++++-\n t/t5541-http-push-smart.sh          | 40 +++++++++++++++++++++++++++--\n transport-helper.c                  |  4 +++\n transport.h                         |  3 +++\n 5 files changed, 62 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt\nindex a5c3c04371..f48a031dc3 100644\n--- a/Documentation/gitremote-helpers.txt\n+++ b/Documentation/gitremote-helpers.txt\n@@ -509,6 +509,11 @@ set by Git if the remote helper has the 'option' capability.\n \tIndicate that only the objects wanted need to be fetched, not\n \ttheir dependents.\n \n+'option atomic' {'true'|'false'}::\n+\tWhen pushing, request the remote server to update refs in a single atomic\n+\ttransaction.  If successful, all refs will be updated, or none will.  If the\n+\tremote side does not support this capability, the push will fail.\n+\n SEE ALSO\n --------\n linkgit:git-remote[1]\ndiff --git a/remote-curl.c b/remote-curl.c\nindex 051f26629d..5232ed84b6 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -40,7 +40,8 @@ struct options {\n \t\tpush_cert : 2,\n \t\tdeepen_relative : 1,\n \t\tfrom_promisor : 1,\n-\t\tno_dependents : 1;\n+\t\tno_dependents : 1,\n+\t\tatomic : 1;\n };\n static struct options options;\n static struct string_list cas_options = STRING_LIST_INIT_DUP;\n@@ -148,6 +149,14 @@ static int set_option(const char *name, const char *value)\n \t\telse\n \t\t\treturn -1;\n \t\treturn 0;\n+\t} else if (!strcmp(name, \"atomic\")) {\n+\t\tif (!strcmp(value, \"true\"))\n+\t\t\toptions.atomic = 1;\n+\t\telse if (!strcmp(value, \"false\"))\n+\t\t\toptions.atomic = 0;\n+\t\telse\n+\t\t\treturn -1;\n+\t\treturn 0;\n \t} else if (!strcmp(name, \"push-option\")) {\n \t\tif (*value != '\"')\n \t\t\tstring_list_append(&options.push_options, value);\n@@ -1196,6 +1205,8 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)\n \t\targv_array_push(&args, \"--signed=yes\");\n \telse if (options.push_cert == SEND_PACK_PUSH_CERT_IF_ASKED)\n \t\targv_array_push(&args, \"--signed=if-asked\");\n+\tif (options.atomic)\n+\t\targv_array_push(&args, \"--atomic\");\n \tif (options.verbosity == 0)\n \t\targv_array_push(&args, \"--quiet\");\n \telse if (options.verbosity > 1)\ndiff --git a/t/t5541-http-push-smart.sh b/t/t5541-http-push-smart.sh\nindex 92bac43257..4c970787b0 100755\n--- a/t/t5541-http-push-smart.sh\n+++ b/t/t5541-http-push-smart.sh\n@@ -184,11 +184,12 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat\n \ttest_config -C \"$d\" http.receivepack true &&\n \tup=\"$HTTPD_URL\"/smart/atomic-branches.git &&\n \n-\t# Tell \"$up\" about two branches for now\n+\t# Tell \"$up\" about three branches for now\n \ttest_commit atomic1 &&\n \ttest_commit atomic2 &&\n \tgit branch collateral &&\n-\tgit push \"$up\" master collateral &&\n+\tgit branch other &&\n+\tgit push \"$up\" master collateral other &&\n \n \t# collateral is a valid push, but should be failed by atomic push\n \tgit checkout collateral &&\n@@ -226,6 +227,41 @@ test_expect_success 'push --atomic also prevents branch creation, reports collat\n \tgrep \"^ ! .*rejected.* collateral -> collateral .*atomic push failed\" output\n '\n \n+test_expect_success 'push --atomic fails on server-side errors' '\n+\t# Use previously set up repository\n+\td=$HTTPD_DOCUMENT_ROOT_PATH/atomic-branches.git &&\n+\ttest_config -C \"$d\" http.receivepack true &&\n+\tup=\"$HTTPD_URL\"/smart/atomic-branches.git &&\n+\n+\t# break ref updates for other on the remote site\n+\tmkdir \"$d/refs/heads/other.lock\" &&\n+\n+\t# add the new commit to other\n+\tgit branch -f other collateral &&\n+\n+\t# --atomic should cause entire push to be rejected\n+\ttest_must_fail git push --atomic \"$up\" atomic other 2>output  &&\n+\n+\t# the new branch should not have been created upstream\n+\ttest_must_fail git -C \"$d\" show-ref --verify refs/heads/atomic &&\n+\n+\t# upstream should still reflect atomic2, the last thing we pushed\n+\t# successfully\n+\tgit rev-parse atomic2 >expected &&\n+\t# ...to other.\n+\tgit -C \"$d\" rev-parse refs/heads/other >actual &&\n+\ttest_cmp expected actual &&\n+\n+\t# the new branch should not have been created upstream\n+\ttest_must_fail git -C \"$d\" show-ref --verify refs/heads/atomic &&\n+\n+\t# the failed refs should be indicated to the user\n+\tgrep \"^ ! .*rejected.* other -> other .*atomic transaction failed\" output &&\n+\n+\t# the collateral failure refs should be indicated to the user\n+\tgrep \"^ ! .*rejected.* atomic -> atomic .*atomic transaction failed\" output\n+'\n+\n test_expect_success 'push --all can push to empty repo' '\n \td=$HTTPD_DOCUMENT_ROOT_PATH/empty-all.git &&\n \tgit init --bare \"$d\" &&\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 9e1279b928..a9d690297e 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -854,6 +854,10 @@ static void set_common_push_options(struct transport *transport,\n \t\t\tdie(_(\"helper %s does not support --signed=if-asked\"), name);\n \t}\n \n+\tif (flags & TRANSPORT_PUSH_ATOMIC)\n+\t\tif (set_helper_option(transport, TRANS_OPT_ATOMIC, \"true\") != 0)\n+\t\t\tdie(_(\"helper %s does not support --atomic\"), name);\n+\n \tif (flags & TRANSPORT_PUSH_OPTIONS) {\n \t\tstruct string_list_item *item;\n \t\tfor_each_string_list_item(item, transport->push_options)\ndiff --git a/transport.h b/transport.h\nindex 0b5f7806f6..e0131daab9 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -208,6 +208,9 @@ void transport_check_allowed(const char *type);\n /* Filter objects for partial clone and fetch */\n #define TRANS_OPT_LIST_OBJECTS_FILTER \"filter\"\n \n+/* Request atomic (all-or-nothing) updates when pushing */\n+#define TRANS_OPT_ATOMIC \"atomic\"\n+\n /**\n  * Returns 0 if the option was used, non-zero otherwise. Prints a\n  * message to stderr if the option is not used.\n"},{"id":"384244","messageId":"xmqq4l0797ho.fsf@gitster-ct.c.googlers.com","threadId":"52036","inReplyTo":"20191016234306.tqaszjc2sfxqg3zr@camp.crustytoothpaste.net","subject":"Re: [PATCH] remote-curl: pass on atomic capability to remote side","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-17T07:05:55Z","receivedAt":"2019-10-17T07:07:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> Yeah, my default editor configuration for AsciiDoc is two spaces.  I\n> noticed that Junio's already picked it up for next, but I'll send a v2\n> with this fixed in case he wants to merge the fixed version to master\n> instead.\n>\n> If it's more convenient, I can send a follow-up patch fixing the whitespace.\n\nEither is fine.  At this point in the cycle where we are very close\nto -rc0, I do not mind too much about having a few reverts of 'next'\nto give a clean slate to an obvious and trivial fix.\n\nThanks.\n"}]}