builtin/push.c: add push.pushOption config

Push options need to be given explicitly, via the command line as "git push --push-option <option>". Add the config option push.pushOption, which is a multi-valued option, containing push options that are sent by default. When push options are set in the lower-priority configulation file (e.g. /etc/gitconfig, or $HOME/.gitconfig), they can be unset later in the more specific repository config by the empty string. Add tests and update documentation as well. Signed-off-by: Marius Paliga <marius.paliga@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Marius Paliga committed Oct 23, 2017 at 13:44 UTC d8052750c5fdd53cb5a664a18ce9d78dbedb22ae
4 files changed +135 -7
Documentation/config.txt
+29
@@ -2621,6 +2621,35 @@ push.gpgSign::
2621 override a value from a lower-priority config file. An explicit
2622 command-line flag always overrides this config option.
2623
2624 +push.pushOption::
2625 + When no `--push-option=<option>` argument is given from the
2626 + command line, `git push` behaves as if each <value> of
2627 + this variable is given as `--push-option=<value>`.
2628 ++
2629 +This is a multi-valued variable, and an empty value can be used in a
2630 +higher priority configuration file (e.g. `.git/config` in a
2631 +repository) to clear the values inherited from a lower priority
2632 +configuration files (e.g. `$HOME/.gitconfig`).
2633 ++
2634 +--
2635 +
2636 +Example:
2637 +
2638 +/etc/gitconfig
2639 + push.pushoption = a
2640 + push.pushoption = b
2641 +
2642 +~/.gitconfig
2643 + push.pushoption = c
2644 +
2645 +repo/.git/config
2646 + push.pushoption =
2647 + push.pushoption = b
2648 +
2649 +This will result in only b (a and c are cleared).
2650 +
2651 +--
2652 +
2653 push.recurseSubmodules::
2654 Make sure all submodule commits used by the revisions to be pushed
2655 are available on a remote-tracking branch. If the value is 'check'
Documentation/git-push.txt
+8 -2
@@ -156,11 +156,17 @@ already exists on the remote side.
156 Either all refs are updated, or on error, no refs are updated.
157 If the server does not support atomic pushes the push will fail.
158
159 --o::
160 ---push-option::
159 +-o <option>::
160 +--push-option=<option>::
161 Transmit the given string to the server, which passes them to
162 the pre-receive as well as the post-receive hook. The given string
163 must not contain a NUL or LF character.
164 + When multiple `--push-option=<option>` are given, they are
165 + all sent to the other side in the order listed on the
166 + command line.
167 + When no `--push-option=<option>` is given from the command
168 + line, the values of configuration variable `push.pushOption`
169 + are used instead.
170
171 --receive-pack=<git-receive-pack>::
172 --exec=<git-receive-pack>::
builtin/push.c
+21 -5
@@ -32,6 +32,8 @@ static const char **refspec;
32 static int refspec_nr;
33 static int refspec_alloc;
34
35 +static struct string_list push_options_config = STRING_LIST_INIT_DUP;
36 +
37 static void add_refspec(const char *ref)
38 {
39 refspec_nr++;
@@ -503,6 +505,15 @@ static int git_push_config(const char *k, const char *v, void *cb)
505 int val = git_config_bool(k, v) ?
506 RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
507 recurse_submodules = val;
508 + } else if (!strcmp(k, "push.pushoption")) {
509 + if (!v)
510 + return config_error_nonbool(k);
511 + else
512 + if (!*v)
513 + string_list_clear(&push_options_config, 0);
514 + else
515 + string_list_append(&push_options_config, v);
516 + return 0;
517 }
518
519 return git_default_config(k, v, NULL);
@@ -515,7 +526,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
526 int push_cert = -1;
527 int rc;
528 const char *repo = NULL; /* default repository */
518 - struct string_list push_options = STRING_LIST_INIT_DUP;
529 + struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
530 + struct string_list *push_options;
531 const struct string_list_item *item;
532
533 struct option options[] = {
@@ -551,7 +563,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
563 0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
564 PARSE_OPT_OPTARG, option_parse_push_signed },
565 OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
554 - OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
566 + OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
567 OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
568 TRANSPORT_FAMILY_IPV4),
569 OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
@@ -562,6 +574,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
574 packet_trace_identity("push");
575 git_config(git_push_config, &flags);
576 argc = parse_options(argc, argv, prefix, options, push_usage, 0);
577 + push_options = (push_options_cmdline.nr
578 + ? &push_options_cmdline
579 + : &push_options_config);
580 set_push_cert_flags(&flags, push_cert);
581
582 if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
@@ -584,12 +599,13 @@ int cmd_push(int argc, const char **argv, const char *prefix)
599 set_refspecs(argv + 1, argc - 1, repo);
600 }
601
587 - for_each_string_list_item(item, &push_options)
602 + for_each_string_list_item(item, push_options)
603 if (strchr(item->string, '\n'))
604 die(_("push options must not have new line characters"));
605
591 - rc = do_push(repo, flags, &push_options);
592 - string_list_clear(&push_options, 0);
606 + rc = do_push(repo, flags, push_options);
607 + string_list_clear(&push_options_cmdline, 0);
608 + string_list_clear(&push_options_config, 0);
609 if (rc == -1)
610 usage_with_options(push_usage, options);
611 else
t/t5545-push-options.sh
+77
@@ -140,6 +140,83 @@ test_expect_success 'push options and submodules' '
140 test_cmp expect parent_upstream/.git/hooks/post-receive.push_options
141 '
142
143 +test_expect_success 'default push option' '
144 + mk_repo_pair &&
145 + git -C upstream config receive.advertisePushOptions true &&
146 + (
147 + cd workbench &&
148 + test_commit one &&
149 + git push --mirror up &&
150 + test_commit two &&
151 + git -c push.pushOption=default push up master
152 + ) &&
153 + test_refs master master &&
154 + echo "default" >expect &&
155 + test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
156 + test_cmp expect upstream/.git/hooks/post-receive.push_options
157 +'
158 +
159 +test_expect_success 'two default push options' '
160 + mk_repo_pair &&
161 + git -C upstream config receive.advertisePushOptions true &&
162 + (
163 + cd workbench &&
164 + test_commit one &&
165 + git push --mirror up &&
166 + test_commit two &&
167 + git -c push.pushOption=default1 -c push.pushOption=default2 push up master
168 + ) &&
169 + test_refs master master &&
170 + printf "default1\ndefault2\n" >expect &&
171 + test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
172 + test_cmp expect upstream/.git/hooks/post-receive.push_options
173 +'
174 +
175 +test_expect_success 'push option from command line overrides from-config push option' '
176 + mk_repo_pair &&
177 + git -C upstream config receive.advertisePushOptions true &&
178 + (
179 + cd workbench &&
180 + test_commit one &&
181 + git push --mirror up &&
182 + test_commit two &&
183 + git -c push.pushOption=default push --push-option=manual up master
184 + ) &&
185 + test_refs master master &&
186 + echo "manual" >expect &&
187 + test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
188 + test_cmp expect upstream/.git/hooks/post-receive.push_options
189 +'
190 +
191 +test_expect_success 'empty value of push.pushOption in config clears the list' '
192 + mk_repo_pair &&
193 + git -C upstream config receive.advertisePushOptions true &&
194 + (
195 + cd workbench &&
196 + test_commit one &&
197 + git push --mirror up &&
198 + test_commit two &&
199 + git -c push.pushOption=default1 -c push.pushOption= -c push.pushOption=default2 push up master
200 + ) &&
201 + test_refs master master &&
202 + echo "default2" >expect &&
203 + test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
204 + test_cmp expect upstream/.git/hooks/post-receive.push_options
205 +'
206 +
207 +test_expect_success 'invalid push option in config' '
208 + mk_repo_pair &&
209 + git -C upstream config receive.advertisePushOptions true &&
210 + (
211 + cd workbench &&
212 + test_commit one &&
213 + git push --mirror up &&
214 + test_commit two &&
215 + test_must_fail git -c push.pushOption push up master
216 + ) &&
217 + test_refs master HEAD@{1}
218 +'
219 +
220 . "$TEST_DIRECTORY"/lib-httpd.sh
221 start_httpd
222