push: accept push options

This implements everything that is required on the client side to make use
of push options from the porcelain push command.

Signed-off-by: Stefan Beller <sbeller@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Stefan Beller 2016-07-14 14:49:47 -07:00 committed by Junio C Hamano
parent c714e45f87
commit f6a4e61fbb
6 changed files with 63 additions and 4 deletions

View File

@ -11,7 +11,7 @@ SYNOPSIS
[verse] [verse]
'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>] 'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose] [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
[-u | --set-upstream] [-u | --set-upstream] [--push-option=<string>]
[--[no-]signed|--sign=(true|false|if-asked)] [--[no-]signed|--sign=(true|false|if-asked)]
[--force-with-lease[=<refname>[:<expect>]]] [--force-with-lease[=<refname>[:<expect>]]]
[--no-verify] [<repository> [<refspec>...]] [--no-verify] [<repository> [<refspec>...]]
@ -156,6 +156,12 @@ 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::
--push-option::
Transmit the given string to the server, which passes them to
the pre-receive as well as the post-receive hook. The given string
must not contain a NUL or LF character.
--receive-pack=<git-receive-pack>:: --receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>:: --exec=<git-receive-pack>::
Path to the 'git-receive-pack' program on the remote Path to the 'git-receive-pack' program on the remote

View File

@ -353,7 +353,8 @@ static int push_with_options(struct transport *transport, int flags)
return 1; return 1;
} }
static int do_push(const char *repo, int flags) static int do_push(const char *repo, int flags,
const struct string_list *push_options)
{ {
int i, errs; int i, errs;
struct remote *remote = pushremote_get(repo); struct remote *remote = pushremote_get(repo);
@ -376,6 +377,9 @@ static int do_push(const char *repo, int flags)
if (remote->mirror) if (remote->mirror)
flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
if (push_options->nr)
flags |= TRANSPORT_PUSH_OPTIONS;
if ((flags & TRANSPORT_PUSH_ALL) && refspec) { if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
if (!strcmp(*refspec, "refs/tags/*")) if (!strcmp(*refspec, "refs/tags/*"))
return error(_("--all and --tags are incompatible")); return error(_("--all and --tags are incompatible"));
@ -406,13 +410,16 @@ static int do_push(const char *repo, int flags)
for (i = 0; i < url_nr; i++) { for (i = 0; i < url_nr; i++) {
struct transport *transport = struct transport *transport =
transport_get(remote, url[i]); transport_get(remote, url[i]);
if (flags & TRANSPORT_PUSH_OPTIONS)
transport->push_options = push_options;
if (push_with_options(transport, flags)) if (push_with_options(transport, flags))
errs++; errs++;
} }
} else { } else {
struct transport *transport = struct transport *transport =
transport_get(remote, NULL); transport_get(remote, NULL);
if (flags & TRANSPORT_PUSH_OPTIONS)
transport->push_options = push_options;
if (push_with_options(transport, flags)) if (push_with_options(transport, flags))
errs++; errs++;
} }
@ -500,6 +507,9 @@ 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 */
static struct string_list push_options = STRING_LIST_INIT_DUP;
static struct string_list_item *item;
struct option options[] = { struct option options[] = {
OPT__VERBOSITY(&verbosity), OPT__VERBOSITY(&verbosity),
OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")), OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
@ -533,6 +543,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_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"),
@ -563,7 +574,11 @@ 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);
} }
rc = do_push(repo, flags); for_each_string_list_item(item, &push_options)
if (strchr(item->string, '\n'))
die(_("push options must not have new line characters"));
rc = do_push(repo, flags, &push_options);
if (rc == -1) if (rc == -1)
usage_with_options(push_usage, options); usage_with_options(push_usage, options);
else else

View File

@ -260,6 +260,7 @@ static int generate_push_cert(struct strbuf *req_buf,
const char *push_cert_nonce) const char *push_cert_nonce)
{ {
const struct ref *ref; const struct ref *ref;
struct string_list_item *item;
char *signing_key = xstrdup(get_signing_key()); char *signing_key = xstrdup(get_signing_key());
const char *cp, *np; const char *cp, *np;
struct strbuf cert = STRBUF_INIT; struct strbuf cert = STRBUF_INIT;
@ -276,6 +277,9 @@ static int generate_push_cert(struct strbuf *req_buf,
} }
if (push_cert_nonce[0]) if (push_cert_nonce[0])
strbuf_addf(&cert, "nonce %s\n", push_cert_nonce); strbuf_addf(&cert, "nonce %s\n", push_cert_nonce);
if (args->push_options)
for_each_string_list_item(item, args->push_options)
strbuf_addf(&cert, "push-option %s\n", item->string);
strbuf_addstr(&cert, "\n"); strbuf_addstr(&cert, "\n");
for (ref = remote_refs; ref; ref = ref->next) { for (ref = remote_refs; ref; ref = ref->next) {
@ -370,6 +374,8 @@ int send_pack(struct send_pack_args *args,
int agent_supported = 0; int agent_supported = 0;
int use_atomic = 0; int use_atomic = 0;
int atomic_supported = 0; int atomic_supported = 0;
int use_push_options = 0;
int push_options_supported = 0;
unsigned cmds_sent = 0; unsigned cmds_sent = 0;
int ret; int ret;
struct async demux; struct async demux;
@ -392,6 +398,8 @@ int send_pack(struct send_pack_args *args,
args->use_thin_pack = 0; args->use_thin_pack = 0;
if (server_supports("atomic")) if (server_supports("atomic"))
atomic_supported = 1; atomic_supported = 1;
if (server_supports("push-options"))
push_options_supported = 1;
if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) { if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
int len; int len;
@ -418,6 +426,11 @@ int send_pack(struct send_pack_args *args,
use_atomic = atomic_supported && args->atomic; use_atomic = atomic_supported && args->atomic;
if (args->push_options && !push_options_supported)
die(_("the receiving end does not support push options"));
use_push_options = push_options_supported && args->push_options;
if (status_report) if (status_report)
strbuf_addstr(&cap_buf, " report-status"); strbuf_addstr(&cap_buf, " report-status");
if (use_sideband) if (use_sideband)
@ -426,6 +439,8 @@ int send_pack(struct send_pack_args *args,
strbuf_addstr(&cap_buf, " quiet"); strbuf_addstr(&cap_buf, " quiet");
if (use_atomic) if (use_atomic)
strbuf_addstr(&cap_buf, " atomic"); strbuf_addstr(&cap_buf, " atomic");
if (use_push_options)
strbuf_addstr(&cap_buf, " push-options");
if (agent_supported) if (agent_supported)
strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized()); strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
@ -512,6 +527,18 @@ int send_pack(struct send_pack_args *args,
strbuf_release(&req_buf); strbuf_release(&req_buf);
strbuf_release(&cap_buf); strbuf_release(&cap_buf);
if (use_push_options) {
struct string_list_item *item;
struct strbuf sb = STRBUF_INIT;
for_each_string_list_item(item, args->push_options)
packet_buf_write(&sb, "%s", item->string);
write_or_die(out, sb.buf, sb.len);
packet_flush(out);
strbuf_release(&sb);
}
if (use_sideband && cmds_sent) { if (use_sideband && cmds_sent) {
memset(&demux, 0, sizeof(demux)); memset(&demux, 0, sizeof(demux));
demux.proc = sideband_demux; demux.proc = sideband_demux;

View File

@ -1,6 +1,8 @@
#ifndef SEND_PACK_H #ifndef SEND_PACK_H
#define SEND_PACK_H #define SEND_PACK_H
#include "string-list.h"
/* Possible values for push_cert field in send_pack_args. */ /* Possible values for push_cert field in send_pack_args. */
#define SEND_PACK_PUSH_CERT_NEVER 0 #define SEND_PACK_PUSH_CERT_NEVER 0
#define SEND_PACK_PUSH_CERT_IF_ASKED 1 #define SEND_PACK_PUSH_CERT_IF_ASKED 1
@ -21,6 +23,7 @@ struct send_pack_args {
push_cert:2, push_cert:2,
stateless_rpc:1, stateless_rpc:1,
atomic:1; atomic:1;
const struct string_list *push_options;
}; };
struct option; struct option;

View File

@ -510,6 +510,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN); args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC); args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
args.push_options = transport->push_options;
args.url = transport->url; args.url = transport->url;
if (flags & TRANSPORT_PUSH_CERT_ALWAYS) if (flags & TRANSPORT_PUSH_CERT_ALWAYS)

View File

@ -48,6 +48,12 @@ struct transport {
*/ */
unsigned cloning : 1; unsigned cloning : 1;
/*
* These strings will be passed to the {pre, post}-receive hook,
* on the remote side, if both sides support the push options capability.
*/
const struct string_list *push_options;
/** /**
* Returns 0 if successful, positive if the option is not * Returns 0 if successful, positive if the option is not
* recognized or is inapplicable, and negative if the option * recognized or is inapplicable, and negative if the option
@ -134,6 +140,7 @@ struct transport {
#define TRANSPORT_PUSH_CERT_ALWAYS 2048 #define TRANSPORT_PUSH_CERT_ALWAYS 2048
#define TRANSPORT_PUSH_CERT_IF_ASKED 4096 #define TRANSPORT_PUSH_CERT_IF_ASKED 4096
#define TRANSPORT_PUSH_ATOMIC 8192 #define TRANSPORT_PUSH_ATOMIC 8192
#define TRANSPORT_PUSH_OPTIONS 16384
#define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3) #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
#define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x) #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)