git send-email improvements

34 messages, 5 authors, 2016-06-15 · open the first message on its own page

git send-email improvements

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

The teaser
==========

This series has been sent using:
  git send-email --to git@vger.kernel.org --compose --annotate HEAD~3..


The series
==========

Here is a patch series to improve git send-email following our
discussions at GitTogether'08, despite my hate for perl.

The first patch is a minor nitpick, because leaking fd's sucks.

The second patch allow git-send-email to receive revision lists as
arguments. This doesn't allow complex arguments combinations as it
proces the revision lists one by one (IOW ^$sha1 $sha2 won't work as
expected _at all_) but this shouldn't be a problem since this command is
primarily used for interactive users. People wanting to use
git-send-email with complex revision lists through scripts MUST
git-format-patch first into a safe temporary directory and use
git-send-email on this afterwards.

The last patch adds the possibility to review patches into an editor
before sending them, which allow you (thanks to patch 2) to serialize,
review, annotate, and send patches in one command.


Further discussion
==================

I think one could make git send-email better doing this:

(1) make --compose and --annotate default, do not asking for a Subject
    if it's missing, neither should we ask for the in-reply-to if it's
    missing.

    Then spawn the editor with a first empty file that contains rougly a
    template looking like this:

        ----8<----
        GIT: Purge this buffer from any content if you don't want a series summary
        GIT:
        GIT: Lines beginning in "GIT: " will be removed.
        GIT: Consider including an overall diffstat or table of contents
        GIT: for the patch you are writing.
        GIT:
        GIT: Please fill a Subject if missing
        GIT: Leave the In-Reply-To field empty if not applicable.
        Subject:
        In-Reply-To:
        --> <we may want to add some more headers here: To/Cc/Bcc/...>

        GIT: put the content of the mail below this line


        GIT: [PATCH 1/10] ....  \
        GIT: [PATCH 2/10] ....   | this would contain all the Subject's
        [...]                    | from the commits that are beeing sent
        GIT: [PATCH 8/10] ....   | as a conveniency for people not
        GIT: [PATCH 9/10] ....   | having to cut&paste them
        GIT: [PATCH 10/10] .... /
        ---->8----

    I suggest we don't enable --compose when the series is reduced to
    one patch, as the usual way is to comment inline. This is probably
    arguable.

(2) Introduce a --batch option that basically:
    * turns --compose and --annotate off
    * turns any interactive feature off
    * complain (and fail) if it misses any information that is usually
      asked interactively

What do you think ?

[PATCH 2/3] git send-email: interpret unknown files as revision lists

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

Instead of skipping unkown files on the command line, pass them through
git format-patch into a safe temporary directory. This allow no
complicated rev-list option lists combining "--all" "--not" and so on, but
allow to use ranges which are quite enough for most of the use cases.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |    2 +-
 git-send-email.perl              |    6 ++++--
 2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 82f5056..cafff1a 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails
 
 SYNOPSIS
 --------
-'git send-email' [options] <file|directory> [... file|directory]
+'git send-email' [options] <file|directory|rev-list>...
 
 
 DESCRIPTION
diff --git a/git-send-email.perl b/git-send-email.perl
index 94ca5c8..0d50ee2 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -22,6 +22,7 @@ use Term::ReadLine;
 use Getopt::Long;
 use Data::Dumper;
 use Term::ANSIColor;
+use File::Temp qw/ tempdir /;
 use Git;
 
 package FakeTerm;
@@ -38,7 +39,7 @@ package main;
 
 sub usage {
 	print <<EOT;
-git send-email [options] <file | directory>...
+git send-email [options] <file | directory | rev-list >
 
   Composing:
     --from                  <str>  * Email From:
@@ -378,7 +379,8 @@ for my $f (@ARGV) {
 	} elsif (-f $f or -p $f) {
 		push @files, $f;
 	} else {
-		print STDERR "Skipping $f - not found.\n";
+		my $tempdir = tempdir(CLEANUP => 1);
+		push @files, $repo->command('format-patch', '-o', $tempdir, $f);
 	}
 }
 
-- 
1.6.0.3.759.g40a2.dirty

[PATCH 1/3] git send-email: avoid leaking directory file descriptors.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

Signed-off-by: Pierre Habouzit <redacted>
---
 git-send-email.perl |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index bdbfac6..94ca5c8 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -374,10 +374,9 @@ for my $f (@ARGV) {
 
 		push @files, grep { -f $_ } map { +$f . "/" . $_ }
 				sort readdir(DH);
-
+		closedir(DH);
 	} elsif (-f $f or -p $f) {
 		push @files, $f;
-
 	} else {
 		print STDERR "Skipping $f - not found.\n";
 	}
-- 
1.6.0.3.759.g40a2.dirty

[PATCH 3/3] git send-email: add --annotate option

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

This allows to review every patch (and fix various aspects of them, or
comment them) in an editor just before being sent. Combined to the fact
that git send-email can now process revision lists, this makes git
send-email and efficient way to review and send patches interactively.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |   11 +++++++++++
 git-send-email.perl              |   26 ++++++++++++++++++++++++--
 2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index cafff1a..9ee81d5 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -37,6 +37,11 @@ The --bcc option must be repeated for each user you want on the bcc list.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
+--annotate::
+	Review each patch you're about to send in an editor. The setting
+	'sendemail.multiedit' defines if this will spawn one editor per patch
+	or one for all of them at once.
+
 --compose::
 	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
 	introductory message for the patch series.
@@ -204,6 +209,12 @@ sendemail.aliasfiletype::
 	Format of the file(s) specified in sendemail.aliasesfile. Must be
 	one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
+sendemail.multiedit::
+	If true (default), a single editor instance will be spawned to edit
+	files you have to edit (patches when '--annotate' is used, and the
+	summary when '--compose' is used). If false, files will be edited one
+	after the other, spawning a new editor each time.
+
 
 Author
 ------
diff --git a/git-send-email.perl b/git-send-email.perl
index 0d50ee2..65c254d 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -48,6 +48,7 @@ git send-email [options] <file | directory | rev-list >
     --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.
     --compose                      * Open an editor for introduction.
 
   Sending:
@@ -130,7 +131,8 @@ my $compose_filename = ".msg.$$";
 
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
+	$initial_reply_to,$initial_subject,@files,
+	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
 my $envelope_sender;
 
@@ -151,6 +153,17 @@ if ($@) {
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
 
+# Handle interactive edition of files.
+my $multiedit;
+my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+sub do_edit {
+	if (defined($multiedit) && !$multiedit) {
+		map { system('sh', '-c', $editor.' "$@"', $editor, $_); } @_;
+	} else {
+		system('sh', '-c', $editor.' "$@"', $editor, @_);
+	}
+}
+
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
@@ -180,6 +193,7 @@ my %config_settings = (
     "aliasesfile" => \@alias_files,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
+    "multiedit" => \$multiedit,
 );
 
 # Handle Uncouth Termination
@@ -222,6 +236,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
 		    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
 		    "smtp-encryption=s" => \$smtp_encryption,
 		    "identity=s" => \$identity,
+		    "annotate" => \$annotate,
 		    "compose" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
@@ -499,7 +514,12 @@ EOT
 	close(C);
 
 	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-	system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
 
 	open(C2,">",$compose_filename . ".final")
 		or die "Failed to open $compose_filename.final : " . $!;
@@ -548,6 +568,8 @@ EOT
 	}
 
 	@files = ($compose_filename . ".final", @files);
+} elsif ($annotate) {
+	do_edit(@files);
 }
 
 # Variables we set as part of the loop over files
-- 
1.6.0.3.759.g40a2.dirty

[PATCH 1/3] git send-email: make the message file name more specific.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

This helps editors choosing their syntax hilighting properly.

Signed-off-by: Pierre Habouzit <redacted>
---
 git-send-email.perl |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index 65c254d..4ca571f 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -127,7 +127,7 @@ sub unique_email_list(@);
 sub cleanup_compose_files();
 
 # Constants (essentially)
-my $compose_filename = ".msg.$$";
+my $compose_filename = ".gitsendemail.msg.$$";
 
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-- 
1.6.0.3.763.g0275.dirty

Further enhancement proposal for git-send-email

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

Here is a three patch series (again).

[PATCH 1/3] git send-email: make the message file name more specific.
  -> quite independant, and should IMHO be taken.


[PATCH 2/3] git send-email: do not ask questions when --compose is used.
[PATCH 3/3] git send-email: turn --compose on when more than one patch.

  Those two patches enhance git-send-email by making ask less questions
  when --compose is used (as it can grab the subject, from and reply-to
  from the buffer).

  It also turns --compose on by default as soon as there is more than
  one patch, as I believe than commenting a patch series is more often
  done than not. It's is really trivial to "refuse" to comment the
  series by just erasing the full buffer content, which should not
  really be too anoying (or one can explicitely pass --no-compose for
  the same result).

  It's probable that those two changes may trigger some discussion
  though, but I just used that to send this series, and I can tell with
  this git-send-email is nearer what I would like it to be.

[PATCH 2/3] git send-email: do not ask questions when --compose is used.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

When --compose is used, we can grab the From/Subject/In-Reply-To from the
edited summary, let it be so and don't ask the user silly questions.

The summary templates gets quite revamped, and includes the list of
patches subjects that are going to be sent with this batch.

Signed-off-by: Pierre Habouzit <redacted>
---
 git-send-email.perl |  174 ++++++++++++++++++++++++++++++---------------------
 1 files changed, 102 insertions(+), 72 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index 4ca571f..5c189a7 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -417,6 +417,105 @@ if (@files) {
 	usage();
 }
 
+sub get_patch_subject($) {
+	my $fn = shift;
+	open (my $fh, '<', $fn);
+	while (my $line = <$fh>) {
+		next unless ($line =~ /^Subject: (.*)$/);
+		close $fh;
+		return "GIT: $1\n";
+	}
+	close $fh;
+	die "No subject line in $fn ?";
+}
+
+if ($compose) {
+	# Note that this does not need to be secure, but we will make a small
+	# effort to have it be unique
+	open(C,">",$compose_filename)
+		or die "Failed to open for writing $compose_filename: $!";
+
+
+	my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
+	my $tpl_subject = $initial_subject || '';
+	my $tpl_reply_to = $initial_reply_to || '';
+
+	print C <<EOT;
+From $tpl_sender # This line is ignored.
+GIT: Lines beginning in "GIT: " will be removed.
+GIT: Consider including an overall diffstat or table of contents
+GIT: for the patch you are writing.
+From: $tpl_sender
+Subject: $tpl_subject
+In-Reply-To: $tpl_reply_to
+
+GIT: Please enter your email below this line.
+
+EOT
+	for my $f (@files) {
+		print C get_patch_subject($f);
+	}
+	close(C);
+
+	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
+
+	open(C2,">",$compose_filename . ".final")
+		or die "Failed to open $compose_filename.final : " . $!;
+
+	open(C,"<",$compose_filename)
+		or die "Failed to open $compose_filename : " . $!;
+
+	my $need_8bit_cte = file_has_nonascii($compose_filename);
+	my $in_body = 0;
+	my $summary_empty = 1;
+	while(<C>) {
+		next if m/^GIT: /;
+		if ($in_body) {
+		} elsif (/^\n$/) {
+			$in_body = 1;
+			if ($need_8bit_cte) {
+				print C2 "MIME-Version: 1.0\n",
+					 "Content-Type: text/plain; ",
+					   "charset=utf-8\n",
+					 "Content-Transfer-Encoding: 8bit\n";
+			}
+		} elsif (/^MIME-Version:/i) {
+			$need_8bit_cte = 0;
+		} elsif (/^Subject:\s*(.+)\s*$/i) {
+			$initial_subject = $1;
+			my $subject = $initial_subject;
+			$_ = "Subject: " .
+				($subject =~ /[^[:ascii:]]/ ?
+				 quote_rfc2047($subject) :
+				 $subject) .
+				"\n";
+		} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+			$initial_reply_to = $1;
+			next;
+		} elsif (/^From:\s*(.+)\s*$/i) {
+			$sender = $1;
+			next;
+		}
+		$summary_empty = 0;
+		print C2 $_;
+	}
+	close(C);
+	close(C2);
+
+	if ($summary_empty) {
+		print "Summary email is empty, skpping it\n";
+		$compose = -1;
+	}
+} elsif ($annotate) {
+	do_edit(@files);
+}
+
 my $prompting = 0;
 if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
@@ -461,17 +560,6 @@ sub expand_aliases {
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
-if (!defined $initial_subject && $compose) {
-	while (1) {
-		$_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
-		last if defined $_;
-		print "\n";
-	}
-
-	$initial_subject = $_;
-	$prompting++;
-}
-
 if ($thread && !defined $initial_reply_to && $prompting) {
 	while (1) {
 		$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
@@ -498,64 +586,6 @@ if (!defined $smtp_server) {
 }
 
 if ($compose) {
-	# Note that this does not need to be secure, but we will make a small
-	# effort to have it be unique
-	open(C,">",$compose_filename)
-		or die "Failed to open for writing $compose_filename: $!";
-	print C "From $sender # This line is ignored.\n";
-	printf C "Subject: %s\n\n", $initial_subject;
-	printf C <<EOT;
-GIT: Please enter your email below.
-GIT: Lines beginning in "GIT: " will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-
-EOT
-	close(C);
-
-	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-
-	if ($annotate) {
-		do_edit($compose_filename, @files);
-	} else {
-		do_edit($compose_filename);
-	}
-
-	open(C2,">",$compose_filename . ".final")
-		or die "Failed to open $compose_filename.final : " . $!;
-
-	open(C,"<",$compose_filename)
-		or die "Failed to open $compose_filename : " . $!;
-
-	my $need_8bit_cte = file_has_nonascii($compose_filename);
-	my $in_body = 0;
-	while(<C>) {
-		next if m/^GIT: /;
-		if (!$in_body && /^\n$/) {
-			$in_body = 1;
-			if ($need_8bit_cte) {
-				print C2 "MIME-Version: 1.0\n",
-					 "Content-Type: text/plain; ",
-					   "charset=utf-8\n",
-					 "Content-Transfer-Encoding: 8bit\n";
-			}
-		}
-		if (!$in_body && /^MIME-Version:/i) {
-			$need_8bit_cte = 0;
-		}
-		if (!$in_body && /^Subject: ?(.*)/i) {
-			my $subject = $1;
-			$_ = "Subject: " .
-				($subject =~ /[^[:ascii:]]/ ?
-				 quote_rfc2047($subject) :
-				 $subject) .
-				"\n";
-		}
-		print C2 $_;
-	}
-	close(C);
-	close(C2);
-
 	while (1) {
 		$_ = $term->readline("Send this email? (y|n) ");
 		last if defined $_;
@@ -567,9 +597,9 @@ EOT
 		exit(0);
 	}
 
-	@files = ($compose_filename . ".final", @files);
-} elsif ($annotate) {
-	do_edit(@files);
+	if ($compose > 0) {
+		@files = ($compose_filename . ".final", @files);
+	}
 }
 
 # Variables we set as part of the loop over files
-- 
1.6.0.3.763.g0275.dirty

[PATCH 3/3] git send-email: turn --compose on when more than one patch.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

Automatically turn --compose on when there is more than one patch, and
that the output is a tty.

Do not print the list of files sent anymore in that case, as the list is
shown in the summary editor.

Signed-off-by: Pierre Habouzit <redacted>
---
 git-send-email.perl |   10 +++++++---
 1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index 5c189a7..5cebb40 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -49,7 +49,7 @@ git send-email [options] <file | directory | rev-list >
     --subject               <str>  * Email "Subject:"
     --in-reply-to           <str>  * Email "In-Reply-To:"
     --annotate                     * Review each patch that will be sent in an editor.
-    --compose                      * Open an editor for introduction.
+    --[no-]compose                 * Open an editor for introduction.
 
   Sending:
     --envelope-sender       <str>  * Email envelope sender.
@@ -237,7 +237,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
 		    "smtp-encryption=s" => \$smtp_encryption,
 		    "identity=s" => \$identity,
 		    "annotate" => \$annotate,
-		    "compose" => \$compose,
+		    "compose!" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
 		    "suppress-from!" => \$suppress_from,
@@ -409,7 +409,11 @@ if ($validate) {
 }
 
 if (@files) {
-	unless ($quiet) {
+	if (!defined($compose) && -t STDOUT) {
+		# turn $compose on if there is more than one file
+		$compose = $#files;
+	}
+	unless ($quiet || $compose) {
 		print $_,"\n" for (@files);
 	}
 } else {
-- 
1.6.0.3.763.g0275.dirty

[PATCH] git send-email: allow any rev-list option as an argument.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

Signed-off-by: Pierre Habouzit <redacted>
---

  One can consider to squash that on top of
  [off-list ref] to be able to pass
  all non path arguments before a possible '--' to git format-patch.

  The downside of this patch is that:

    git send-email -C -C -M origin/next

  will send the content of origin/next if it's an existing file. Of course a
  disambiguation can be:

    git send-email -C -C -M refs/heads/origin/next

  But again if this file also exists, one is basically screwed. I see no
  proper way to fix that, unless to change git-send-email behaviour at once.


  Though I believe this semantics to be better than the one in the previous
  patch, as it's often a good idea to pass -M -C -C to format-patch, which is
  currently impossible. It also allow revision lists to work as expected (wrt
  --all, --not and so on).

  Comments are welcomed.


 Documentation/git-send-email.txt |    2 +-
 git-send-email.perl              |   19 ++++++++++++++-----
 2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 9ee81d5..39d6da9 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails
 
 SYNOPSIS
 --------
-'git send-email' [options] <file|directory|rev-list>...
+'git send-email' [options] <file|directory|rev-list options>...
 
 
 DESCRIPTION
diff --git a/git-send-email.perl b/git-send-email.perl
index 5c189a7..8667e0b 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -25,6 +25,8 @@ use Term::ANSIColor;
 use File::Temp qw/ tempdir /;
 use Git;
 
+Getopt::Long::Configure qw/ pass_through /;
+
 package FakeTerm;
 sub new {
 	my ($class, $reason) = @_;
@@ -39,7 +41,7 @@ package main;
 
 sub usage {
 	print <<EOT;
-git send-email [options] <file | directory | rev-list >
+git send-email [options] <file | directory | rev-list options >
 
   Composing:
     --from                  <str>  * Email From:
@@ -383,8 +385,12 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
 
 # Now that all the defaults are set, process the rest of the command line
 # arguments and collect up the files that need to be processed.
-for my $f (@ARGV) {
-	if (-d $f) {
+my @rev_list_opts;
+while (my $f = pop @ARGV) {
+	if ($f eq "--") {
+		push @rev_list_opts, "--", @ARGV;
+		@ARGV = ();
+	} elsif (-d $f) {
 		opendir(DH,$f)
 			or die "Failed to opendir $f: $!";
 
@@ -394,11 +400,14 @@ for my $f (@ARGV) {
 	} elsif (-f $f or -p $f) {
 		push @files, $f;
 	} else {
-		my $tempdir = tempdir(CLEANUP => 1);
-		push @files, $repo->command('format-patch', '-o', $tempdir, $f);
+		push @rev_list_opts, $f;
 	}
 }
 
+if (@rev_list_opts) {
+	push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
+}
+
 if ($validate) {
 	foreach my $f (@files) {
 		unless (-p $f) {
-- 
1.6.0.3.791.g15769.dirty

Re: [PATCH 2/3] git send-email: do not ask questions when --compose is used.

From: Ian Hilt <hidden>
Date: 2016-06-15 22:45:33

On Fri, Oct 31, 2008 at 01:36:48PM +0100, Pierre Habouzit wrote:
+GIT: Please enter your email below this line.
At first glance I thought this meant to enter my email address here.
So, instead of "email" would "message" be better?  Although on second
glance I realized this is where the body of the message went.  Not sure
if this is worth changing.

	Ian

Re: [PATCH 3/3] git send-email: add --annotate option

From: Ian Hilt <hidden>
Date: 2016-06-15 22:45:33

On Fri, Oct 31, 2008 at 11:57:12AM +0100, Pierre Habouzit wrote:
quoted hunk
This allows to review every patch (and fix various aspects of them, or
comment them) in an editor just before being sent. Combined to the fact
that git send-email can now process revision lists, this makes git
send-email and efficient way to review and send patches interactively.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |   11 +++++++++++
 git-send-email.perl              |   26 ++++++++++++++++++++++++--
 2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index cafff1a..9ee81d5 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -37,6 +37,11 @@ The --bcc option must be repeated for each user you want on the bcc list.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
+--annotate::
+	Review each patch you're about to send in an editor. The setting
+	'sendemail.multiedit' defines if this will spawn one editor per patch
+	or one for all of them at once.
+
 --compose::
 	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
 	introductory message for the patch series.
@@ -204,6 +209,12 @@ sendemail.aliasfiletype::
 	Format of the file(s) specified in sendemail.aliasesfile. Must be
 	one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
+sendemail.multiedit::
+	If true (default), a single editor instance will be spawned to edit
+	files you have to edit (patches when '--annotate' is used, and the
+	summary when '--compose' is used). If false, files will be edited one
+	after the other, spawning a new editor each time.
+
 
 Author
 ------
diff --git a/git-send-email.perl b/git-send-email.perl
index 0d50ee2..65c254d 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -48,6 +48,7 @@ git send-email [options] <file | directory | rev-list >
     --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.
     --compose                      * Open an editor for introduction.
 
   Sending:
@@ -130,7 +131,8 @@ my $compose_filename = ".msg.$$";
 
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
+	$initial_reply_to,$initial_subject,@files,
+	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
 my $envelope_sender;
 
@@ -151,6 +153,17 @@ if ($@) {
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
 
+# Handle interactive edition of files.
s/edition/editing/;

Re: [PATCH 2/3] git send-email: do not ask questions when --compose is used.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:33

On Fri, Oct 31, 2008 at 09:33:38PM +0000, Ian Hilt wrote:
On Fri, Oct 31, 2008 at 01:36:48PM +0100, Pierre Habouzit wrote:
quoted
+GIT: Please enter your email below this line.
At first glance I thought this meant to enter my email address here.
So, instead of "email" would "message" be better?  Although on second
glance I realized this is where the body of the message went.  Not sure
if this is worth changing.
Well, this line sounds kind of awkward actually, so I was even thinking
about removing it.

Decent editors should probably have a plugin to put the cursor here and
be done with it.


In fact what looks odd is the GIT: stuff. a line looking like:

    --- write your message below this line ---

Looks 10x better, though need some code to strip it out if the user kept
it, and I'm lazy, GIT: stuff is automatically removed...


But if that's the only thing that you don't like in the series, I'm
glad, this is quite a minor issue ;)
-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

Re: [PATCH 2/3] git send-email: do not ask questions when --compose is used.

From: Ian Hilt <hidden>
Date: 2016-06-15 22:45:33

On Fri, Oct 31, 2008 at 10:38:03PM +0100, Pierre Habouzit wrote:
On Fri, Oct 31, 2008 at 09:33:38PM +0000, Ian Hilt wrote:
quoted
On Fri, Oct 31, 2008 at 01:36:48PM +0100, Pierre Habouzit wrote:
quoted
+GIT: Please enter your email below this line.
At first glance I thought this meant to enter my email address here.
So, instead of "email" would "message" be better?  Although on second
glance I realized this is where the body of the message went.  Not sure
if this is worth changing.
Well, this line sounds kind of awkward actually, so I was even thinking
about removing it.

Decent editors should probably have a plugin to put the cursor here and
be done with it.


In fact what looks odd is the GIT: stuff. a line looking like:

    --- write your message below this line ---

Looks 10x better, though need some code to strip it out if the user kept
it, and I'm lazy, GIT: stuff is automatically removed...
Or, to follow the convention of git-status and git-commit, you could do
this with "# ".

So something like,

--->8---
From: Ian Hilt <redacted>
Date: Fri, 31 Oct 2008 17:55:46 -0400
Subject: [PATCH] Use a hash instead of GIT: for line removal

Signed-off-by: Ian Hilt <redacted>
---
 git-send-email.perl |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index 5cebb40..c6e21a8 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -427,7 +427,7 @@ sub get_patch_subject($) {
 	while (my $line = <$fh>) {
 		next unless ($line =~ /^Subject: (.*)$/);
 		close $fh;
-		return "GIT: $1\n";
+		return "# $1\n";
 	}
 	close $fh;
 	die "No subject line in $fn ?";
@@ -446,14 +446,14 @@ if ($compose) {
 
 	print C <<EOT;
 From $tpl_sender # This line is ignored.
-GIT: Lines beginning in "GIT: " will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
+# Lines beginning in "# " will be removed.
+# Consider including an overall diffstat or table of contents
+# for the patch you are writing.
 From: $tpl_sender
 Subject: $tpl_subject
 In-Reply-To: $tpl_reply_to
 
-GIT: Please enter your email below this line.
+# --- write your message below this line ---
 
 EOT
 	for my $f (@files) {
@@ -479,7 +479,7 @@ EOT
 	my $in_body = 0;
 	my $summary_empty = 1;
 	while(<C>) {
-		next if m/^GIT: /;
+		next if m/^# /;
 		if ($in_body) {
 		} elsif (/^\n$/) {
 			$in_body = 1;
--->8---
But if that's the only thing that you don't like in the series, I'm
glad, this is quite a minor issue ;)
I've thought something like this would be a good thing.  An editor makes
things easier to fix than the command-line.

	Ian

Re: [PATCH 1/3] git send-email: avoid leaking directory file descriptors.

From: Jeff King <hidden>
Date: 2016-06-15 22:45:34

On Fri, Oct 31, 2008 at 11:57:10AM +0100, Pierre Habouzit wrote:
+		closedir(DH);
Ugh. This is a great reason to use a scoped variable (like "my $dh),
which will close automatically. Once upon a time I think you _had_ to
use globs for this, but I think it has not been the case for some time
(and I think we only support back to perl 5.6 these days). Can any perl
gurus comment?

-Peff

Re: [PATCH] git send-email: allow any rev-list option as an argument.

From: Jeff King <hidden>
Date: 2016-06-15 22:45:34

On Fri, Oct 31, 2008 at 05:52:05PM +0100, Pierre Habouzit wrote:
Signed-off-by: Pierre Habouzit <redacted>
---

  One can consider to squash that on top of
  [off-list ref] to be able to pass
  all non path arguments before a possible '--' to git format-patch.
Personally, I think the other patch is not useful without this. I often
pull out funny subsets of patches if I know it is safe to do so (e.g., I
collect small, unrelated bugfixes directly onto a single branch, but I
send them separately).

With this patch, I might even find send-email usable. :)

-Peff

Re: [PATCH] git send-email: allow any rev-list option as an argument.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:34

On Sun, Nov 02, 2008 at 04:35:23AM +0000, Jeff King wrote:
On Fri, Oct 31, 2008 at 05:52:05PM +0100, Pierre Habouzit wrote:
quoted
Signed-off-by: Pierre Habouzit <redacted>
---

  One can consider to squash that on top of
  [off-list ref] to be able to pass
  all non path arguments before a possible '--' to git format-patch.
Personally, I think the other patch is not useful without this. I often
pull out funny subsets of patches if I know it is safe to do so (e.g., I
collect small, unrelated bugfixes directly onto a single branch, but I
send them separately).

With this patch, I might even find send-email usable. :)
Well it still messes the file/reference name conflict with no way to
prevent it because of the backward compatibility, and even if unlikely
it's still possible.

-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

Re: [PATCH] git send-email: allow any rev-list option as an argument.

From: Jeff King <hidden>
Date: 2016-06-15 22:45:34

On Sun, Nov 02, 2008 at 10:39:07AM +0100, Pierre Habouzit wrote:
Well it still messes the file/reference name conflict with no way to
prevent it because of the backward compatibility, and even if unlikely
it's still possible.
Hmm. As Junio mentioned, this is really an easier way of doing:

  git format-patch -o tmp "$@"
  $EDITOR tmp/*
  git send-email tmp

So I guess a wrapper program would suffice, that just called send-email.
But of course then you would have to think of a new name, and explain
the confusion between it and send-email.

-Peff

Re: [PATCH] git send-email: allow any rev-list option as an argument.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:34

On Sun, Nov 02, 2008 at 06:02:21PM +0000, Jeff King wrote:
On Sun, Nov 02, 2008 at 10:39:07AM +0100, Pierre Habouzit wrote:
quoted
Well it still messes the file/reference name conflict with no way to
prevent it because of the backward compatibility, and even if unlikely
it's still possible.
Hmm. As Junio mentioned, this is really an easier way of doing:

  git format-patch -o tmp "$@"
  $EDITOR tmp/*
  git send-email tmp

So I guess a wrapper program would suffice, that just called send-email.
But of course then you would have to think of a new name, and explain
the confusion between it and send-email.
Well that defeats the purpose of fixing send-email to me. I really would
like to see this fixed properly like it should. I mean it makes sense to
me to use _three_ commands where one should be enough. Not to mention
that introducing a new command is just completely against the spirit of
*simplifying* the current UI ;)

Actually I see a few possibilities.

(1) The first one is to pass a --[no]-format-patch flag to
    git-send-email which says that it should understand arguments as
    format-patch arguments.  You add to that a sendemail.format-patch
    setting that would default to false for backward compatibility sake,
    that would allow the user to force --format-patch as a default.

    This would e.g. cleanly allow:  git send-email --format-patch -3 HEAD.

    I would understand if people dislike the setting: it basically
    modifies the behaviour of a git command a lot, which has been
    frowned upon in the past. Even though I would argue than using
    git-send-email in scripting is quite bad, for something that you can
    probably replace with:

    while read patchname; do mail some@where.org < $patchname; done < git format-patch "$@"

    But if people think it's too dangerous, replacing it with a short
    switch so that it's not too painful to use would fly for me,
    something like -F or whatever.


(2) Another way is to add a --pass-to-format-patch kind of option that
    would take its arguments and pass it to git-format-patch. Like in:
    git send-email --pass-to-format-patch "-3 HEAD". (Of course a short
    switch would help ;p).

(3) Use -- for mandatory separating <format-patch> arguments like this:

	git send-email [send-email options] -- -3 HEAD

    or if you want to send patches that would modify only a given path:

        git send-email [s-e options] -- origin/next.. -- git-gui

    that would run internally:

        git format-patch origin/next.. -- git-gui


I would say that I dislike (2) a LOT because it's a pain to use: needs a
lot of quoting, and it gets worse if you want to pass things with spaces
in it to format-patch.

(2) has the small drawback of not being 100% backward compatible: with
the current use of perl Getoptions, -- is used to stop options
processing, and people _may_ have used it to do `git s-e -- --my.patch`
and such a use would break. However this is highly unlikely to cause
issues in real life I think (unlike the problem of refs against filename
clashes).

In (1) people may dislike the idea of a setting, I've not strong
feelings about it, I won't mind if it gets rejected, a short switch will
do just fine then.


As a summary, I'd say that I like both (1) and (3) because those are
handy, short, and either completely or mostly backward compatible. My
way would be to go down (1) and add a alias.s-e = !git send-email -F in
my .gitconfig.

What do you think ?

-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[take 2] git send-email updates

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:36

  [PATCH 1/5] git send-email: make the message file name more specific.
    self described

  [PATCH 2/5] git send-email: interpret unknown files as revision lists
    All unknown arguments are passed to git-format-patch at once,
    checking for possible file/rev conflicts and dying in that case,
    like Junio suggested.

  [PATCH 3/5] git send-email: add --annotate option
    same as before.

  [PATCH 4/5] git send-email: ask less questions when --compose is used.
    same as before, with an update wrt empty bodies. Still doesn't grok
    To/Cc/Bcc. I would be really glad if a patch to deal with it was
    appended to that series, but a patch that deals with Header
    continuations well.

  [PATCH 5/5] git send-email: turn --compose on when more than one patch.
    This patch is probably controversial. I propose it not because I'm
    lazy, I now have a 'git send' alias for the task that expands to
    'send-email -C -C -M -n --annotate --compose --to'. I propose it
    because I believe it's a good thing to make people write about their
    stuff when there is a series and not a single patch. If they still
    don't want to, they just have to clear the mail buffer at once.

    The drawback is that it _may_ break some scripts, those people would
    have to pass --no-compose to their send-email call to fix the
    scripts.

    I wouldn't complain if the patch gets dropped.

-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[PATCH 1/5] git send-email: make the message file name more specific.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:36

This helps editors choosing their syntax hilighting properly.

Also make the file live under the git directory.

Signed-off-by: Pierre Habouzit <redacted>
---
 git-send-email.perl |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index 94ca5c8..aaace02 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -124,9 +124,6 @@ my $auth;
 sub unique_email_list(@);
 sub cleanup_compose_files();
 
-# Constants (essentially)
-my $compose_filename = ".msg.$$";
-
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
 	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
@@ -149,6 +146,7 @@ if ($@) {
 
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
+my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
-- 
1.5.6.5

[PATCH 5/5] git send-email: turn --compose on when more than one patch.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:36

Automatically turn --compose on when there is more than one patch, and
that the output is a tty.

Do not print the list of files sent anymore in that case, as the list is
shown in the summary editor.

Signed-off-by: Pierre Habouzit <redacted>
---
 git-send-email.perl |   10 +++++++---
 1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index fd72127..3c7818f 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -51,7 +51,7 @@ git send-email [options] <file | directory | rev-list options >
     --subject               <str>  * Email "Subject:"
     --in-reply-to           <str>  * Email "In-Reply-To:"
     --annotate                     * Review each patch that will be sent in an editor.
-    --compose                      * Open an editor for introduction.
+    --[no-]compose                 * Open an editor for introduction.
 
   Sending:
     --envelope-sender       <str>  * Email envelope sender.
@@ -237,7 +237,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
 		    "smtp-encryption=s" => \$smtp_encryption,
 		    "identity=s" => \$identity,
 		    "annotate" => \$annotate,
-		    "compose" => \$compose,
+		    "compose!" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
 		    "suppress-from!" => \$suppress_from,
@@ -425,7 +425,11 @@ if ($validate) {
 }
 
 if (@files) {
-	unless ($quiet) {
+	if (!defined($compose) && -t STDOUT) {
+		# turn $compose on if there is more than one file
+		$compose = $#files;
+	}
+	unless ($quiet || $compose) {
 		print $_,"\n" for (@files);
 	}
 } else {
-- 
1.5.6.5

[PATCH 3/5] git send-email: add --annotate option

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:36

This allows to review every patch (and fix various aspects of them, or
comment them) in an editor just before being sent. Combined to the fact
that git send-email can now process revision lists, this makes git
send-email and efficient way to review and send patches interactively.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |   11 +++++++++++
 git-send-email.perl              |   26 ++++++++++++++++++++++++--
 2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 4654d4f..39d6da9 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -37,6 +37,11 @@ The --bcc option must be repeated for each user you want on the bcc list.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
+--annotate::
+	Review each patch you're about to send in an editor. The setting
+	'sendemail.multiedit' defines if this will spawn one editor per patch
+	or one for all of them at once.
+
 --compose::
 	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
 	introductory message for the patch series.
@@ -204,6 +209,12 @@ sendemail.aliasfiletype::
 	Format of the file(s) specified in sendemail.aliasesfile. Must be
 	one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
+sendemail.multiedit::
+	If true (default), a single editor instance will be spawned to edit
+	files you have to edit (patches when '--annotate' is used, and the
+	summary when '--compose' is used). If false, files will be edited one
+	after the other, spawning a new editor each time.
+
 
 Author
 ------
diff --git a/git-send-email.perl b/git-send-email.perl
index c29868a..d0c5a41 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -50,6 +50,7 @@ git send-email [options] <file | directory | rev-list options >
     --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.
     --compose                      * Open an editor for introduction.
 
   Sending:
@@ -129,7 +130,8 @@ sub cleanup_compose_files();
 
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
+	$initial_reply_to,$initial_subject,@files,
+	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
 my $envelope_sender;
 
@@ -151,6 +153,17 @@ if ($@) {
 my ($quiet, $dry_run) = (0, 0);
 my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
 
+# Handle interactive edition of files.
+my $multiedit;
+my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+sub do_edit {
+	if (defined($multiedit) && !$multiedit) {
+		map { system('sh', '-c', $editor.' "$@"', $editor, $_); } @_;
+	} else {
+		system('sh', '-c', $editor.' "$@"', $editor, @_);
+	}
+}
+
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
@@ -180,6 +193,7 @@ my %config_settings = (
     "aliasesfile" => \@alias_files,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
+    "multiedit" => \$multiedit,
 );
 
 # Handle Uncouth Termination
@@ -222,6 +236,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
 		    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
 		    "smtp-encryption=s" => \$smtp_encryption,
 		    "identity=s" => \$identity,
+		    "annotate" => \$annotate,
 		    "compose" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
@@ -515,7 +530,12 @@ EOT
 	close(C);
 
 	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-	system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
 
 	open(C2,">",$compose_filename . ".final")
 		or die "Failed to open $compose_filename.final : " . $!;
@@ -564,6 +584,8 @@ EOT
 	}
 
 	@files = ($compose_filename . ".final", @files);
+} elsif ($annotate) {
+	do_edit(@files);
 }
 
 # Variables we set as part of the loop over files
-- 
1.5.6.5

[PATCH 4/5] git send-email: ask less questions when --compose is used.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:36

When --compose is used, we can grab the From/Subject/In-Reply-To from the
edited summary, let it be so and don't ask the user silly questions.

The summary templates gets quite revamped, and includes the list of
patches subjects that are going to be sent with this batch.

When having a body full of empty lines, the summary isn't sent. Document
that in the git-send-email manpage fully.

Note: It doesn't deal with To/Cc/Bcc yet.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |    9 ++
 git-send-email.perl              |  177 ++++++++++++++++++++++---------------
 2 files changed, 114 insertions(+), 72 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 39d6da9..e06db6b 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -45,6 +45,15 @@ The --cc option must be repeated for each user you want on the cc list.
 --compose::
 	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
 	introductory message for the patch series.
++
+When compose is in used, git send-email gets less interactive will use the
+values of the headers you set there. If the body of the email (what you type
+after the headers and a blank line) only contains blank (or GIT: prefixed)
+lines, the summary won't be sent, but git-send-email will still use the
+Headers values if you don't removed them.
++
+If it wasn't able to see a header in the summary it will ask you about it
+interactively after quitting your editor.
 
 --from::
 	Specify the sender of the emails.  This will default to
diff --git a/git-send-email.perl b/git-send-email.perl
index d0c5a41..fd72127 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -433,6 +433,108 @@ if (@files) {
 	usage();
 }
 
+sub get_patch_subject($) {
+	my $fn = shift;
+	open (my $fh, '<', $fn);
+	while (my $line = <$fh>) {
+		next unless ($line =~ /^Subject: (.*)$/);
+		close $fh;
+		return "GIT: $1\n";
+	}
+	close $fh;
+	die "No subject line in $fn ?";
+}
+
+if ($compose) {
+	# Note that this does not need to be secure, but we will make a small
+	# effort to have it be unique
+	open(C,">",$compose_filename)
+		or die "Failed to open for writing $compose_filename: $!";
+
+
+	my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
+	my $tpl_subject = $initial_subject || '';
+	my $tpl_reply_to = $initial_reply_to || '';
+
+	print C <<EOT;
+From $tpl_sender # This line is ignored.
+GIT: Lines beginning in "GIT: " will be removed.
+GIT: Consider including an overall diffstat or table of contents
+GIT: for the patch you are writing.
+GIT:
+GIT: Clear the body content if you don't wish to send a summary.
+From: $tpl_sender
+Subject: $tpl_subject
+In-Reply-To: $tpl_reply_to
+
+EOT
+	for my $f (@files) {
+		print C get_patch_subject($f);
+	}
+	close(C);
+
+	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
+
+	open(C2,">",$compose_filename . ".final")
+		or die "Failed to open $compose_filename.final : " . $!;
+
+	open(C,"<",$compose_filename)
+		or die "Failed to open $compose_filename : " . $!;
+
+	my $need_8bit_cte = file_has_nonascii($compose_filename);
+	my $in_body = 0;
+	my $summary_empty = 1;
+	while(<C>) {
+		next if m/^GIT: /;
+		if ($in_body) {
+			$summary_empty = 0 unless (/^\n$/);
+		} elsif (/^\n$/) {
+			$in_body = 1;
+			if ($need_8bit_cte) {
+				print C2 "MIME-Version: 1.0\n",
+					 "Content-Type: text/plain; ",
+					   "charset=utf-8\n",
+					 "Content-Transfer-Encoding: 8bit\n";
+			}
+		} elsif (/^MIME-Version:/i) {
+			$need_8bit_cte = 0;
+		} elsif (/^Subject:\s*(.+)\s*$/i) {
+			$initial_subject = $1;
+			my $subject = $initial_subject;
+			$_ = "Subject: " .
+				($subject =~ /[^[:ascii:]]/ ?
+				 quote_rfc2047($subject) :
+				 $subject) .
+				"\n";
+		} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+			$initial_reply_to = $1;
+			next;
+		} elsif (/^From:\s*(.+)\s*$/i) {
+			$sender = $1;
+			next;
+		} elsif (/^(?:To|Cc|Bcc):/i) {
+			print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
+			next;
+		}
+		print C2 $_;
+	}
+	close(C);
+	close(C2);
+
+	if ($summary_empty) {
+		print "Summary email is empty, skpping it\n";
+		$compose = -1;
+	}
+} elsif ($annotate) {
+	do_edit(@files);
+}
+
 my $prompting = 0;
 if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
@@ -477,17 +579,6 @@ sub expand_aliases {
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
-if (!defined $initial_subject && $compose) {
-	while (1) {
-		$_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
-		last if defined $_;
-		print "\n";
-	}
-
-	$initial_subject = $_;
-	$prompting++;
-}
-
 if ($thread && !defined $initial_reply_to && $prompting) {
 	while (1) {
 		$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
@@ -514,64 +605,6 @@ if (!defined $smtp_server) {
 }
 
 if ($compose) {
-	# Note that this does not need to be secure, but we will make a small
-	# effort to have it be unique
-	open(C,">",$compose_filename)
-		or die "Failed to open for writing $compose_filename: $!";
-	print C "From $sender # This line is ignored.\n";
-	printf C "Subject: %s\n\n", $initial_subject;
-	printf C <<EOT;
-GIT: Please enter your email below.
-GIT: Lines beginning in "GIT: " will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-
-EOT
-	close(C);
-
-	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-
-	if ($annotate) {
-		do_edit($compose_filename, @files);
-	} else {
-		do_edit($compose_filename);
-	}
-
-	open(C2,">",$compose_filename . ".final")
-		or die "Failed to open $compose_filename.final : " . $!;
-
-	open(C,"<",$compose_filename)
-		or die "Failed to open $compose_filename : " . $!;
-
-	my $need_8bit_cte = file_has_nonascii($compose_filename);
-	my $in_body = 0;
-	while(<C>) {
-		next if m/^GIT: /;
-		if (!$in_body && /^\n$/) {
-			$in_body = 1;
-			if ($need_8bit_cte) {
-				print C2 "MIME-Version: 1.0\n",
-					 "Content-Type: text/plain; ",
-					   "charset=utf-8\n",
-					 "Content-Transfer-Encoding: 8bit\n";
-			}
-		}
-		if (!$in_body && /^MIME-Version:/i) {
-			$need_8bit_cte = 0;
-		}
-		if (!$in_body && /^Subject: ?(.*)/i) {
-			my $subject = $1;
-			$_ = "Subject: " .
-				($subject =~ /[^[:ascii:]]/ ?
-				 quote_rfc2047($subject) :
-				 $subject) .
-				"\n";
-		}
-		print C2 $_;
-	}
-	close(C);
-	close(C2);
-
 	while (1) {
 		$_ = $term->readline("Send this email? (y|n) ");
 		last if defined $_;
@@ -583,9 +616,9 @@ EOT
 		exit(0);
 	}
 
-	@files = ($compose_filename . ".final", @files);
-} elsif ($annotate) {
-	do_edit(@files);
+	if ($compose > 0) {
+		@files = ($compose_filename . ".final", @files);
+	}
 }
 
 # Variables we set as part of the loop over files
-- 
1.5.6.5

[PATCH 2/5] git send-email: interpret unknown files as revision lists

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:36

Filter out all the arguments git-send-email doesn't like to a
git format-patch command, that dumps its content to a safe directory.

Barf when a file/revision conflict occurs.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |    2 +-
 git-send-email.perl              |   28 ++++++++++++++++++++++++----
 2 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 82f5056..4654d4f 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails
 
 SYNOPSIS
 --------
-'git send-email' [options] <file|directory> [... file|directory]
+'git send-email' [options] <file|directory|rev-list options>...
 
 
 DESCRIPTION
diff --git a/git-send-email.perl b/git-send-email.perl
index aaace02..c29868a 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -22,8 +22,11 @@ use Term::ReadLine;
 use Getopt::Long;
 use Data::Dumper;
 use Term::ANSIColor;
+use File::Temp qw/ tempdir /;
 use Git;
 
+Getopt::Long::Configure qw/ pass_through /;
+
 package FakeTerm;
 sub new {
 	my ($class, $reason) = @_;
@@ -38,7 +41,7 @@ package main;
 
 sub usage {
 	print <<EOT;
-git send-email [options] <file | directory>...
+git send-email [options] <file | directory | rev-list options >
 
   Composing:
     --from                  <str>  * Email From:
@@ -363,10 +366,22 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
 
 ($sender) = expand_aliases($sender) if defined $sender;
 
+sub check_file_rev_conflict($) {
+	my $f = shift;
+	if ($repo->command('rev-parse', '--verify', '--quiet', $f)) {
+		die("revision/filename conflict on `$f'");
+	}
+}
+
 # Now that all the defaults are set, process the rest of the command line
 # arguments and collect up the files that need to be processed.
-for my $f (@ARGV) {
-	if (-d $f) {
+my @rev_list_opts;
+while (my $f = pop @ARGV) {
+	if ($f eq "--") {
+		push @rev_list_opts, "--", @ARGV;
+		@ARGV = ();
+	} elsif (-d $f) {
+		check_file_rev_conflict($f);
 		opendir(DH,$f)
 			or die "Failed to opendir $f: $!";
 
@@ -374,12 +389,17 @@ for my $f (@ARGV) {
 				sort readdir(DH);
 		closedir(DH);
 	} elsif (-f $f or -p $f) {
+		check_file_rev_conflict($f);
 		push @files, $f;
 	} else {
-		print STDERR "Skipping $f - not found.\n";
+		push @rev_list_opts, $f;
 	}
 }
 
+if (@rev_list_opts) {
+	push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
+}
+
 if ($validate) {
 	foreach my $f (@files) {
 		unless (-p $f) {
-- 
1.5.6.5

Re: [PATCH 4/5] git send-email: ask less questions when --compose is used.

From: Francis Galiegue <hidden>
Date: 2016-06-15 22:45:36

Le Tuesday 04 November 2008 17:24:17, vous avez écrit :
+       if ($summary_empty) {
+               print "Summary email is empty, --> skpping <-- it\n";
Typo...

-- 
fge

Re: [PATCH 2/5] git send-email: interpret unknown files as revision lists

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:45:36

Pierre Habouzit [off-list ref] writes:
quoted hunk
diff --git a/git-send-email.perl b/git-send-email.perl
index aaace02..c29868a 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -22,8 +22,11 @@ use Term::ReadLine;
 use Getopt::Long;
 use Data::Dumper;
 use Term::ANSIColor;
+use File::Temp qw/ tempdir /;
We seem to use File::Temp::tempdir already elsewhere, but they are in
archimport, cvsexportcommit and cvsserver, all of which are rather rarely
used ones.  I think this is Perl 5.6.1 addition.  Is everybody Ok with
this dependency?  Just double checking.
quoted hunk
@@ -363,10 +366,22 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
 
 ($sender) = expand_aliases($sender) if defined $sender;
 
+sub check_file_rev_conflict($) {
+	my $f = shift;
+	if ($repo->command('rev-parse', '--verify', '--quiet', $f)) {
+		die("revision/filename conflict on `$f'");
Perhaps wording this a bit more to the point?  This is triggered when 
'$f' can be both a filename or a revision, so...

	File '$f' exists but it could also be the range of commits
        to produce patches for.  Please disambiguate by...

	* Saying "./$f" if you mean a file; or
        * Giving -F option if you mean a range.

Earlier I suggested that "origin^0" is a way for the user to disambiguate
favouring a rev, but such a filename can exist, so we cannot blindly
suggest to say "$f^0" here.  I think adding -F (or --format-patch) option
to send-email to explicitly disable file/directory interpretation would be
a cleaner solution for this (and it would allow you to drive this from a
script without worrying about what garbage files you happen to have in the
working tree).

Re: [PATCH 5/5] git send-email: turn --compose on when more than one patch.

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:45:36

Pierre Habouzit [off-list ref] writes:
Automatically turn --compose on when there is more than one patch, and
that the output is a tty.
I do not think this is a good idea.  I suspect I am not the only person
who uses "format-patch --cover-letter", edit the files to review and
prepare, and runs send-email to fire them off.

Re: [PATCH 4/5] git send-email: ask less questions when --compose is used.

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:45:36

Pierre Habouzit [off-list ref] writes:
+	print C <<EOT;
+From $tpl_sender # This line is ignored.
+GIT: Lines beginning in "GIT: " will be removed.
+GIT: Consider including an overall diffstat or table of contents
+GIT: for the patch you are writing.
+GIT:
+GIT: Clear the body content if you don't wish to send a summary.
+From: $tpl_sender
+Subject: $tpl_subject
+In-Reply-To: $tpl_reply_to
+
Somebody already suggested this but I really think GIT: lines should be at
the end and use '# ' prefix instead.

With the ability to give --cover-letter option to underlying format-patch
do you still need this?
+	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
Don't we want to abort the whole process when the user kills the editor
instead of normal exit (iow, do_edit() which is system() reports that the
editor was killed)?

Re: [PATCH 5/5] git send-email: turn --compose on when more than one patch.

From: Jeff King <hidden>
Date: 2016-06-15 22:45:36

On Tue, Nov 04, 2008 at 03:54:54PM -0800, Junio C Hamano wrote:
Pierre Habouzit [off-list ref] writes:
quoted
Automatically turn --compose on when there is more than one patch, and
that the output is a tty.
I do not think this is a good idea.  I suspect I am not the only person
who uses "format-patch --cover-letter", edit the files to review and
prepare, and runs send-email to fire them off.
Maybe a config option to turn this behavior on? It seems specific to
different workflows (i.e., whether or not you are using "git send-email
$REVS" or using format-patch first).

-Peff

[take 2] git send-email updates

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:38

The last patch is dropped for now (the automatic --compose stuff)
because I'm not sure which option to add, and that I don't care enough
about it to spend more time on it.

I think I've incorporated most of the stuff people asked about in this
series.

 [PATCH 1/4] git send-email: make the message file name more specific.
 [PATCH 2/4] git send-email: interpret unknown files as revision lists
 [PATCH 3/4] git send-email: add --annotate option
 [PATCH 4/4] git send-email: ask less questions when --compose is used.

[PATCH 4/4] git send-email: ask less questions when --compose is used.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:38

When --compose is used, we can grab the From/Subject/In-Reply-To from the
edited summary, let it be so and don't ask the user silly questions.

The summary templates gets quite revamped, and includes the list of
patches subjects that are going to be sent with this batch.

When having a body full of empty lines, the summary isn't sent. Document
that in the git-send-email manpage fully.

Note: It doesn't deal with To/Cc/Bcc yet.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |    9 ++
 git-send-email.perl              |  187 +++++++++++++++++++++++---------------
 2 files changed, 123 insertions(+), 73 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 66d5f4c..acf8bf4 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -45,6 +45,15 @@ The --cc option must be repeated for each user you want on the cc list.
 --compose::
 	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
 	introductory message for the patch series.
++
+When compose is in used, git send-email gets less interactive will use the
+values of the headers you set there. If the body of the email (what you type
+after the headers and a blank line) only contains blank (or GIT: prefixed)
+lines, the summary won't be sent, but git-send-email will still use the
+Headers values if you don't removed them.
++
+If it wasn't able to see a header in the summary it will ask you about it
+interactively after quitting your editor.
 
 --from::
 	Specify the sender of the emails.  This will default to
diff --git a/git-send-email.perl b/git-send-email.perl
index ccb3b18..9039cfd 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -162,9 +162,17 @@ my $multiedit;
 my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
 sub do_edit {
 	if (defined($multiedit) && !$multiedit) {
-		map { system('sh', '-c', $editor.' "$@"', $editor, $_); } @_;
+		map {
+			system('sh', '-c', $editor.' "$@"', $editor, $_);
+			if (($? & 127) || ($? >> 8)) {
+				die("the editor exited uncleanly, aborting everything");
+			}
+		} @_;
 	} else {
 		system('sh', '-c', $editor.' "$@"', $editor, @_);
+		if (($? & 127) || ($? >> 8)) {
+			die("the editor exited uncleanly, aborting everything");
+		}
 	}
 }
 
@@ -450,6 +458,108 @@ if (@files) {
 	usage();
 }
 
+sub get_patch_subject($) {
+	my $fn = shift;
+	open (my $fh, '<', $fn);
+	while (my $line = <$fh>) {
+		next unless ($line =~ /^Subject: (.*)$/);
+		close $fh;
+		return "GIT: $1\n";
+	}
+	close $fh;
+	die "No subject line in $fn ?";
+}
+
+if ($compose) {
+	# Note that this does not need to be secure, but we will make a small
+	# effort to have it be unique
+	open(C,">",$compose_filename)
+		or die "Failed to open for writing $compose_filename: $!";
+
+
+	my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
+	my $tpl_subject = $initial_subject || '';
+	my $tpl_reply_to = $initial_reply_to || '';
+
+	print C <<EOT;
+From $tpl_sender # This line is ignored.
+GIT: Lines beginning in "GIT: " will be removed.
+GIT: Consider including an overall diffstat or table of contents
+GIT: for the patch you are writing.
+GIT:
+GIT: Clear the body content if you don't wish to send a summary.
+From: $tpl_sender
+Subject: $tpl_subject
+In-Reply-To: $tpl_reply_to
+
+EOT
+	for my $f (@files) {
+		print C get_patch_subject($f);
+	}
+	close(C);
+
+	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
+
+	open(C2,">",$compose_filename . ".final")
+		or die "Failed to open $compose_filename.final : " . $!;
+
+	open(C,"<",$compose_filename)
+		or die "Failed to open $compose_filename : " . $!;
+
+	my $need_8bit_cte = file_has_nonascii($compose_filename);
+	my $in_body = 0;
+	my $summary_empty = 1;
+	while(<C>) {
+		next if m/^GIT: /;
+		if ($in_body) {
+			$summary_empty = 0 unless (/^\n$/);
+		} elsif (/^\n$/) {
+			$in_body = 1;
+			if ($need_8bit_cte) {
+				print C2 "MIME-Version: 1.0\n",
+					 "Content-Type: text/plain; ",
+					   "charset=utf-8\n",
+					 "Content-Transfer-Encoding: 8bit\n";
+			}
+		} elsif (/^MIME-Version:/i) {
+			$need_8bit_cte = 0;
+		} elsif (/^Subject:\s*(.+)\s*$/i) {
+			$initial_subject = $1;
+			my $subject = $initial_subject;
+			$_ = "Subject: " .
+				($subject =~ /[^[:ascii:]]/ ?
+				 quote_rfc2047($subject) :
+				 $subject) .
+				"\n";
+		} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
+			$initial_reply_to = $1;
+			next;
+		} elsif (/^From:\s*(.+)\s*$/i) {
+			$sender = $1;
+			next;
+		} elsif (/^(?:To|Cc|Bcc):/i) {
+			print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
+			next;
+		}
+		print C2 $_;
+	}
+	close(C);
+	close(C2);
+
+	if ($summary_empty) {
+		print "Summary email is empty, skipping it\n";
+		$compose = -1;
+	}
+} elsif ($annotate) {
+	do_edit(@files);
+}
+
 my $prompting = 0;
 if (!defined $sender) {
 	$sender = $repoauthor || $repocommitter || '';
@@ -494,17 +604,6 @@ sub expand_aliases {
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
-if (!defined $initial_subject && $compose) {
-	while (1) {
-		$_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
-		last if defined $_;
-		print "\n";
-	}
-
-	$initial_subject = $_;
-	$prompting++;
-}
-
 if ($thread && !defined $initial_reply_to && $prompting) {
 	while (1) {
 		$_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
@@ -531,64 +630,6 @@ if (!defined $smtp_server) {
 }
 
 if ($compose) {
-	# Note that this does not need to be secure, but we will make a small
-	# effort to have it be unique
-	open(C,">",$compose_filename)
-		or die "Failed to open for writing $compose_filename: $!";
-	print C "From $sender # This line is ignored.\n";
-	printf C "Subject: %s\n\n", $initial_subject;
-	printf C <<EOT;
-GIT: Please enter your email below.
-GIT: Lines beginning in "GIT: " will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-
-EOT
-	close(C);
-
-	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-
-	if ($annotate) {
-		do_edit($compose_filename, @files);
-	} else {
-		do_edit($compose_filename);
-	}
-
-	open(C2,">",$compose_filename . ".final")
-		or die "Failed to open $compose_filename.final : " . $!;
-
-	open(C,"<",$compose_filename)
-		or die "Failed to open $compose_filename : " . $!;
-
-	my $need_8bit_cte = file_has_nonascii($compose_filename);
-	my $in_body = 0;
-	while(<C>) {
-		next if m/^GIT: /;
-		if (!$in_body && /^\n$/) {
-			$in_body = 1;
-			if ($need_8bit_cte) {
-				print C2 "MIME-Version: 1.0\n",
-					 "Content-Type: text/plain; ",
-					   "charset=utf-8\n",
-					 "Content-Transfer-Encoding: 8bit\n";
-			}
-		}
-		if (!$in_body && /^MIME-Version:/i) {
-			$need_8bit_cte = 0;
-		}
-		if (!$in_body && /^Subject: ?(.*)/i) {
-			my $subject = $1;
-			$_ = "Subject: " .
-				($subject =~ /[^[:ascii:]]/ ?
-				 quote_rfc2047($subject) :
-				 $subject) .
-				"\n";
-		}
-		print C2 $_;
-	}
-	close(C);
-	close(C2);
-
 	while (1) {
 		$_ = $term->readline("Send this email? (y|n) ");
 		last if defined $_;
@@ -600,9 +641,9 @@ EOT
 		exit(0);
 	}
 
-	@files = ($compose_filename . ".final", @files);
-} elsif ($annotate) {
-	do_edit(@files);
+	if ($compose > 0) {
+		@files = ($compose_filename . ".final", @files);
+	}
 }
 
 # Variables we set as part of the loop over files
-- 
1.6.0.4.859.g7ecd.dirty

[PATCH 1/4] git send-email: make the message file name more specific.

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:38

This helps editors choosing their syntax hilighting properly.

Also make the file live under the git directory.

Signed-off-by: Pierre Habouzit <redacted>
---
 git-send-email.perl |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index 94ca5c8..aaace02 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -124,9 +124,6 @@ my $auth;
 sub unique_email_list(@);
 sub cleanup_compose_files();
 
-# Constants (essentially)
-my $compose_filename = ".msg.$$";
-
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
 	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
@@ -149,6 +146,7 @@ if ($@) {
 
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
+my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
-- 
1.6.0.4.859.g7ecd.dirty

[PATCH 3/4] git send-email: add --annotate option

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:38

This allows to review every patch (and fix various aspects of them, or
comment them) in an editor just before being sent. Combined to the fact
that git send-email can now process revision lists, this makes git
send-email and efficient way to review and send patches interactively.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |   11 +++++++++++
 git-send-email.perl              |   26 ++++++++++++++++++++++++--
 2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 0beaad4..66d5f4c 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -37,6 +37,11 @@ The --bcc option must be repeated for each user you want on the bcc list.
 +
 The --cc option must be repeated for each user you want on the cc list.
 
+--annotate::
+	Review each patch you're about to send in an editor. The setting
+	'sendemail.multiedit' defines if this will spawn one editor per patch
+	or one for all of them at once.
+
 --compose::
 	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
 	introductory message for the patch series.
@@ -210,6 +215,12 @@ sendemail.aliasfiletype::
 	Format of the file(s) specified in sendemail.aliasesfile. Must be
 	one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
+sendemail.multiedit::
+	If true (default), a single editor instance will be spawned to edit
+	files you have to edit (patches when '--annotate' is used, and the
+	summary when '--compose' is used). If false, files will be edited one
+	after the other, spawning a new editor each time.
+
 
 Author
 ------
diff --git a/git-send-email.perl b/git-send-email.perl
index 6f5a613..ccb3b18 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -51,6 +51,7 @@ git send-email [options] <file | directory | rev-list options >
     --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.
     --compose                      * Open an editor for introduction.
 
   Sending:
@@ -132,7 +133,8 @@ sub cleanup_compose_files();
 
 # Variables we fill in automatically, or via prompting:
 my (@to,@cc,@initial_cc,@bcclist,@xh,
-	$initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
+	$initial_reply_to,$initial_subject,@files,
+	$author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
 my $envelope_sender;
 
@@ -155,6 +157,17 @@ my ($quiet, $dry_run) = (0, 0);
 my $format_patch;
 my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
 
+# Handle interactive edition of files.
+my $multiedit;
+my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
+sub do_edit {
+	if (defined($multiedit) && !$multiedit) {
+		map { system('sh', '-c', $editor.' "$@"', $editor, $_); } @_;
+	} else {
+		system('sh', '-c', $editor.' "$@"', $editor, @_);
+	}
+}
+
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
@@ -184,6 +197,7 @@ my %config_settings = (
     "aliasesfile" => \@alias_files,
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
+    "multiedit" => \$multiedit,
 );
 
 # Handle Uncouth Termination
@@ -226,6 +240,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
 		    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
 		    "smtp-encryption=s" => \$smtp_encryption,
 		    "identity=s" => \$identity,
+		    "annotate" => \$annotate,
 		    "compose" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
@@ -532,7 +547,12 @@ EOT
 	close(C);
 
 	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-	system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
+
+	if ($annotate) {
+		do_edit($compose_filename, @files);
+	} else {
+		do_edit($compose_filename);
+	}
 
 	open(C2,">",$compose_filename . ".final")
 		or die "Failed to open $compose_filename.final : " . $!;
@@ -581,6 +601,8 @@ EOT
 	}
 
 	@files = ($compose_filename . ".final", @files);
+} elsif ($annotate) {
+	do_edit(@files);
 }
 
 # Variables we set as part of the loop over files
-- 
1.6.0.4.859.g7ecd.dirty

[PATCH 2/4] git send-email: interpret unknown files as revision lists

From: Pierre Habouzit <hidden>
Date: 2016-06-15 22:45:38

Filter out all the arguments git-send-email doesn't like to a
git format-patch command, that dumps its content to a safe directory.

Barf when a file/revision conflict occurs, allow it to be overriden
--[no-]format-patch.

Signed-off-by: Pierre Habouzit <redacted>
---
 Documentation/git-send-email.txt |    8 +++++-
 git-send-email.perl              |   47 +++++++++++++++++++++++++++++++++----
 t/t9001-send-email.sh            |    8 ++++++
 3 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 82f5056..0beaad4 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -8,7 +8,7 @@ git-send-email - Send a collection of patches as emails
 
 SYNOPSIS
 --------
-'git send-email' [options] <file|directory> [... file|directory]
+'git send-email' [options] <file|directory|rev-list options>...
 
 
 DESCRIPTION
@@ -183,6 +183,12 @@ Administering
 --[no-]validate::
 	Perform sanity checks on patches.
 	Currently, validation means the following:
+
+--[no-]format-patch::
+	When an argument may be understood either as a reference or as a file name,
+	choose to understand it as a format-patch argument ('--format-patch')
+	or as a file name ('--no-format-patch'). By default, when such a conflict
+	occurs, git send-email will fail.
 +
 --
 		*	Warn of patches that contain lines longer than 998 characters; this
diff --git a/git-send-email.perl b/git-send-email.perl
index aaace02..6f5a613 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -22,8 +22,12 @@ use Term::ReadLine;
 use Getopt::Long;
 use Data::Dumper;
 use Term::ANSIColor;
+use File::Temp qw/ tempdir /;
+use Error qw(:try);
 use Git;
 
+Getopt::Long::Configure qw/ pass_through /;
+
 package FakeTerm;
 sub new {
 	my ($class, $reason) = @_;
@@ -38,7 +42,7 @@ package main;
 
 sub usage {
 	print <<EOT;
-git send-email [options] <file | directory>...
+git send-email [options] <file | directory | rev-list options >
 
   Composing:
     --from                  <str>  * Email From:
@@ -73,6 +77,8 @@ git send-email [options] <file | directory>...
     --quiet                        * Output one line of info per email.
     --dry-run                      * Don't actually send the emails.
     --[no-]validate                * Perform patch sanity checks. Default on.
+    --[no-]format-patch            * understand any non optional arguments as
+                                     `git format-patch` ones.
 
 EOT
 	exit(1);
@@ -146,6 +152,7 @@ if ($@) {
 
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
+my $format_patch;
 my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
 
 # Variables with corresponding config settings
@@ -229,6 +236,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
 		    "envelope-sender=s" => \$envelope_sender,
 		    "thread!" => \$thread,
 		    "validate!" => \$validate,
+		    "format-patch!" => \$format_patch,
 	 );
 
 unless ($rc) {
@@ -363,23 +371,52 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
 
 ($sender) = expand_aliases($sender) if defined $sender;
 
+# returns 1 if the conflict must be solved using it as a format-patch argument
+sub check_file_rev_conflict($) {
+	my $f = shift;
+	try {
+		$repo->command('rev-parse', '--verify', '--quiet', $f);
+		if (defined($format_patch)) {
+			print "foo\n";
+			return $format_patch;
+		}
+		die(<<EOF);
+File '$f' exists but it could also be the range of commits
+to produce patches for.  Please disambiguate by...
+
+    * Saying "./$f" if you mean a file; or
+    * Giving --format-patch option if you mean a range.
+EOF
+	} catch Git::Error::Command with {
+		return 0;
+	}
+}
+
 # Now that all the defaults are set, process the rest of the command line
 # arguments and collect up the files that need to be processed.
-for my $f (@ARGV) {
-	if (-d $f) {
+my @rev_list_opts;
+while (my $f = pop @ARGV) {
+	if ($f eq "--") {
+		push @rev_list_opts, "--", @ARGV;
+		@ARGV = ();
+	} elsif (-d $f and !check_file_rev_conflict($f)) {
 		opendir(DH,$f)
 			or die "Failed to opendir $f: $!";
 
 		push @files, grep { -f $_ } map { +$f . "/" . $_ }
 				sort readdir(DH);
 		closedir(DH);
-	} elsif (-f $f or -p $f) {
+	} elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
 		push @files, $f;
 	} else {
-		print STDERR "Skipping $f - not found.\n";
+		push @rev_list_opts, $f;
 	}
 }
 
+if (@rev_list_opts) {
+	push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
+}
+
 if ($validate) {
 	foreach my $f (@files) {
 		unless (-p $f) {
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 561ae7d..b4bddd1 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -292,4 +292,12 @@ test_expect_success '--compose adds MIME for utf8 subject' '
 	grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
+test_expect_success 'detects ambiguous reference/file conflict' '
+	echo master > master &&
+	git add master &&
+	git commit -m"add master" &&
+	test_must_fail git send-email --dry-run master > errors &&
+	grep disambiguate errors
+'
+
 test_done
-- 
1.6.0.4.859.g7ecd.dirty
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help