Merge branch 'fc/send-email-annotate'

Allows format-patch --cover-letter to be configurable; the most
notable is the "auto" mode to create cover-letter only for multi
patch series.

* fc/send-email-annotate:
  rebase-am: explicitly disable cover-letter
  format-patch: trivial cleanups
  format-patch: add format.coverLetter configuration variable
  log: update to OPT_BOOL
  format-patch: refactor branch name calculation
  format-patch: improve head calculation for cover-letter
  send-email: make annotate configurable
This commit is contained in:
Junio C Hamano 2013-04-18 11:49:11 -07:00
commit 288aa7534a
7 changed files with 136 additions and 90 deletions

View file

@ -1109,6 +1109,11 @@ format.signoff::
the rights to submit this work under the same open source license.
Please see the 'SubmittingPatches' document for further discussion.
format.coverLetter::
A boolean that controls whether to generate a cover-letter when
format-patch is invoked, but in addition can be set to "auto", to
generate a cover-letter only when there's more than one patch.
filter.<driver>.clean::
The command which is used to convert the content of a worktree
file to a blob upon checkin. See linkgit:gitattributes[5] for
@ -2016,6 +2021,7 @@ sendemail.<identity>.*::
sendemail.aliasesfile::
sendemail.aliasfiletype::
sendemail.annotate::
sendemail.bcc::
sendemail.cc::
sendemail.cccmd::

View file

@ -20,7 +20,7 @@ SYNOPSIS
[--ignore-if-in-upstream]
[--subject-prefix=Subject-Prefix] [(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--cover-letter] [--quiet] [--notes[=<ref>]]
[--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
[<common diff options>]
[ <since> | <revision range> ]
@ -195,7 +195,7 @@ will want to ensure that threading is disabled for `git send-email`.
`Cc:`, and custom) headers added so far from config or command
line.
--cover-letter::
--[no-]cover-letter::
In addition to the patches, generate a cover letter file
containing the shortlog and the overall diffstat. You can
fill in a description in the file before sending it out.
@ -260,6 +260,7 @@ attachments, and sign off patches with configuration variables.
cc = <email>
attach [ = mime-boundary-string ]
signoff = true
coverletter = auto
------------

View file

@ -45,8 +45,9 @@ Composing
~~~~~~~~~
--annotate::
Review and edit each patch you're about to send. See the
CONFIGURATION section for 'sendemail.multiedit'.
Review and edit each patch you're about to send. Default is the value
of 'sendemail.annotate'. See the CONFIGURATION section for
'sendemail.multiedit'.
--bcc=<address>::
Specify a "Bcc:" value for each email. Default is the value of

View file

@ -100,9 +100,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
int quiet = 0, source = 0, mailmap = 0;
const struct option builtin_log_options[] = {
OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
OPT_BOOLEAN(0, "source", &source, N_("show source")),
OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")),
OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
OPT_BOOL(0, "source", &source, N_("show source")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback},
OPT_END()
@ -622,6 +622,14 @@ static void add_header(const char *value)
static int thread;
static int do_signoff;
static const char *signature = git_version_string;
static int config_cover_letter;
enum {
COVER_UNSET,
COVER_OFF,
COVER_ON,
COVER_AUTO
};
static int git_format_config(const char *var, const char *value, void *cb)
{
@ -683,6 +691,14 @@ static int git_format_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "format.signature"))
return git_config_string(&signature, var, value);
if (!strcmp(var, "format.coverletter")) {
if (value && !strcasecmp(value, "auto")) {
config_cover_letter = COVER_AUTO;
return 0;
}
config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
return 0;
}
return git_log_config(var, value, cb);
}
@ -794,9 +810,37 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
}
}
static char *find_branch_name(struct rev_info *rev)
{
int i, positive = -1;
unsigned char branch_sha1[20];
const unsigned char *tip_sha1;
const char *ref;
char *full_ref, *branch = NULL;
for (i = 0; i < rev->cmdline.nr; i++) {
if (rev->cmdline.rev[i].flags & UNINTERESTING)
continue;
if (positive < 0)
positive = i;
else
return NULL;
}
if (positive < 0)
return NULL;
ref = rev->cmdline.rev[positive].name;
tip_sha1 = rev->cmdline.rev[positive].item->sha1;
if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
!prefixcmp(full_ref, "refs/heads/") &&
!hashcmp(tip_sha1, branch_sha1))
branch = xstrdup(full_ref + strlen("refs/heads/"));
free(full_ref);
return branch;
}
static void make_cover_letter(struct rev_info *rev, int use_stdout,
struct commit *origin,
int nr, struct commit **list, struct commit *head,
int nr, struct commit **list,
const char *branch_name,
int quiet)
{
@ -810,6 +854,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
struct diff_options opts;
int need_8bit_cte = 0;
struct pretty_print_context pp = {0};
struct commit *head = list[0];
if (rev->commit_format != CMIT_FMT_EMAIL)
die(_("Cover letter needs email format"));
@ -827,6 +872,9 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
if (has_non_ascii(list[i]->buffer))
need_8bit_cte = 1;
if (!branch_name)
branch_name = find_branch_name(rev);
msg = body;
pp.fmt = CMIT_FMT_EMAIL;
pp.date_mode = DATE_RFC2822;
@ -1033,45 +1081,6 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
static char *find_branch_name(struct rev_info *rev)
{
int i, positive = -1;
unsigned char branch_sha1[20];
const unsigned char *tip_sha1;
const char *ref;
char *full_ref, *branch = NULL;
for (i = 0; i < rev->cmdline.nr; i++) {
if (rev->cmdline.rev[i].flags & UNINTERESTING)
continue;
if (positive < 0)
positive = i;
else
return NULL;
}
if (0 <= positive) {
ref = rev->cmdline.rev[positive].name;
tip_sha1 = rev->cmdline.rev[positive].item->sha1;
} else if (!rev->cmdline.nr && rev->pending.nr == 1 &&
!strcmp(rev->pending.objects[0].name, "HEAD")) {
/*
* No actual ref from command line, but "HEAD" from
* rev->def was added in setup_revisions()
* e.g. format-patch --cover-letter -12
*/
ref = "HEAD";
tip_sha1 = rev->pending.objects[0].item->sha1;
} else {
return NULL;
}
if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
!prefixcmp(full_ref, "refs/heads/") &&
!hashcmp(tip_sha1, branch_sha1))
branch = xstrdup(full_ref + strlen("refs/heads/"));
free(full_ref);
return branch;
}
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@ -1083,10 +1092,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int start_number = -1;
int just_numbers = 0;
int ignore_if_in_upstream = 0;
int cover_letter = 0;
int cover_letter = -1;
int boundary_count = 0;
int no_binary_diff = 0;
struct commit *origin = NULL, *head = NULL;
struct commit *origin = NULL;
const char *in_reply_to = NULL;
struct patch_ids ids;
struct strbuf buf = STRBUF_INIT;
@ -1101,12 +1110,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
N_("use [PATCH] even with multiple patches"),
PARSE_OPT_NOARG, no_numbered_callback },
OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
OPT_BOOLEAN(0, "stdout", &use_stdout,
OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
OPT_BOOL(0, "stdout", &use_stdout,
N_("print patches to standard out")),
OPT_BOOLEAN(0, "cover-letter", &cover_letter,
OPT_BOOL(0, "cover-letter", &cover_letter,
N_("generate a cover letter")),
OPT_BOOLEAN(0, "numbered-files", &just_numbers,
OPT_BOOL(0, "numbered-files", &just_numbers,
N_("use simple number sequence for output file names")),
OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
N_("use <sfx> instead of '.patch'")),
@ -1280,28 +1289,36 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
}
if (rev.pending.nr == 1) {
int check_head = 0;
if (rev.max_count < 0 && !rev.show_root_diff) {
/*
* This is traditional behaviour of "git format-patch
* origin" that prepares what the origin side still
* does not have.
*/
unsigned char sha1[20];
const char *ref;
rev.pending.objects[0].item->flags |= UNINTERESTING;
add_head_to_pending(&rev);
ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
if (ref && !prefixcmp(ref, "refs/heads/"))
branch_name = xstrdup(ref + strlen("refs/heads/"));
else
branch_name = xstrdup(""); /* no branch */
check_head = 1;
}
/*
* Otherwise, it is "format-patch -22 HEAD", and/or
* "format-patch --root HEAD". The user wants
* get_revision() to do the usual traversal.
*/
if (!strcmp(rev.pending.objects[0].name, "HEAD"))
check_head = 1;
if (check_head) {
unsigned char sha1[20];
const char *ref;
ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
if (ref && !prefixcmp(ref, "refs/heads/"))
branch_name = xstrdup(ref + strlen("refs/heads/"));
else
branch_name = xstrdup(""); /* no branch */
}
}
/*
@ -1310,29 +1327,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
*/
rev.show_root_diff = 1;
if (cover_letter) {
/*
* NEEDSWORK:randomly pick one positive commit to show
* diffstat; this is often the tip and the command
* happens to do the right thing in most cases, but a
* complex command like "--cover-letter a b c ^bottom"
* picks "c" and shows diffstat between bottom..c
* which may not match what the series represents at
* all and totally broken.
*/
int i;
for (i = 0; i < rev.pending.nr; i++) {
struct object *o = rev.pending.objects[i].item;
if (!(o->flags & UNINTERESTING))
head = (struct commit *)o;
}
/* There is nothing to show; it is not an error, though. */
if (!head)
return 0;
if (!branch_name)
branch_name = find_branch_name(&rev);
}
if (ignore_if_in_upstream) {
/* Don't say anything if head and upstream are the same. */
if (rev.pending.nr == 2) {
@ -1364,11 +1358,21 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
list = xrealloc(list, nr * sizeof(list[0]));
list[nr - 1] = commit;
}
if (nr == 0)
/* nothing to do */
return 0;
total = nr;
if (!keep_subject && auto_number && total > 1)
numbered = 1;
if (numbered)
rev.total = total + start_number - 1;
if (cover_letter == -1) {
if (config_cover_letter == COVER_AUTO)
cover_letter = (total > 1);
else
cover_letter = (config_cover_letter == COVER_ON);
}
if (in_reply_to || thread || cover_letter)
rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
if (in_reply_to) {
@ -1381,7 +1385,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (thread)
gen_message_id(&rev, "cover");
make_cover_letter(&rev, use_stdout,
origin, nr, list, head, branch_name, quiet);
origin, nr, list, branch_name, quiet);
total++;
start_number--;
}

View file

@ -31,8 +31,8 @@ else
rm -f "$GIT_DIR/rebased-patches"
git format-patch -k --stdout --full-index --ignore-if-in-upstream \
--src-prefix=a/ --dst-prefix=b/ \
--no-renames $root_flag "$revisions" >"$GIT_DIR/rebased-patches"
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
$root_flag "$revisions" >"$GIT_DIR/rebased-patches"
ret=$?
if test 0 != $ret

View file

@ -54,7 +54,7 @@ sub usage {
--[no-]bcc <str> * Email Bcc:
--subject <str> * Email "Subject:"
--in-reply-to <str> * Email "In-Reply-To:"
--annotate * Review each patch that will be sent in an editor.
--[no-]annotate * Review each patch that will be sent in an editor.
--compose * Open an editor for introduction.
--compose-encoding <str> * Encoding to assume for introduction.
--8bit-encoding <str> * Encoding to assume 8bit mails if undeclared
@ -212,7 +212,8 @@ sub do_edit {
"signedoffbycc" => [\$signed_off_by_cc, undef],
"signedoffcc" => [\$signed_off_by_cc, undef], # Deprecated
"validate" => [\$validate, 1],
"multiedit" => [\$multiedit, undef]
"multiedit" => [\$multiedit, undef],
"annotate" => [\$annotate, undef]
);
my %config_settings = (
@ -304,7 +305,7 @@ sub signal_handler {
"smtp-debug:i" => \$debug_net_smtp,
"smtp-domain:s" => \$smtp_domain,
"identity=s" => \$identity,
"annotate" => \$annotate,
"annotate!" => \$annotate,
"compose" => \$compose,
"quiet" => \$quiet,
"cc-cmd=s" => \$cc_cmd,

View file

@ -1284,4 +1284,37 @@ test_expect_success 'cover letter using branch description (6)' '
grep hello actual >/dev/null
'
test_expect_success 'cover letter with nothing' '
git format-patch --stdout --cover-letter >actual &&
test_line_count = 0 actual
'
test_expect_success 'cover letter auto' '
mkdir -p tmp &&
test_when_finished "rm -rf tmp;
git config --unset format.coverletter" &&
git config format.coverletter auto &&
git format-patch -o tmp -1 >list &&
test_line_count = 1 list &&
git format-patch -o tmp -2 >list &&
test_line_count = 3 list
'
test_expect_success 'cover letter auto user override' '
mkdir -p tmp &&
test_when_finished "rm -rf tmp;
git config --unset format.coverletter" &&
git config format.coverletter auto &&
git format-patch -o tmp --cover-letter -1 >list &&
test_line_count = 2 list &&
git format-patch -o tmp --cover-letter -2 >list &&
test_line_count = 3 list &&
git format-patch -o tmp --no-cover-letter -1 >list &&
test_line_count = 1 list &&
git format-patch -o tmp --no-cover-letter -2 >list &&
test_line_count = 2 list
'
test_done