git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michael Witten <mfwitten@gmail.com>
To: git@vger.kernel.org
Subject: [PATCH RFC 10/10] send-email: --compose takes optional argument to existing file
Date: Sat, 11 Apr 2009 14:08:28 -0500	[thread overview]
Message-ID: <1239476908-25944-10-git-send-email-mfwitten@gmail.com> (raw)
In-Reply-To: <1239476908-25944-9-git-send-email-mfwitten@gmail.com>

Now, a user may specify an existing (in-progress) file to use as
the introductory/summary email.

The file is opened for any additional editing as usual, but it
is not deleted upon normal termination.

There are also a number of fixes to how the internals and
temporaries are handled.

Signed-off-by: Michael Witten <mfwitten@gmail.com>
---
 Documentation/git-send-email.txt |   39 ++++++--
 git-send-email.perl              |  190 ++++++++++++++++++++++++--------------
 2 files changed, 151 insertions(+), 78 deletions(-)

diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 07c831e..a2af3e5 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -60,17 +60,38 @@ 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.
 
---compose::
-	Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
-	introductory message for the patch series.
+--compose[=<path>]::
+	Use `$GIT_EDITOR`, 'core.editor', `$VISUAL`, or `$EDITOR`, or
+	`vi` to edit an introductory message for the patch series. A
+	path for the intermediate composition may be given. If the path
+	doesn't exist, a file with default contents is created at the
+	path and opened for editing. If the path does exist, then the
+	file at that path is opened for editing as-is. If no path is
+	specified, a new temporary file is created with some default
+	contents. Upon the successful completion of send-email, all
+	temporary files are automatically unlinked (deleted). However,
+	if send-email is terminated by a trappable signal, then this
+	temporary file is not unlinked, and the user is informed of its
+	path.
 +
-When '--compose' is used, git send-email will use the From, Subject, and
-In-Reply-To headers specified in the message. If the body of the message
-(what you type after the headers and a blank line) only contains blank
-(or GIT: prefixed) lines the summary won't be sent, but From, Subject,
-and In-Reply-To headers will be used unless they are removed.
+The user actually composes what will become a valid email message;
+therefore, the message must have the following form (as described in
+`RFC 822`):
 +
-Missing From or In-Reply-To headers will be prompted for.
+	<headers>
+	<blank line>
+	<body>
++
+In particular, `<headers>` must contain the "`Subject`" header. Once the
+user saves the message and quits the editor, this intermediate message is
+transformed into the final email message by removing all lines that begin
+with "`GIT:`". If the `<body>` of the final version is empty, then the
+email is not sent, and send-email proceeds as if '--compose' had never been
+used.
++
+The "`From`", "`Subject`", and "`In-Reply-To`" headers are taken directly
+from the message; missing "`From`" or "`In-Reply-To`" headers will be
+prompted for.
 
 --from=<address>::
 	Specify the sender of the emails.  This will default to
diff --git a/git-send-email.perl b/git-send-email.perl
index e771720..877732f 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -63,10 +63,9 @@ git send-email [options] <file | directory | rev-list options >
     --bcc               <address>  * Email Bcc:
     --subject            <string>  * Email "Subject:"
     --in-reply-to    <message-id>  * Email "In-Reply-To:"; include '<' and '>'.
+    --compose             [<path>] * Open an editor for introduction.
     --annotate                     * Review each patch that will be sent in
                                      an editor.
-    --compose                      * Open an editor for introduction.
-
   Sending:
     --envelope-sender   <address>  * Email envelope sender.
     --smtp-server          <host>  * Outgoing SMTP server to use.
@@ -173,7 +172,7 @@ if ($@) {
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
 my $format_patch;
-my $compose_filename;
+my ($compose_filename, $compose_final_filename);
 
 # Handle interactive edition of files.
 my $multiedit;
@@ -239,16 +238,15 @@ sub signal_handler {
 	system "stty echo";
 
 	# tmp files from --compose
-	if (defined $compose_filename) {
-		if (-e $compose_filename) {
-			print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
-		}
-		if (-e ($compose_filename . ".final")) {
-			print "'$compose_filename.final' contains the composed email.\n"
-		}
+	if (defined $compose_filename and -f $compose_filename) {
+		print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
+	}
+
+	if (defined $compose_final_filename) {
+		unlink $compose_final_filename if defined $compose_final_filename;
 	}
 
-	exit;
+	exit 1;
 };
 
 $SIG{TERM} = \&signal_handler;
@@ -273,8 +271,8 @@ my $rc = GetOptions(
 	"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
 	"smtp-encryption=s" => \$smtp_encryption,
 	"identity=s" => \$identity,
+	"compose:s" => \$compose,
 	"annotate" => \$annotate,
-	"compose" => \$compose,
 	"quiet" => \$quiet,
 	"cc-cmd=s" => \$cc_cmd,
 	"suppress-from!" => \$suppress_from,
@@ -596,36 +594,69 @@ sub get_patch_subject($) {
 	die "'Subject:' line expected in '$patch'";
 }
 
-if ($compose) {
-	# Note that this does not need to be secure, but we will make a small
-	# effort to have it be unique
-	$compose_filename = ($repo ?
-		tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
-		tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
-	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
+if (defined $compose) {{
 
-EOT
-	for my $f (@files) {
-		print C "GIT: ", get_patch_subject($f), "\n";
+	# Some parameters for creating temporary files:
+
+	my $template = ".gitsendemail.msg.XXXXXX";
+	my @dir = (DIR => ($repo ? $repo->repo_path() : "."));
+	my @suffix = (SUFFIX => ".final");
+
+	# Figure out the file that the user should be editing:
+
+	my $compose_file;
+	my $needs_default_contents;
+
+	if ($compose eq '') { # if no path was given
+		$needs_default_contents = 1;
+		($compose_file, $compose_filename) = tempfile($template, @dir)
+			or die "--compose: Could not create temporary file for the user to edit: $!\n";
+	} else {
+		$compose_filename = $compose;
+		$needs_default_contents = not -f $compose_filename;
 	}
-	close(C);
+
+	# Fill in default contents if necessary:
+
+	if ($needs_default_contents) {
+
+		open $compose_file, ">", $compose_filename;
+
+		# For convenience:
+
+		local *STDOUT = $compose_file;
+
+		# Help the user out with some instruction and initial headers:
+
+		my $from = $sender || $repoauthor || $repocommitter || '';
+		my $subject = $initial_subject || '';
+		my $reply_to = $initial_reply_to || '';
+
+		print "From $from # This line is ignored.\n";
+		print "GIT:\n";
+		print "GIT: Lines beginning in 'GIT:' will be removed.\n";
+		print "GIT:\n";
+		print "GIT: Consider including an overall diffstat\n";
+		print "GIT: (git diff --stat) or table of contents\n";
+		print "GIT: (as provide below).\n";
+		print "GIT:\n";
+		print "GIT: Clear the body content if you decide not\n";
+		print "GIT: to send this message.\n";
+		print "GIT:\n";
+		print "GIT: Here are the <headers>:\n";
+		print "From: $from\n";
+		print "Subject: $subject\n";
+		print "In-Reply-To: $reply_to\n";
+		print "\n";
+		print "GIT: This is the first line of the <body>:\n";
+		print "\n";
+
+		for my $f (@files) {
+			print "GIT: ", get_patch_subject($f), "\n";
+		}
+	}
+
+	# Do the editing:
 
 	my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
 
@@ -635,23 +666,40 @@ EOT
 		do_edit($compose_filename);
 	}
 
-	open(C2,">",$compose_filename . ".final")
-		or die "Failed to open $compose_filename.final : " . $!;
+	# Now transform the user-edited introduction into something
+	# suitable for sending via email; the user's editor may have
+	# unlinked the original file and replaced it with an entirely
+	# new one. If this be the case, then it wouldn't do just to seek
+	# to the beginning and start reading, because then only the
+	# original content would be retrieved. Consequently, the file
+	# must be reopened to be safe (note, the original filehandle is
+	# closed automatically):
+
+	unless (-f $compose_filename) {
+		print "--compose: File '$compose_filename' doesn't exist; not sending.\n";
+		last;
+	}
+
+	open $compose_file, "<", $compose_filename
+		or die "--compose: Failed to open '$compose_filename' for reading: $!";
 
-	open(C,"<",$compose_filename)
-		or die "Failed to open $compose_filename : " . $!;
+	# Create the final version:
 
+	(my $compose_final_file, $compose_final_filename) = tempfile($template, @dir, @suffix)
+		or die "--compose: Could not create temporary file for final version: $!\n";
+
+	my ($subject, $reply_to, $from);
 	my $need_8bit_cte = file_has_nonascii($compose_filename);
 	my $in_body = 0;
 	my $summary_empty = 1;
-	while(<C>) {
+	while(<$compose_file>) {
 		next if m/^GIT:/;
 		if ($in_body) {
 			$summary_empty = 0 unless (/^\n$/);
 		} elsif (/^\n$/) {
 			$in_body = 1;
 			if ($need_8bit_cte) {
-				print C2
+				print $compose_final_file
 					"MIME-Version: 1.0\n",
 					"Content-Type: text/plain; ",
 					"charset=utf-8\n",
@@ -660,33 +708,31 @@ EOT
 		} 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";
+			$subject = $1;
+			next;
 		} elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
-			$initial_reply_to = $1;
+			$reply_to = $1;
 			next;
 		} elsif (/^From:\s*(.+)\s*$/i) {
-			$sender = $1;
+			$from = $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;
+		print $compose_final_file $_;
 	}
-} elsif ($annotate) {
+
+	print("Summary email is empty, skipping it.\n"), last if ($summary_empty);
+
+	$initial_subject = $subject;
+	$initial_reply_to = $reply_to;
+	$sender = $from;
+
+	unshift(@files, $compose_final_filename);
+
+}} elsif ($annotate) {
 	do_edit(@files);
 }
 
@@ -776,10 +822,6 @@ if (defined $smtp_server and $smtp_server ne '') {
 	$smtp_server ||= '127.0.0.1';
 }
 
-if ($compose && $compose > 0) {
-	@files = ($compose_filename . ".final", @files);
-}
-
 # Variables we set as part of the loop over files
 our ($message_id, %mail, $subject, $reply_to, $references, $message,
 	$needs_confirm, $message_num, $ask_default);
@@ -924,9 +966,12 @@ sub send_message
 	my $sanitized_sender = sanitize_address($sender);
 	make_message_id() unless defined($message_id);
 
+	my $has_non_ascii = ($subject =~ /[^[:ascii:]]/);
+	my $sanitized_subject = $has_non_ascii ? quote_rfc2047($subject) : $subject;
+
 	my $header = "From: $sanitized_sender
 To: $to${ccline}
-Subject: $subject
+Subject: $sanitized_subject
 Date: $date
 Message-Id: $message_id
 X-Mailer: git-send-email $gitversion
@@ -1286,7 +1331,14 @@ for (my $index = 0; $index < @files; $index++) {
 cleanup_compose_files();
 
 sub cleanup_compose_files() {
-	unlink($compose_filename, $compose_filename . ".final") if $compose;
+
+	if (defined $compose_final_filename) {
+		unlink $compose_final_filename;
+	}
+
+	if (defined $compose_filename and not $compose) {
+		unlink $compose_filename;
+	}
 }
 
 $smtp->quit if $smtp;
-- 
1.6.2.2.479.g2aec

  reply	other threads:[~2009-04-11 19:20 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-04-11 19:08 [PATCH RFC 01/10] Docs: send-email: Put options back into alphabetical order Michael Witten
2009-04-11 19:08 ` [PATCH RFC 02/10] Docs: send-email: Remove superfluous information in CONFIGURATION Michael Witten
2009-04-11 19:08   ` [PATCH RFC 03/10] send-email: Cleanup the usage text and docs a bit Michael Witten
2009-04-11 19:08     ` [PATCH RFC 04/10] send-email: --smtp-server-port should take an integer Michael Witten
2009-04-11 19:08       ` [PATCH RFC 05/10] send-email: Handle "GIT:" rather than "GIT: " during --compose Michael Witten
2009-04-11 19:08         ` [PATCH RFC 06/10] send-email: References: should only reference what is actually sent Michael Witten
2009-04-11 19:08           ` [PATCH RFC 07/10] send-email: Remove horrible mix of tabs and spaces Michael Witten
2009-04-11 19:08             ` [PATCH RFC 08/10] send-email: Add --sleep for email throttling Michael Witten
2009-04-11 19:08               ` [PATCH RFC 09/10] send-email: Minor cleanup of $smtp_server usage and send_message() Michael Witten
2009-04-11 19:08                 ` Michael Witten [this message]
2009-04-11 19:25                   ` [PATCH RFC 10/10] send-email: --compose takes optional argument to existing file Sverre Rabbelier
2009-04-11 19:38                     ` Michael Witten
2009-04-11 21:58                 ` [PATCH RFC 09/10] send-email: Minor cleanup of $smtp_server usage and send_message() Stephen Boyd
2009-04-11 22:13                   ` Michael Witten
2009-04-12  2:27               ` [PATCH RFC 08/10] send-email: Add --sleep for email throttling Jay Soffian
2009-04-12  2:59               ` Michael Witten
2009-04-11 19:17             ` [PATCH RFC 07/10] send-email: Remove horrible mix of tabs and spaces Sverre Rabbelier
2009-04-11 19:31               ` Michael Witten
2009-04-13  1:49               ` Miles Bader
2009-04-13  2:15                 ` Michael Witten
2009-04-11 21:52           ` [PATCH RFC 06/10] send-email: References: should only reference what is actually sent Stephen Boyd
2009-04-11 22:42             ` Michael Witten
2009-04-11 21:42   ` [PATCH RFC 02/10] Docs: send-email: Remove superfluous information in CONFIGURATION Stephen Boyd

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1239476908-25944-10-git-send-email-mfwitten@gmail.com \
    --to=mfwitten@gmail.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).