All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.