Merge branch 'sd/format-patch-to'

* sd/format-patch-to:
  send-email: add --no-cc, --no-to, and --no-bcc
  format-patch: add --no-cc, --no-to, and --no-add-headers
  format-patch: use a string_list for headers
  Add 'git format-patch --to=' option and 'format.to' configuration variable.
This commit is contained in:
Junio C Hamano 2010-03-15 00:58:55 -07:00
commit c24138bc55
5 changed files with 197 additions and 46 deletions

View File

@ -18,7 +18,7 @@ SYNOPSIS
[--in-reply-to=Message-Id] [--suffix=.<sfx>] [--in-reply-to=Message-Id] [--suffix=.<sfx>]
[--ignore-if-in-upstream] [--ignore-if-in-upstream]
[--subject-prefix=Subject-Prefix] [--subject-prefix=Subject-Prefix]
[--cc=<email>] [--to=<email>] [--cc=<email>]
[--cover-letter] [--cover-letter]
[<common diff options>] [<common diff options>]
[ <since> | <revision range> ] [ <since> | <revision range> ]
@ -162,6 +162,10 @@ will want to ensure that threading is disabled for `git send-email`.
allows for useful naming of a patch series, and can be allows for useful naming of a patch series, and can be
combined with the `--numbered` option. combined with the `--numbered` option.
--to=<email>::
Add a `To:` header to the email headers. This is in addition
to any configured headers, and may be used multiple times.
--cc=<email>:: --cc=<email>::
Add a `Cc:` header to the email headers. This is in addition Add a `Cc:` header to the email headers. This is in addition
to any configured headers, and may be used multiple times. to any configured headers, and may be used multiple times.
@ -202,8 +206,8 @@ CONFIGURATION
------------- -------------
You can specify extra mail header lines to be added to each message, You can specify extra mail header lines to be added to each message,
defaults for the subject prefix and file suffix, number patches when defaults for the subject prefix and file suffix, number patches when
outputting more than one patch, add "Cc:" headers, configure attachments, outputting more than one patch, add "To" or "Cc:" headers, configure
and sign off patches with configuration variables. attachments, and sign off patches with configuration variables.
------------ ------------
[format] [format]
@ -211,6 +215,7 @@ and sign off patches with configuration variables.
subjectprefix = CHANGE subjectprefix = CHANGE
suffix = .txt suffix = .txt
numbered = auto numbered = auto
to = <email>
cc = <email> cc = <email>
attach [ = mime-boundary-string ] attach [ = mime-boundary-string ]
signoff = true signoff = true

View File

@ -458,35 +458,28 @@ static int auto_number = 1;
static char *default_attach = NULL; static char *default_attach = NULL;
static char **extra_hdr; static struct string_list extra_hdr;
static int extra_hdr_nr; static struct string_list extra_to;
static int extra_hdr_alloc; static struct string_list extra_cc;
static char **extra_to;
static int extra_to_nr;
static int extra_to_alloc;
static char **extra_cc;
static int extra_cc_nr;
static int extra_cc_alloc;
static void add_header(const char *value) static void add_header(const char *value)
{ {
struct string_list_item *item;
int len = strlen(value); int len = strlen(value);
while (len && value[len - 1] == '\n') while (len && value[len - 1] == '\n')
len--; len--;
if (!strncasecmp(value, "to: ", 4)) { if (!strncasecmp(value, "to: ", 4)) {
ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc); item = string_list_append(value + 4, &extra_to);
extra_to[extra_to_nr++] = xstrndup(value + 4, len - 4); len -= 4;
return; } else if (!strncasecmp(value, "cc: ", 4)) {
item = string_list_append(value + 4, &extra_cc);
len -= 4;
} else {
item = string_list_append(value, &extra_hdr);
} }
if (!strncasecmp(value, "cc: ", 4)) {
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); item->string[len] = '\0';
extra_cc[extra_cc_nr++] = xstrndup(value + 4, len - 4);
return;
}
ALLOC_GROW(extra_hdr, extra_hdr_nr + 1, extra_hdr_alloc);
extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
} }
#define THREAD_SHALLOW 1 #define THREAD_SHALLOW 1
@ -504,11 +497,16 @@ static int git_format_config(const char *var, const char *value, void *cb)
} }
if (!strcmp(var, "format.suffix")) if (!strcmp(var, "format.suffix"))
return git_config_string(&fmt_patch_suffix, var, value); return git_config_string(&fmt_patch_suffix, var, value);
if (!strcmp(var, "format.to")) {
if (!value)
return config_error_nonbool(var);
string_list_append(value, &extra_to);
return 0;
}
if (!strcmp(var, "format.cc")) { if (!strcmp(var, "format.cc")) {
if (!value) if (!value)
return config_error_nonbool(var); return config_error_nonbool(var);
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); string_list_append(value, &extra_cc);
extra_cc[extra_cc_nr++] = xstrdup(value);
return 0; return 0;
} }
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@ -871,14 +869,31 @@ static int inline_callback(const struct option *opt, const char *arg, int unset)
static int header_callback(const struct option *opt, const char *arg, int unset) static int header_callback(const struct option *opt, const char *arg, int unset)
{ {
if (unset) {
string_list_clear(&extra_hdr, 0);
string_list_clear(&extra_to, 0);
string_list_clear(&extra_cc, 0);
} else {
add_header(arg); add_header(arg);
}
return 0;
}
static int to_callback(const struct option *opt, const char *arg, int unset)
{
if (unset)
string_list_clear(&extra_to, 0);
else
string_list_append(arg, &extra_to);
return 0; return 0;
} }
static int cc_callback(const struct option *opt, const char *arg, int unset) static int cc_callback(const struct option *opt, const char *arg, int unset)
{ {
ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); if (unset)
extra_cc[extra_cc_nr++] = xstrdup(arg); string_list_clear(&extra_cc, 0);
else
string_list_append(arg, &extra_cc);
return 0; return 0;
} }
@ -937,10 +952,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_NONEG | PARSE_OPT_NOARG }, PARSE_OPT_NONEG | PARSE_OPT_NOARG },
OPT_GROUP("Messaging"), OPT_GROUP("Messaging"),
{ OPTION_CALLBACK, 0, "add-header", NULL, "header", { OPTION_CALLBACK, 0, "add-header", NULL, "header",
"add email header", PARSE_OPT_NONEG, "add email header", 0, header_callback },
header_callback }, { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header",
0, to_callback },
{ OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header", { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
PARSE_OPT_NONEG, cc_callback }, 0, cc_callback },
OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id", OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
"make first mail a reply to <message-id>"), "make first mail a reply to <message-id>"),
{ OPTION_CALLBACK, 0, "attach", &rev, "boundary", { OPTION_CALLBACK, 0, "attach", &rev, "boundary",
@ -956,6 +972,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
OPT_END() OPT_END()
}; };
extra_hdr.strdup_strings = 1;
extra_to.strdup_strings = 1;
extra_cc.strdup_strings = 1;
git_config(git_format_config, NULL); git_config(git_format_config, NULL);
init_revisions(&rev, prefix); init_revisions(&rev, prefix);
rev.commit_format = CMIT_FMT_EMAIL; rev.commit_format = CMIT_FMT_EMAIL;
@ -992,29 +1011,29 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
add_signoff = xmemdupz(committer, endpos - committer + 1); add_signoff = xmemdupz(committer, endpos - committer + 1);
} }
for (i = 0; i < extra_hdr_nr; i++) { for (i = 0; i < extra_hdr.nr; i++) {
strbuf_addstr(&buf, extra_hdr[i]); strbuf_addstr(&buf, extra_hdr.items[i].string);
strbuf_addch(&buf, '\n'); strbuf_addch(&buf, '\n');
} }
if (extra_to_nr) if (extra_to.nr)
strbuf_addstr(&buf, "To: "); strbuf_addstr(&buf, "To: ");
for (i = 0; i < extra_to_nr; i++) { for (i = 0; i < extra_to.nr; i++) {
if (i) if (i)
strbuf_addstr(&buf, " "); strbuf_addstr(&buf, " ");
strbuf_addstr(&buf, extra_to[i]); strbuf_addstr(&buf, extra_to.items[i].string);
if (i + 1 < extra_to_nr) if (i + 1 < extra_to.nr)
strbuf_addch(&buf, ','); strbuf_addch(&buf, ',');
strbuf_addch(&buf, '\n'); strbuf_addch(&buf, '\n');
} }
if (extra_cc_nr) if (extra_cc.nr)
strbuf_addstr(&buf, "Cc: "); strbuf_addstr(&buf, "Cc: ");
for (i = 0; i < extra_cc_nr; i++) { for (i = 0; i < extra_cc.nr; i++) {
if (i) if (i)
strbuf_addstr(&buf, " "); strbuf_addstr(&buf, " ");
strbuf_addstr(&buf, extra_cc[i]); strbuf_addstr(&buf, extra_cc.items[i].string);
if (i + 1 < extra_cc_nr) if (i + 1 < extra_cc.nr)
strbuf_addch(&buf, ','); strbuf_addch(&buf, ',');
strbuf_addch(&buf, '\n'); strbuf_addch(&buf, '\n');
} }
@ -1223,6 +1242,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
fclose(stdout); fclose(stdout);
} }
free(list); free(list);
string_list_clear(&extra_to, 0);
string_list_clear(&extra_cc, 0);
string_list_clear(&extra_hdr, 0);
if (ignore_if_in_upstream) if (ignore_if_in_upstream)
free_patch_ids(&ids); free_patch_ids(&ids);
return 0; return 0;

View File

@ -47,9 +47,9 @@ git send-email [options] <file | directory | rev-list options >
Composing: Composing:
--from <str> * Email From: --from <str> * Email From:
--to <str> * Email To: --[no-]to <str> * Email To:
--cc <str> * Email Cc: --[no-]cc <str> * Email Cc:
--bcc <str> * Email Bcc: --[no-]bcc <str> * Email Bcc:
--subject <str> * Email "Subject:" --subject <str> * Email "Subject:"
--in-reply-to <str> * Email "In-Reply-To:" --in-reply-to <str> * Email "In-Reply-To:"
--annotate * Review each patch that will be sent in an editor. --annotate * Review each patch that will be sent in an editor.
@ -135,7 +135,7 @@ sub unique_email_list(@);
sub cleanup_compose_files(); sub cleanup_compose_files();
# Variables we fill in automatically, or via prompting: # Variables we fill in automatically, or via prompting:
my (@to,@cc,@initial_cc,@bcclist,@xh, my (@to,$no_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
$initial_reply_to,$initial_subject,@files, $initial_reply_to,$initial_subject,@files,
$author,$sender,$smtp_authpass,$annotate,$compose,$time); $author,$sender,$smtp_authpass,$annotate,$compose,$time);
@ -261,8 +261,11 @@ my $rc = GetOptions("sender|from=s" => \$sender,
"in-reply-to=s" => \$initial_reply_to, "in-reply-to=s" => \$initial_reply_to,
"subject=s" => \$initial_subject, "subject=s" => \$initial_subject,
"to=s" => \@to, "to=s" => \@to,
"no-to" => \$no_to,
"cc=s" => \@initial_cc, "cc=s" => \@initial_cc,
"no-cc" => \$no_cc,
"bcc=s" => \@bcclist, "bcc=s" => \@bcclist,
"no-bcc" => \$no_bcc,
"chain-reply-to!" => \$chain_reply_to, "chain-reply-to!" => \$chain_reply_to,
"smtp-server=s" => \$smtp_server, "smtp-server=s" => \$smtp_server,
"smtp-server-port=s" => \$smtp_server_port, "smtp-server-port=s" => \$smtp_server_port,
@ -305,6 +308,9 @@ sub read_config {
foreach my $setting (keys %config_settings) { foreach my $setting (keys %config_settings) {
my $target = $config_settings{$setting}; my $target = $config_settings{$setting};
next if $setting eq "to" and defined $no_to;
next if $setting eq "cc" and defined $no_cc;
next if $setting eq "bcc" and defined $no_bcc;
if (ref($target) eq "ARRAY") { if (ref($target) eq "ARRAY") {
unless (@$target) { unless (@$target) {
my @values = Git::config(@repo, "$prefix.$setting"); my @values = Git::config(@repo, "$prefix.$setting");

View File

@ -143,6 +143,58 @@ test_expect_success 'configuration headers and command line headers' '
grep "^ *S. E. Cipient <scipient@example.com>\$" patch7 grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
' '
test_expect_success 'command line To: header' '
git config --unset-all format.headers &&
git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8
'
test_expect_success 'configuration To: header' '
git config format.to "R. E. Cipient <rcipient@example.com>" &&
git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9
'
test_expect_success '--no-to overrides config.to' '
git config --replace-all format.to \
"R. E. Cipient <rcipient@example.com>" &&
git format-patch --no-to --stdout master..side |
sed -e "/^\$/q" >patch10 &&
! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10
'
test_expect_success '--no-to and --to replaces config.to' '
git config --replace-all format.to \
"Someone <someone@out.there>" &&
git format-patch --no-to --to="Someone Else <else@out.there>" \
--stdout master..side |
sed -e "/^\$/q" >patch11 &&
! grep "^To: Someone <someone@out.there>\$" patch11 &&
grep "^To: Someone Else <else@out.there>\$" patch11
'
test_expect_success '--no-cc overrides config.cc' '
git config --replace-all format.cc \
"C. E. Cipient <rcipient@example.com>" &&
git format-patch --no-cc --stdout master..side |
sed -e "/^\$/q" >patch12 &&
! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12
'
test_expect_success '--no-add-headers overrides config.headers' '
git config --replace-all format.headers \
"Header1: B. E. Cipient <rcipient@example.com>" &&
git format-patch --no-add-headers --stdout master..side |
sed -e "/^\$/q" >patch13 &&
! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13
'
test_expect_success 'multiple files' ' test_expect_success 'multiple files' '
rm -rf patches/ && rm -rf patches/ &&

View File

@ -852,4 +852,70 @@ test_expect_success 'no warning with sendemail.chainreplyto = true' '
! grep "no-chain-reply-to" errors ! grep "no-chain-reply-to" errors
' '
test_expect_success 'sendemail.to works' '
git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
$patches $patches >stdout &&
grep "To: Somebody <somebody@ex.com>" stdout
'
test_expect_success '--no-to overrides sendemail.to' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
--no-to \
--to=nobody@example.com \
$patches $patches >stdout &&
grep "To: nobody@example.com" stdout &&
! grep "To: Somebody <somebody@ex.com>" stdout
'
test_expect_success 'sendemail.cc works' '
git config --replace-all sendemail.cc "Somebody <somebody@ex.com>" &&
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
$patches $patches >stdout &&
grep "Cc: Somebody <somebody@ex.com>" stdout
'
test_expect_success '--no-cc overrides sendemail.cc' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
--no-cc \
--cc=bodies@example.com \
--to=nobody@example.com \
$patches $patches >stdout &&
grep "Cc: bodies@example.com" stdout &&
! grep "Cc: Somebody <somebody@ex.com>" stdout
'
test_expect_success 'sendemail.bcc works' '
git config --replace-all sendemail.bcc "Other <other@ex.com>" &&
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--smtp-server relay.example.com \
$patches $patches >stdout &&
grep "RCPT TO:<other@ex.com>" stdout
'
test_expect_success '--no-bcc overrides sendemail.bcc' '
git send-email \
--dry-run \
--from="Example <nobody@example.com>" \
--no-bcc \
--bcc=bodies@example.com \
--to=nobody@example.com \
--smtp-server relay.example.com \
$patches $patches >stdout &&
grep "RCPT TO:<bodies@example.com>" stdout &&
! grep "RCPT TO:<other@ex.com>" stdout
'
test_done test_done