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>
This commit is contained in:
Marius Paliga 2017-10-23 13:44:49 +02:00 committed by Junio C Hamano
parent 4843cdefe3
commit d8052750c5
4 changed files with 135 additions and 7 deletions

View File

@ -2621,6 +2621,35 @@ push.gpgSign::
override a value from a lower-priority config file. An explicit override a value from a lower-priority config file. An explicit
command-line flag always overrides this config option. command-line flag always overrides this config option.
push.pushOption::
When no `--push-option=<option>` argument is given from the
command line, `git push` behaves as if each <value> of
this variable is given as `--push-option=<value>`.
+
This is a multi-valued variable, and an empty value can be used in a
higher priority configuration file (e.g. `.git/config` in a
repository) to clear the values inherited from a lower priority
configuration files (e.g. `$HOME/.gitconfig`).
+
--
Example:
/etc/gitconfig
push.pushoption = a
push.pushoption = b
~/.gitconfig
push.pushoption = c
repo/.git/config
push.pushoption =
push.pushoption = b
This will result in only b (a and c are cleared).
--
push.recurseSubmodules:: push.recurseSubmodules::
Make sure all submodule commits used by the revisions to be pushed Make sure all submodule commits used by the revisions to be pushed
are available on a remote-tracking branch. If the value is 'check' are available on a remote-tracking branch. If the value is 'check'

View File

@ -156,11 +156,17 @@ already exists on the remote side.
Either all refs are updated, or on error, no refs are updated. Either all refs are updated, or on error, no refs are updated.
If the server does not support atomic pushes the push will fail. If the server does not support atomic pushes the push will fail.
-o:: -o <option>::
--push-option:: --push-option=<option>::
Transmit the given string to the server, which passes them to Transmit the given string to the server, which passes them to
the pre-receive as well as the post-receive hook. The given string the pre-receive as well as the post-receive hook. The given string
must not contain a NUL or LF character. must not contain a NUL or LF character.
When multiple `--push-option=<option>` are given, they are
all sent to the other side in the order listed on the
command line.
When no `--push-option=<option>` is given from the command
line, the values of configuration variable `push.pushOption`
are used instead.
--receive-pack=<git-receive-pack>:: --receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>:: --exec=<git-receive-pack>::

View File

@ -32,6 +32,8 @@ static const char **refspec;
static int refspec_nr; static int refspec_nr;
static int refspec_alloc; static int refspec_alloc;
static struct string_list push_options_config = STRING_LIST_INIT_DUP;
static void add_refspec(const char *ref) static void add_refspec(const char *ref)
{ {
refspec_nr++; refspec_nr++;
@ -503,6 +505,15 @@ static int git_push_config(const char *k, const char *v, void *cb)
int val = git_config_bool(k, v) ? int val = git_config_bool(k, v) ?
RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF; RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
recurse_submodules = val; recurse_submodules = val;
} else if (!strcmp(k, "push.pushoption")) {
if (!v)
return config_error_nonbool(k);
else
if (!*v)
string_list_clear(&push_options_config, 0);
else
string_list_append(&push_options_config, v);
return 0;
} }
return git_default_config(k, v, NULL); return git_default_config(k, v, NULL);
@ -515,7 +526,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
int push_cert = -1; int push_cert = -1;
int rc; int rc;
const char *repo = NULL; /* default repository */ const char *repo = NULL; /* default repository */
struct string_list push_options = STRING_LIST_INIT_DUP; struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
struct string_list *push_options;
const struct string_list_item *item; const struct string_list_item *item;
struct option options[] = { struct option options[] = {
@ -551,7 +563,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"), 0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
PARSE_OPT_OPTARG, option_parse_push_signed }, PARSE_OPT_OPTARG, option_parse_push_signed },
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC), OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")), OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
TRANSPORT_FAMILY_IPV4), TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), 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)
packet_trace_identity("push"); packet_trace_identity("push");
git_config(git_push_config, &flags); git_config(git_push_config, &flags);
argc = parse_options(argc, argv, prefix, options, push_usage, 0); argc = parse_options(argc, argv, prefix, options, push_usage, 0);
push_options = (push_options_cmdline.nr
? &push_options_cmdline
: &push_options_config);
set_push_cert_flags(&flags, push_cert); set_push_cert_flags(&flags, push_cert);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) 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)
set_refspecs(argv + 1, argc - 1, repo); set_refspecs(argv + 1, argc - 1, repo);
} }
for_each_string_list_item(item, &push_options) for_each_string_list_item(item, push_options)
if (strchr(item->string, '\n')) if (strchr(item->string, '\n'))
die(_("push options must not have new line characters")); die(_("push options must not have new line characters"));
rc = do_push(repo, flags, &push_options); rc = do_push(repo, flags, push_options);
string_list_clear(&push_options, 0); string_list_clear(&push_options_cmdline, 0);
string_list_clear(&push_options_config, 0);
if (rc == -1) if (rc == -1)
usage_with_options(push_usage, options); usage_with_options(push_usage, options);
else else

View File

@ -140,6 +140,83 @@ test_expect_success 'push options and submodules' '
test_cmp expect parent_upstream/.git/hooks/post-receive.push_options test_cmp expect parent_upstream/.git/hooks/post-receive.push_options
' '
test_expect_success 'default push option' '
mk_repo_pair &&
git -C upstream config receive.advertisePushOptions true &&
(
cd workbench &&
test_commit one &&
git push --mirror up &&
test_commit two &&
git -c push.pushOption=default push up master
) &&
test_refs master master &&
echo "default" >expect &&
test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
test_cmp expect upstream/.git/hooks/post-receive.push_options
'
test_expect_success 'two default push options' '
mk_repo_pair &&
git -C upstream config receive.advertisePushOptions true &&
(
cd workbench &&
test_commit one &&
git push --mirror up &&
test_commit two &&
git -c push.pushOption=default1 -c push.pushOption=default2 push up master
) &&
test_refs master master &&
printf "default1\ndefault2\n" >expect &&
test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
test_cmp expect upstream/.git/hooks/post-receive.push_options
'
test_expect_success 'push option from command line overrides from-config push option' '
mk_repo_pair &&
git -C upstream config receive.advertisePushOptions true &&
(
cd workbench &&
test_commit one &&
git push --mirror up &&
test_commit two &&
git -c push.pushOption=default push --push-option=manual up master
) &&
test_refs master master &&
echo "manual" >expect &&
test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
test_cmp expect upstream/.git/hooks/post-receive.push_options
'
test_expect_success 'empty value of push.pushOption in config clears the list' '
mk_repo_pair &&
git -C upstream config receive.advertisePushOptions true &&
(
cd workbench &&
test_commit one &&
git push --mirror up &&
test_commit two &&
git -c push.pushOption=default1 -c push.pushOption= -c push.pushOption=default2 push up master
) &&
test_refs master master &&
echo "default2" >expect &&
test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
test_cmp expect upstream/.git/hooks/post-receive.push_options
'
test_expect_success 'invalid push option in config' '
mk_repo_pair &&
git -C upstream config receive.advertisePushOptions true &&
(
cd workbench &&
test_commit one &&
git push --mirror up &&
test_commit two &&
test_must_fail git -c push.pushOption push up master
) &&
test_refs master HEAD@{1}
'
. "$TEST_DIRECTORY"/lib-httpd.sh . "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd start_httpd