{"thread":{"id":"52836","subject":"[PATCH v2] push: introduce --push-option-if-able","startedAt":"2020-02-18T20:11:11Z","lastAt":"2020-02-18T20:59:08Z","messageCount":2,"participants":["Drew DeVault","Jeff King"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"392010","messageId":"20200218200913.128519-1-sir@cmpwn.com","threadId":"52836","inReplyTo":null,"subject":"[PATCH v2] push: introduce --push-option-if-able","fromName":"Drew DeVault","fromEmail":"sir@cmpwn.com","sentAt":"2020-02-18T20:09:14Z","receivedAt":"2020-02-18T20:11:11Z","isPatch":true,"sender":{"key":"sir@cmpwn.com","avatar":"https://avatars.githubusercontent.com/u/1310872?v=4"},"body":"This introduces a --push-option-if-able, and along with it updates\nsend-pack, transport, push, etc to track the list of push options\nspecified via this flag. These options will be used if the remote\nsupports push options, but will not cause the push operation to\nterminate if the remote does not support push options.\n\nThis is desirable in the following scenario: you frequently use two git\nhosts, A and B, of which only B supports push options. If you wish to\nset a push option globally (via git config push.pushOptions), any\nattempts to push to host A will fail, requiring you to explicitly\noverride it at the command line. This renders the push.pushOption\nconfig value basically useless for a lot of users.\n\nSigned-off-by: Drew DeVault <sir@cmpwn.com>\n---\nPrevious version of this patch made --push-option non-fatal in the face\nof a server which does not support push options. Following feedback that\nthis might be risky when the push options are relied upon to prevent\nsome undesirable default behavior from occuring, I've implemented the\nsuggested --push-option-if-able as an alternative. Thanks to Jeff King\nfor the review.\n\nIt was also suggested to add remote.*.pushOption{,IfAble}, but seeing as\nremote.*.pushOption is not presently supported I think this is best\nsaved for a later patch (it's definitely a good idea, though).\n\n Documentation/config/push.txt      |  6 +++++\n Documentation/git-push.txt         | 14 +++++++++++-\n Documentation/git-receive-pack.txt | 10 +++++++++\n Documentation/githooks.txt         |  3 ++-\n builtin/push.c                     | 35 +++++++++++++++++++++++++-----\n send-pack.c                        |  9 ++++++--\n send-pack.h                        |  2 +-\n submodule.c                        | 11 +++++++++-\n submodule.h                        |  1 +\n transport-helper.c                 |  3 +++\n transport.c                        |  2 ++\n transport.h                        |  5 +++++\n 12 files changed, 90 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/config/push.txt b/Documentation/config/push.txt\nindex 54871f8213..6ad0c6808a 100644\n--- a/Documentation/config/push.txt\n+++ b/Documentation/config/push.txt\n@@ -99,6 +99,12 @@ This will result in only b (a and c are cleared).\n \n --\n \n+push.pushOptionIfAble::\n+\tWhen no `--push-option-if-able=<option>` argument is given\n+\tfrom the command line, `git push` behaves as if each <value>\n+\tof this variable is given as `--push-option-if-able=<value>`.\n+\tIn other respects, it is equivalent to push.pushOption.\n+\n push.recurseSubmodules::\n \tMake sure all submodule commits used by the revisions to be pushed\n \tare available on a remote-tracking branch. If the value is 'check'\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 3b8053447e..5f8504b05e 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -11,7 +11,8 @@ SYNOPSIS\n [verse]\n 'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]\n \t   [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]\n-\t   [-u | --set-upstream] [-o <string> | --push-option=<string>]\n+\t   [-u | --set-upstream][-o <string> | --push-option=<string>]\n+\t   [--push-option-if-able=<string>]\n \t   [--[no-]signed|--signed=(true|false|if-asked)]\n \t   [--force-with-lease[=<refname>[:<expect>]]]\n \t   [--no-verify] [<repository> [<refspec>...]]\n@@ -224,6 +225,17 @@ already exists on the remote side.\n \tline, the values of configuration variable `push.pushOption`\n \tare used instead.\n \n+--push-option-if-able=<option>::\n+\tIdentical to --push-option, but does not terminate the push if the\n+\tremote does not support push options. This is useful, for example,\n+\tif you wish to globally enable a push option for use on a specific\n+\tgit host, but also occasionally push to hosts which do not have\n+\tpush options enabled. If you were to use --push-option instead,\n+\tpushing to the latter would cause the push to be aborted.\n+\tWhen no `--push-option-if-able=<option>` is given from the command\n+\tline, the values of configuration variable `push.pushOptionIfAble`\n+\tare used instead.\n+\n --receive-pack=<git-receive-pack>::\n --exec=<git-receive-pack>::\n \tPath to the 'git-receive-pack' program on the remote\ndiff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt\nindex 25702ed730..992ef5784f 100644\n--- a/Documentation/git-receive-pack.txt\n+++ b/Documentation/git-receive-pack.txt\n@@ -109,6 +109,16 @@ the following environment variables:\n This hook is called before any refname is updated and before any\n fast-forward checks are performed.\n \n+The number of push options given on the command line of\n+`git push --push-option=...` can be read from the environment\n+variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are\n+found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...\n+If it is negotiated to not use the push options phase, the\n+environment variables will not be set. If the client selects\n+to use push options, but doesn't transmit any, the count variable\n+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. In order for to receive push\n+options, `receive.advertisePushOptions` must be enabled on the server.\n+\n If the pre-receive hook exits with a non-zero exit status no updates\n will be performed, and the update, post-receive and post-update\n hooks will not be invoked either.  This can be useful to quickly\ndiff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\nindex 3dccab5375..48103116fd 100644\n--- a/Documentation/githooks.txt\n+++ b/Documentation/githooks.txt\n@@ -283,7 +283,8 @@ found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...\n If it is negotiated to not use the push options phase, the\n environment variables will not be set. If the client selects\n to use push options, but doesn't transmit any, the count variable\n-will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.\n+will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. In order for to receive push\n+options, `receive.advertisePushOptions` must be enabled on the server.\n \n See the section on \"Quarantine Environment\" in\n linkgit:git-receive-pack[1] for some caveats.\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 6dbf0f0bb7..ddffb3d2f0 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -60,6 +60,7 @@ static struct push_cas_option cas;\n static struct refspec rs = REFSPEC_INIT_PUSH;\n \n static struct string_list push_options_config = STRING_LIST_INIT_DUP;\n+static struct string_list push_options_if_able_config = STRING_LIST_INIT_DUP;\n \n static const char *map_refspec(const char *ref,\n \t\t\t       struct remote *remote, struct ref *local_refs)\n@@ -389,6 +390,7 @@ static int push_with_options(struct transport *transport, struct refspec *rs,\n \n static int do_push(const char *repo, int flags,\n \t\t   const struct string_list *push_options,\n+\t\t   const struct string_list *push_options_if_able,\n \t\t   struct remote *remote)\n {\n \tint i, errs;\n@@ -396,7 +398,7 @@ static int do_push(const char *repo, int flags,\n \tint url_nr;\n \tstruct refspec *push_refspec = &rs;\n \n-\tif (push_options->nr)\n+\tif (push_options->nr || push_options_if_able->nr)\n \t\tflags |= TRANSPORT_PUSH_OPTIONS;\n \n \tif (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) {\n@@ -411,16 +413,20 @@ static int do_push(const char *repo, int flags,\n \t\tfor (i = 0; i < url_nr; i++) {\n \t\t\tstruct transport *transport =\n \t\t\t\ttransport_get(remote, url[i]);\n-\t\t\tif (flags & TRANSPORT_PUSH_OPTIONS)\n+\t\t\tif (flags & TRANSPORT_PUSH_OPTIONS) {\n \t\t\t\ttransport->push_options = push_options;\n+\t\t\t\ttransport->push_options_if_able = push_options_if_able;\n+\t\t\t}\n \t\t\tif (push_with_options(transport, push_refspec, flags))\n \t\t\t\terrs++;\n \t\t}\n \t} else {\n \t\tstruct transport *transport =\n \t\t\ttransport_get(remote, NULL);\n-\t\tif (flags & TRANSPORT_PUSH_OPTIONS)\n+\t\tif (flags & TRANSPORT_PUSH_OPTIONS) {\n \t\t\ttransport->push_options = push_options;\n+\t\t\ttransport->push_options_if_able = push_options_if_able;\n+\t\t}\n \t\tif (push_with_options(transport, push_refspec, flags))\n \t\t\terrs++;\n \t}\n@@ -510,6 +516,15 @@ static int git_push_config(const char *k, const char *v, void *cb)\n \t\t\telse\n \t\t\t\tstring_list_append(&push_options_config, v);\n \t\treturn 0;\n+\t} else if (!strcmp(k, \"push.pushoptionifable\")) {\n+\t\tif (!v)\n+\t\t\treturn config_error_nonbool(k);\n+\t\telse\n+\t\t\tif (!*v)\n+\t\t\t\tstring_list_clear(&push_options_if_able_config, 0);\n+\t\t\telse\n+\t\t\t\tstring_list_append(&push_options_if_able_config, v);\n+\t\treturn 0;\n \t} else if (!strcmp(k, \"color.push\")) {\n \t\tpush_use_color = git_config_colorbool(k, v);\n \t\treturn 0;\n@@ -533,7 +548,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tint rc;\n \tconst char *repo = NULL;\t/* default repository */\n \tstruct string_list push_options_cmdline = STRING_LIST_INIT_DUP;\n-\tstruct string_list *push_options;\n+\tstruct string_list push_options_if_able_cmdline = STRING_LIST_INIT_DUP;\n+\tstruct string_list *push_options, *push_options_if_able;\n \tconst struct string_list_item *item;\n \tstruct remote *remote;\n \n@@ -571,6 +587,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\t  PARSE_OPT_OPTARG, option_parse_push_signed },\n \t\tOPT_BIT(0, \"atomic\", &flags, N_(\"request atomic transaction on remote side\"), TRANSPORT_PUSH_ATOMIC),\n \t\tOPT_STRING_LIST('o', \"push-option\", &push_options_cmdline, N_(\"server-specific\"), N_(\"option to transmit\")),\n+\t\tOPT_STRING_LIST(0, \"push-option-if-able\", &push_options_if_able_cmdline, N_(\"server-specific\"), N_(\"option to transmit, if supported by remote\")),\n \t\tOPT_SET_INT('4', \"ipv4\", &family, N_(\"use IPv4 addresses only\"),\n \t\t\t\tTRANSPORT_FAMILY_IPV4),\n \t\tOPT_SET_INT('6', \"ipv6\", &family, N_(\"use IPv6 addresses only\"),\n@@ -584,6 +601,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tpush_options = (push_options_cmdline.nr\n \t\t? &push_options_cmdline\n \t\t: &push_options_config);\n+\tpush_options_if_able = (push_options_if_able_cmdline.nr\n+\t\t? &push_options_if_able_cmdline\n+\t\t: &push_options_if_able_config);\n \tset_push_cert_flags(&flags, push_cert);\n \n \tif (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))\n@@ -641,10 +661,15 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \tfor_each_string_list_item(item, push_options)\n \t\tif (strchr(item->string, '\\n'))\n \t\t\tdie(_(\"push options must not have new line characters\"));\n+\tfor_each_string_list_item(item, push_options_if_able)\n+\t\tif (strchr(item->string, '\\n'))\n+\t\t\tdie(_(\"push options must not have new line characters\"));\n \n-\trc = do_push(repo, flags, push_options, remote);\n+\trc = do_push(repo, flags, push_options, push_options_if_able, remote);\n \tstring_list_clear(&push_options_cmdline, 0);\n \tstring_list_clear(&push_options_config, 0);\n+\tstring_list_clear(&push_options_if_able_config, 0);\n+\tstring_list_clear(&push_options_if_able_cmdline, 0);\n \tif (rc == -1)\n \t\tusage_with_options(push_usage, options);\n \telse\ndiff --git a/send-pack.c b/send-pack.c\nindex 0407841ae8..60705405f4 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -438,10 +438,13 @@ int send_pack(struct send_pack_args *args,\n \n \tuse_atomic = atomic_supported && args->atomic;\n \n-\tif (args->push_options && !push_options_supported)\n+\tif (args->push_options && args->push_options->nr && !push_options_supported)\n \t\tdie(_(\"the receiving end does not support push options\"));\n+\tif (args->push_options_if_able && args->push_options_if_able->nr && !push_options_supported)\n+\t\twarning(_(\"the receiving end does not support push options\"));\n \n-\tuse_push_options = push_options_supported && args->push_options;\n+\tuse_push_options = push_options_supported\n+\t\t&& (args->push_options || args->push_options_if_able);\n \n \tif (status_report)\n \t\tstrbuf_addstr(&cap_buf, \" report-status\");\n@@ -536,6 +539,8 @@ int send_pack(struct send_pack_args *args,\n \t\tpacket_buf_flush(&req_buf);\n \t\tfor_each_string_list_item(item, args->push_options)\n \t\t\tpacket_buf_write(&req_buf, \"%s\", item->string);\n+\t\tfor_each_string_list_item(item, args->push_options_if_able)\n+\t\t\tpacket_buf_write(&req_buf, \"%s\", item->string);\n \t}\n \n \tif (args->stateless_rpc) {\ndiff --git a/send-pack.h b/send-pack.h\nindex e148fcd960..644968ba06 100644\n--- a/send-pack.h\n+++ b/send-pack.h\n@@ -27,7 +27,7 @@ struct send_pack_args {\n \t\tpush_cert:2,\n \t\tstateless_rpc:1,\n \t\tatomic:1;\n-\tconst struct string_list *push_options;\n+\tconst struct string_list *push_options, *push_options_if_able;\n };\n \n struct option;\ndiff --git a/submodule.c b/submodule.c\nindex 31f391d7d2..2386a2ad56 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -1050,6 +1050,7 @@ static int push_submodule(const char *path,\n \t\t\t  const struct remote *remote,\n \t\t\t  const struct refspec *rs,\n \t\t\t  const struct string_list *push_options,\n+\t\t\t  const struct string_list *push_options_if_able,\n \t\t\t  int dry_run)\n {\n \tif (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {\n@@ -1064,6 +1065,12 @@ static int push_submodule(const char *path,\n \t\t\t\targv_array_pushf(&cp.args, \"--push-option=%s\",\n \t\t\t\t\t\t item->string);\n \t\t}\n+\t\tif (push_options_if_able && push_options_if_able->nr) {\n+\t\t\tconst struct string_list_item *item;\n+\t\t\tfor_each_string_list_item(item, push_options_if_able)\n+\t\t\t\targv_array_pushf(&cp.args, \"--push-option-if-able=%s\",\n+\t\t\t\t\t\t item->string);\n+\t\t}\n \n \t\tif (remote->origin != REMOTE_UNCONFIGURED) {\n \t\t\tint i;\n@@ -1123,6 +1130,7 @@ int push_unpushed_submodules(struct repository *r,\n \t\t\t     const struct remote *remote,\n \t\t\t     const struct refspec *rs,\n \t\t\t     const struct string_list *push_options,\n+\t\t\t     const struct string_list *push_options_if_able,\n \t\t\t     int dry_run)\n {\n \tint i, ret = 1;\n@@ -1157,7 +1165,8 @@ int push_unpushed_submodules(struct repository *r,\n \t\tconst char *path = needs_pushing.items[i].string;\n \t\tfprintf(stderr, _(\"Pushing submodule '%s'\\n\"), path);\n \t\tif (!push_submodule(path, remote, rs,\n-\t\t\t\t    push_options, dry_run)) {\n+\t\t\t\t    push_options, push_options_if_able,\n+\t\t\t\t    dry_run)) {\n \t\t\tfprintf(stderr, _(\"Unable to push submodule '%s'\\n\"), path);\n \t\t\tret = 0;\n \t\t}\ndiff --git a/submodule.h b/submodule.h\nindex c81ec1a9b6..38047c7ef1 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -116,6 +116,7 @@ int push_unpushed_submodules(struct repository *r,\n \t\t\t     const struct remote *remote,\n \t\t\t     const struct refspec *rs,\n \t\t\t     const struct string_list *push_options,\n+\t\t\t     const struct string_list *push_options_if_able,\n \t\t\t     int dry_run);\n /*\n  * Given a submodule path (as in the index), return the repository\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 20a7185ec4..e0cf40ffa2 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -865,6 +865,9 @@ static void set_common_push_options(struct transport *transport,\n \t\tfor_each_string_list_item(item, transport->push_options)\n \t\t\tif (set_helper_option(transport, \"push-option\", item->string) != 0)\n \t\t\t\tdie(_(\"helper %s does not support 'push-option'\"), name);\n+\t\tfor_each_string_list_item(item, transport->push_options_if_able)\n+\t\t\tif (set_helper_option(transport, \"push-option-if-able\", item->string) != 0)\n+\t\t\t\tdie(_(\"helper %s does not support 'push-option-if-able'\"), name);\n \t}\n }\n \ndiff --git a/transport.c b/transport.c\nindex 1fdc7dac1a..47861a4e65 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -691,6 +691,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re\n \targs.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);\n \targs.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);\n \targs.push_options = transport->push_options;\n+\targs.push_options_if_able = transport->push_options_if_able;\n \targs.url = transport->url;\n \n \tif (flags & TRANSPORT_PUSH_CERT_ALWAYS)\n@@ -1195,6 +1196,7 @@ int transport_push(struct repository *r,\n \t\t\t\t\t\t      transport->remote,\n \t\t\t\t\t\t      rs,\n \t\t\t\t\t\t      transport->push_options,\n+\t\t\t\t\t\t      transport->push_options_if_able,\n \t\t\t\t\t\t      pretend)) {\n \t\t\t\toid_array_clear(&commits);\n \t\t\t\ttrace2_region_leave(\"transport_push\", \"push_submodules\", r);\ndiff --git a/transport.h b/transport.h\nindex e0131daab9..1ceeaf43fe 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -91,6 +91,11 @@ struct transport {\n \t * on the remote side, if both sides support the push options capability.\n \t */\n \tconst struct string_list *push_options;\n+\t/*\n+\t * Like push_options, but if the remote does not support push options,\n+\t * the push continues regardless.\n+\t */\n+\tconst struct string_list *push_options_if_able;\n \n \t/*\n \t * These strings will be passed to the remote side on each command\n-- \n2.25.0\n\n"},{"id":"392018","messageId":"20200218205906.GB22630@coredump.intra.peff.net","threadId":"52836","inReplyTo":"20200218200913.128519-1-sir@cmpwn.com","subject":"Re: [PATCH v2] push: introduce --push-option-if-able","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-02-18T20:59:06Z","receivedAt":"2020-02-18T20:59:08Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Feb 18, 2020 at 03:09:14PM -0500, Drew DeVault wrote:\n\n> This introduces a --push-option-if-able, and along with it updates\n> send-pack, transport, push, etc to track the list of push options\n> specified via this flag. These options will be used if the remote\n> supports push options, but will not cause the push operation to\n> terminate if the remote does not support push options.\n> \n> This is desirable in the following scenario: you frequently use two git\n> hosts, A and B, of which only B supports push options. If you wish to\n> set a push option globally (via git config push.pushOptions), any\n> attempts to push to host A will fail, requiring you to explicitly\n> override it at the command line. This renders the push.pushOption\n> config value basically useless for a lot of users.\n\nUnsurprisingly, this approach makes sense to me. :)\n\nThe implementation looks like the right direction, but I noticed a few\nthings I think are worth addressing:\n\n>  Documentation/config/push.txt      |  6 +++++\n>  Documentation/git-push.txt         | 14 +++++++++++-\n>  Documentation/git-receive-pack.txt | 10 +++++++++\n>  Documentation/githooks.txt         |  3 ++-\n>  builtin/push.c                     | 35 +++++++++++++++++++++++++-----\n>  send-pack.c                        |  9 ++++++--\n>  send-pack.h                        |  2 +-\n>  submodule.c                        | 11 +++++++++-\n>  submodule.h                        |  1 +\n>  transport-helper.c                 |  3 +++\n>  transport.c                        |  2 ++\n>  transport.h                        |  5 +++++\n\nWe'd probably want some test coverage of the new command-line and config\noptions. Looks like t/t5545-push-options.sh would be a good place to add\nit, and you should be able to emulate some of the existing tests there.\n\n> @@ -224,6 +225,17 @@ already exists on the remote side.\n>  \tline, the values of configuration variable `push.pushOption`\n>  \tare used instead.\n>  \n> +--push-option-if-able=<option>::\n> +\tIdentical to --push-option, but does not terminate the push if the\n> +\tremote does not support push options. This is useful, for example,\n> +\tif you wish to globally enable a push option for use on a specific\n> +\tgit host, but also occasionally push to hosts which do not have\n> +\tpush options enabled. If you were to use --push-option instead,\n> +\tpushing to the latter would cause the push to be aborted.\n> +\tWhen no `--push-option-if-able=<option>` is given from the command\n> +\tline, the values of configuration variable `push.pushOptionIfAble`\n> +\tare used instead.\n\nThe discussion of the rationale looks good here.\n\nHow do the two command-line lists (and their config counterparts)\ninteract? E.g., if I do:\n\n  git push --push-option-if-able=foo\n\nwe know that overrides push.pushOptionIfAble config. Does it / should it\noverride push.pushOption? Likewise, a blank config option resets the\nlist. Would:\n\n  [push]\n  pushOptionIfAble = foo\n  pushOption =\n\nsend \"foo\" or not?\n\nIMHO we should consider it from the user's point of view as a single\nlist of push options, some of which are annotated with \"if able\" and\nsome not (and the answer would be \"yes\" to both of my questions).\n\nAnd then the implementation could reflect that by using a single list.\nI think that simplifies things by not having to pass around both lists.\nYou should be able to use string_list's util field to store the flag\n(either allocate and point to a struct for each option, or just cast 0/1\nto a void pointer).\n\n> diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt\n> index 25702ed730..992ef5784f 100644\n> --- a/Documentation/git-receive-pack.txt\n> +++ b/Documentation/git-receive-pack.txt\n> @@ -109,6 +109,16 @@ the following environment variables:\n>  This hook is called before any refname is updated and before any\n>  fast-forward checks are performed.\n>  \n> +The number of push options given on the command line of\n> +`git push --push-option=...` can be read from the environment\n> +variable `GIT_PUSH_OPTION_COUNT`, and the options themselves are\n> +found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...\n> +If it is negotiated to not use the push options phase, the\n> +environment variables will not be set. If the client selects\n> +to use push options, but doesn't transmit any, the count variable\n> +will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. In order for to receive push\n> +options, `receive.advertisePushOptions` must be enabled on the server.\n\nI think this documentation change made more sense as a separate patch\n(since the server side is not affected either way by the \"if able\"\nfeature).\n\nIt still has the \"for to\" typo (or am I misreading it?).\n\n> diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt\n> index 3dccab5375..48103116fd 100644\n> --- a/Documentation/githooks.txt\n> +++ b/Documentation/githooks.txt\n> @@ -283,7 +283,8 @@ found in `GIT_PUSH_OPTION_0`, `GIT_PUSH_OPTION_1`,...\n>  If it is negotiated to not use the push options phase, the\n>  environment variables will not be set. If the client selects\n>  to use push options, but doesn't transmit any, the count variable\n> -will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.\n> +will be set to zero, `GIT_PUSH_OPTION_COUNT=0`. In order for to receive push\n> +options, `receive.advertisePushOptions` must be enabled on the server.\n\nLikewise here (including the typo ;) ).\n\n-Peff\n"}]}