Skip to content

Commit b21089d

Browse files
committed
Merge branch 'db/push-sign-if-asked'
The client side codepaths in "git push" have been cleaned up and the user can request to perform an optional "signed push", i.e. sign only when the other end accepts signed push. * db/push-sign-if-asked: push: add a config option push.gpgSign for default signed pushes push: support signing pushes iff the server supports it builtin/send-pack.c: use parse_options API config.c: rename git_config_maybe_bool_text and export it as git_parse_maybe_bool transport: remove git_transport_options.push_cert gitremote-helpers.txt: document pushcert option Documentation/git-send-pack.txt: document --signed Documentation/git-send-pack.txt: wrap long synopsis line Documentation/git-push.txt: document when --signed may fail
2 parents 5b6211a + 68c757f commit b21089d

File tree

14 files changed

+253
-152
lines changed

14 files changed

+253
-152
lines changed

Documentation/config.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,6 +2213,14 @@ push.followTags::
22132213
may override this configuration at time of push by specifying
22142214
'--no-follow-tags'.
22152215

2216+
push.gpgSign::
2217+
May be set to a boolean value, or the string 'if-asked'. A true
2218+
value causes all pushes to be GPG signed, as if '--signed' is
2219+
passed to linkgit:git-push[1]. The string 'if-asked' causes
2220+
pushes to be signed if the server supports it, as if
2221+
'--signed=if-asked' is passed to 'git push'. A false value may
2222+
override a value from a lower-priority config file. An explicit
2223+
command-line flag always overrides this config option.
22162224

22172225
rebase.stat::
22182226
Whether to show a diffstat of what changed upstream since the last

Documentation/git-push.txt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ SYNOPSIS
1111
[verse]
1212
'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
1313
[--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
14-
[-u | --set-upstream] [--signed]
14+
[-u | --set-upstream]
15+
[--[no-]signed|--sign=(true|false|if-asked)]
1516
[--force-with-lease[=<refname>[:<expect>]]]
1617
[--no-verify] [<repository> [<refspec>...]]
1718

@@ -132,12 +133,16 @@ already exists on the remote side.
132133
with configuration variable 'push.followTags'. For more
133134
information, see 'push.followTags' in linkgit:git-config[1].
134135

135-
136-
--signed::
136+
--[no-]signed::
137+
--sign=(true|false|if-asked)::
137138
GPG-sign the push request to update refs on the receiving
138139
side, to allow it to be checked by the hooks and/or be
139-
logged. See linkgit:git-receive-pack[1] for the details
140-
on the receiving end.
140+
logged. If `false` or `--no-signed`, no signing will be
141+
attempted. If `true` or `--signed`, the push will fail if the
142+
server does not support signed pushes. If set to `if-asked`,
143+
sign if and only if the server supports signed pushes. The push
144+
will also fail if the actual call to `gpg --sign` fails. See
145+
linkgit:git-receive-pack[1] for the details on the receiving end.
141146

142147
--[no-]atomic::
143148
Use an atomic transaction on the remote side if available.

Documentation/git-send-pack.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ git-send-pack - Push objects over Git protocol to another repository
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]
12+
'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
13+
[--verbose] [--thin] [--atomic]
14+
[--[no-]signed|--sign=(true|false|if-asked)]
15+
[<host>:]<directory> [<ref>...]
1316

1417
DESCRIPTION
1518
-----------
@@ -67,6 +70,17 @@ be in a separate packet, and the list must end with a flush packet.
6770
fails to update then the entire push will fail without changing any
6871
refs.
6972

73+
--[no-]signed::
74+
--sign=(true|false|if-asked)::
75+
GPG-sign the push request to update refs on the receiving
76+
side, to allow it to be checked by the hooks and/or be
77+
logged. If `false` or `--no-signed`, no signing will be
78+
attempted. If `true` or `--signed`, the push will fail if the
79+
server does not support signed pushes. If set to `if-asked`,
80+
sign if and only if the server supports signed pushes. The push
81+
will also fail if the actual call to `gpg --sign` fails. See
82+
linkgit:git-receive-pack[1] for the details on the receiving end.
83+
7084
<host>::
7185
A remote host to house the repository. When this
7286
part is specified, 'git-receive-pack' is invoked via

Documentation/gitremote-helpers.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ set by Git if the remote helper has the 'option' capability.
448448
'option update-shallow {'true'|'false'}::
449449
Allow to extend .git/shallow if the new refs require it.
450450

451+
'option pushcert {'true'|'false'}::
452+
GPG sign pushes.
453+
451454
SEE ALSO
452455
--------
453456
linkgit:git-remote[1]

builtin/push.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "transport.h"
1010
#include "parse-options.h"
1111
#include "submodule.h"
12+
#include "send-pack.h"
1213

1314
static const char * const push_usage[] = {
1415
N_("git push [<options>] [<repository> [<refspec>...]]"),
@@ -471,6 +472,24 @@ static int option_parse_recurse_submodules(const struct option *opt,
471472
return 0;
472473
}
473474

475+
static void set_push_cert_flags(int *flags, int v)
476+
{
477+
switch (v) {
478+
case SEND_PACK_PUSH_CERT_NEVER:
479+
*flags &= ~(TRANSPORT_PUSH_CERT_ALWAYS | TRANSPORT_PUSH_CERT_IF_ASKED);
480+
break;
481+
case SEND_PACK_PUSH_CERT_ALWAYS:
482+
*flags |= TRANSPORT_PUSH_CERT_ALWAYS;
483+
*flags &= ~TRANSPORT_PUSH_CERT_IF_ASKED;
484+
break;
485+
case SEND_PACK_PUSH_CERT_IF_ASKED:
486+
*flags |= TRANSPORT_PUSH_CERT_IF_ASKED;
487+
*flags &= ~TRANSPORT_PUSH_CERT_ALWAYS;
488+
break;
489+
}
490+
}
491+
492+
474493
static int git_push_config(const char *k, const char *v, void *cb)
475494
{
476495
int *flags = cb;
@@ -486,6 +505,23 @@ static int git_push_config(const char *k, const char *v, void *cb)
486505
else
487506
*flags &= ~TRANSPORT_PUSH_FOLLOW_TAGS;
488507
return 0;
508+
} else if (!strcmp(k, "push.gpgsign")) {
509+
const char *value;
510+
if (!git_config_get_value("push.gpgsign", &value)) {
511+
switch (git_config_maybe_bool("push.gpgsign", value)) {
512+
case 0:
513+
set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER);
514+
break;
515+
case 1:
516+
set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS);
517+
break;
518+
default:
519+
if (value && !strcasecmp(value, "if-asked"))
520+
set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);
521+
else
522+
return error("Invalid value for '%s'", k);
523+
}
524+
}
489525
}
490526

491527
return git_default_config(k, v, NULL);
@@ -495,6 +531,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
495531
{
496532
int flags = 0;
497533
int tags = 0;
534+
int push_cert = -1;
498535
int rc;
499536
const char *repo = NULL; /* default repository */
500537
struct option options[] = {
@@ -526,14 +563,17 @@ int cmd_push(int argc, const char **argv, const char *prefix)
526563
OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
527564
OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
528565
TRANSPORT_PUSH_FOLLOW_TAGS),
529-
OPT_BIT(0, "signed", &flags, N_("GPG sign the push"), TRANSPORT_PUSH_CERT),
566+
{ OPTION_CALLBACK,
567+
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
568+
PARSE_OPT_OPTARG, option_parse_push_signed },
530569
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
531570
OPT_END()
532571
};
533572

534573
packet_trace_identity("push");
535574
git_config(git_push_config, &flags);
536575
argc = parse_options(argc, argv, prefix, options, push_usage, 0);
576+
set_push_cert_flags(&flags, push_cert);
537577

538578
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
539579
die(_("--delete is incompatible with --all, --mirror and --tags"));

builtin/send-pack.c

Lines changed: 87 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@
1212
#include "version.h"
1313
#include "sha1-array.h"
1414
#include "gpg-interface.h"
15+
#include "gettext.h"
1516

16-
static const char send_pack_usage[] =
17-
"git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]\n"
18-
" --all and explicit <ref> specification are mutually exclusive.";
17+
static const char * const send_pack_usage[] = {
18+
N_("git send-pack [--all | --mirror] [--dry-run] [--force] "
19+
"[--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] "
20+
"[<host>:]<directory> [<ref>...]\n"
21+
" --all and explicit <ref> specification are mutually exclusive."),
22+
NULL,
23+
};
1924

2025
static struct send_pack_args args;
2126

@@ -92,6 +97,31 @@ static void print_helper_status(struct ref *ref)
9297
strbuf_release(&buf);
9398
}
9499

100+
static int send_pack_config(const char *k, const char *v, void *cb)
101+
{
102+
git_gpg_config(k, v, NULL);
103+
104+
if (!strcmp(k, "push.gpgsign")) {
105+
const char *value;
106+
if (!git_config_get_value("push.gpgsign", &value)) {
107+
switch (git_config_maybe_bool("push.gpgsign", value)) {
108+
case 0:
109+
args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
110+
break;
111+
case 1:
112+
args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
113+
break;
114+
default:
115+
if (value && !strcasecmp(value, "if-asked"))
116+
args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
117+
else
118+
return error("Invalid value for '%s'", k);
119+
}
120+
}
121+
}
122+
return 0;
123+
}
124+
95125
int cmd_send_pack(int argc, const char **argv, const char *prefix)
96126
{
97127
int i, nr_refspecs = 0;
@@ -107,116 +137,68 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
107137
int ret;
108138
int helper_status = 0;
109139
int send_all = 0;
140+
int verbose = 0;
110141
const char *receivepack = "git-receive-pack";
142+
unsigned dry_run = 0;
143+
unsigned send_mirror = 0;
144+
unsigned force_update = 0;
145+
unsigned quiet = 0;
146+
int push_cert = 0;
147+
unsigned use_thin_pack = 0;
148+
unsigned atomic = 0;
149+
unsigned stateless_rpc = 0;
111150
int flags;
112151
unsigned int reject_reasons;
113152
int progress = -1;
114153
int from_stdin = 0;
115154
struct push_cas_option cas = {0};
116155

117-
git_config(git_gpg_config, NULL);
118-
119-
argv++;
120-
for (i = 1; i < argc; i++, argv++) {
121-
const char *arg = *argv;
122-
123-
if (*arg == '-') {
124-
if (starts_with(arg, "--receive-pack=")) {
125-
receivepack = arg + 15;
126-
continue;
127-
}
128-
if (starts_with(arg, "--exec=")) {
129-
receivepack = arg + 7;
130-
continue;
131-
}
132-
if (starts_with(arg, "--remote=")) {
133-
remote_name = arg + 9;
134-
continue;
135-
}
136-
if (!strcmp(arg, "--all")) {
137-
send_all = 1;
138-
continue;
139-
}
140-
if (!strcmp(arg, "--dry-run")) {
141-
args.dry_run = 1;
142-
continue;
143-
}
144-
if (!strcmp(arg, "--mirror")) {
145-
args.send_mirror = 1;
146-
continue;
147-
}
148-
if (!strcmp(arg, "--force")) {
149-
args.force_update = 1;
150-
continue;
151-
}
152-
if (!strcmp(arg, "--quiet")) {
153-
args.quiet = 1;
154-
continue;
155-
}
156-
if (!strcmp(arg, "--verbose")) {
157-
args.verbose = 1;
158-
continue;
159-
}
160-
if (!strcmp(arg, "--signed")) {
161-
args.push_cert = 1;
162-
continue;
163-
}
164-
if (!strcmp(arg, "--progress")) {
165-
progress = 1;
166-
continue;
167-
}
168-
if (!strcmp(arg, "--no-progress")) {
169-
progress = 0;
170-
continue;
171-
}
172-
if (!strcmp(arg, "--thin")) {
173-
args.use_thin_pack = 1;
174-
continue;
175-
}
176-
if (!strcmp(arg, "--atomic")) {
177-
args.atomic = 1;
178-
continue;
179-
}
180-
if (!strcmp(arg, "--stateless-rpc")) {
181-
args.stateless_rpc = 1;
182-
continue;
183-
}
184-
if (!strcmp(arg, "--stdin")) {
185-
from_stdin = 1;
186-
continue;
187-
}
188-
if (!strcmp(arg, "--helper-status")) {
189-
helper_status = 1;
190-
continue;
191-
}
192-
if (!strcmp(arg, "--" CAS_OPT_NAME)) {
193-
if (parse_push_cas_option(&cas, NULL, 0) < 0)
194-
exit(1);
195-
continue;
196-
}
197-
if (!strcmp(arg, "--no-" CAS_OPT_NAME)) {
198-
if (parse_push_cas_option(&cas, NULL, 1) < 0)
199-
exit(1);
200-
continue;
201-
}
202-
if (starts_with(arg, "--" CAS_OPT_NAME "=")) {
203-
if (parse_push_cas_option(&cas,
204-
strchr(arg, '=') + 1, 0) < 0)
205-
exit(1);
206-
continue;
207-
}
208-
usage(send_pack_usage);
209-
}
210-
if (!dest) {
211-
dest = arg;
212-
continue;
213-
}
214-
refspecs = (const char **) argv;
215-
nr_refspecs = argc - i;
216-
break;
156+
struct option options[] = {
157+
OPT__VERBOSITY(&verbose),
158+
OPT_STRING(0, "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
159+
OPT_STRING(0, "exec", &receivepack, "receive-pack", N_("receive pack program")),
160+
OPT_STRING(0, "remote", &remote_name, "remote", N_("remote name")),
161+
OPT_BOOL(0, "all", &send_all, N_("push all refs")),
162+
OPT_BOOL('n' , "dry-run", &dry_run, N_("dry run")),
163+
OPT_BOOL(0, "mirror", &send_mirror, N_("mirror all refs")),
164+
OPT_BOOL('f', "force", &force_update, N_("force updates")),
165+
{ OPTION_CALLBACK,
166+
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
167+
PARSE_OPT_OPTARG, option_parse_push_signed },
168+
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
169+
OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")),
170+
OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
171+
OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")),
172+
OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")),
173+
OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")),
174+
{ OPTION_CALLBACK,
175+
0, CAS_OPT_NAME, &cas, N_("refname>:<expect"),
176+
N_("require old value of ref to be at this value"),
177+
PARSE_OPT_OPTARG, parseopt_push_cas_option },
178+
OPT_END()
179+
};
180+
181+
git_config(send_pack_config, NULL);
182+
argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);
183+
if (argc > 0) {
184+
dest = argv[0];
185+
refspecs = (const char **)(argv + 1);
186+
nr_refspecs = argc - 1;
217187
}
188+
218189
if (!dest)
219-
usage(send_pack_usage);
190+
usage_with_options(send_pack_usage, options);
191+
192+
args.verbose = verbose;
193+
args.dry_run = dry_run;
194+
args.send_mirror = send_mirror;
195+
args.force_update = force_update;
196+
args.quiet = quiet;
197+
args.push_cert = push_cert;
198+
args.progress = progress;
199+
args.use_thin_pack = use_thin_pack;
200+
args.atomic = atomic;
201+
args.stateless_rpc = stateless_rpc;
220202

221203
if (from_stdin) {
222204
struct argv_array all_refspecs = ARGV_ARRAY_INIT;
@@ -245,7 +227,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
245227
*/
246228
if ((refspecs && (send_all || args.send_mirror)) ||
247229
(send_all && args.send_mirror))
248-
usage(send_pack_usage);
230+
usage_with_options(send_pack_usage, options);
249231

250232
if (remote_name) {
251233
remote = remote_get(remote_name);

0 commit comments

Comments
 (0)