From caf0c3d692a5a4639e48499711271cb279ecd0dc Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:53:59 +0100 Subject: git send-email: make the message file name more specific. This helps editors choosing their syntax hilighting properly. Also make the file live under the git directory. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- git-send-email.perl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 94ca5c89a..aaace02fa 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); -- cgit v1.2.1 From 5df9fcf695a0ba85abfeed68efb3b1c5890068d6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:54:00 +0100 Subject: git send-email: interpret unknown files as revision lists 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 Signed-off-by: Junio C Hamano --- 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 82f505686..0beaad45b 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] +'git send-email' [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 aaace02fa..6f5a61389 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 <... +git send-email [options] Composing: --from * Email From: @@ -73,6 +77,8 @@ git send-email [options] ... --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(<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 561ae7d0a..617e97d96 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 2>errors && + grep disambiguate errors +' + test_done -- cgit v1.2.1 From 8fd5bb7f44b7192a564ebe4f9db376af9fe148ec Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:54:01 +0100 Subject: git send-email: add --annotate option 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 Signed-off-by: Junio C Hamano --- 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 0beaad45b..66d5f4cdd 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 6f5a61389..ccb3b1816 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -51,6 +51,7 @@ git send-email [options] --bcc * Email Bcc: --subject * Email "Subject:" --in-reply-to * 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 -- cgit v1.2.1 From beece9dab8e26c98062351536ce0d871a066790e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 11 Nov 2008 00:54:02 +0100 Subject: git send-email: ask less questions when --compose is used. 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 Signed-off-by: Junio C Hamano --- 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 66d5f4cdd..acf8bf41d 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 ccb3b1816..9039cfde0 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 <",$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() { + 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 <",$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() { - 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 -- cgit v1.2.1