This is mostly a proof of concept/RFC patch. The idea is for git-send-email to store the message-ids it generates, along with the Subject and Date headers of the message. When prompting for which Message-ID should be used in In-Reply-To, display a list of recent emails (identifed using the Date/Subject pairs; the message-ids themselves are not for human consumption). Choosing from that list will then use the corresponding message-id; otherwise, the behaviour is as usual. When composing v2 or v3 of a patch or patch series, this avoids the need to get one's MUA to display the Message-ID of the earlier email (which is cumbersome in some MUAs) and then copy-paste that. If this idea is accepted, I'm certain I'll get to use the feature immediately, since the patch is not quite ready for inclusion :-) Signed-off-by: Rasmus Villemoes <rv@xxxxxxxxxxxxxxxxxx> --- git-send-email.perl | 101 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index f608d9b..2e3685c 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -26,6 +26,7 @@ use Data::Dumper; use Term::ANSIColor; use File::Temp qw/ tempdir tempfile /; use File::Spec::Functions qw(catfile); +use Date::Parse; use Error qw(:try); use Git; @@ -203,6 +204,7 @@ my ($validate, $confirm); my (@suppress_cc); my ($auto_8bit_encoding); my ($compose_encoding); +my ($msgid_cache_file, $msgid_maxprompt); my ($debug_net_smtp) = 0; # Net::SMTP, see send_message() @@ -214,7 +216,7 @@ my %config_bool_settings = ( "signedoffcc" => [\$signed_off_by_cc, undef], # Deprecated "validate" => [\$validate, 1], "multiedit" => [\$multiedit, undef], - "annotate" => [\$annotate, undef] + "annotate" => [\$annotate, undef], ); my %config_settings = ( @@ -237,6 +239,7 @@ my %config_settings = ( "from" => \$sender, "assume8bitencoding" => \$auto_8bit_encoding, "composeencoding" => \$compose_encoding, + "msgidcachefile" => \$msgid_cache_file, ); my %config_path_settings = ( @@ -311,6 +314,7 @@ my $rc = GetOptions("h" => \$help, "8bit-encoding=s" => \$auto_8bit_encoding, "compose-encoding=s" => \$compose_encoding, "force" => \$force, + "msgid-cache-file=s" => \$msgid_cache_file, ); usage() if $help; @@ -784,10 +788,31 @@ sub expand_one_alias { @bcclist = validate_address_list(sanitize_address_list(@bcclist)); if ($thread && !defined $initial_reply_to) { - $initial_reply_to = ask( - "Message-ID to be used as In-Reply-To for the first email (if any)? ", - default => "", - valid_re => qr/\@.*\./, confirm_only => 1); + my @choices; + if ($msgid_cache_file) { + @choices = msgid_cache_getmatches(); + } + if (@choices) { + my $prompt = ''; + my $i = 0; + $prompt .= sprintf "(%d) [%s] %s\n", $i++, $_->{date}, $_->{subject} + for (@choices); + $prompt .= sprintf "Answer 0-%d to use the Message-ID of one of the above\n", $#choices; + $prompt .= "Message-ID to be used as In-Reply-To for the first email (if any)? "; + $initial_reply_to = + ask($prompt, + default => "", + valid_re => qr/\@.*\.|^[0-9]+$/, confirm_only => 1); + if ($initial_reply_to =~ /^[0-9]+$/ && $initial_reply_to < @choices) { + $initial_reply_to = $choices[$initial_reply_to]{id}; + } + } + else { + $initial_reply_to = + ask("Message-ID to be used as In-Reply-To for the first email (if any)? ", + default => "", + valid_re => qr/\@.*\./, confirm_only => 1); + } } if (defined $initial_reply_to) { $initial_reply_to =~ s/^\s*<?//; @@ -1282,6 +1307,8 @@ X-Mailer: git-send-email $gitversion } } + msgid_cache_this($message_id, $subject, $date) if ($msgid_cache_file && !$dry_run); + return 1; } @@ -1508,6 +1535,8 @@ sub cleanup_compose_files { $smtp->quit if $smtp; +msgid_cache_write() if $msgid_cache_file; + sub unique_email_list { my %seen; my @emails; @@ -1556,3 +1585,65 @@ sub body_or_subject_has_nonascii { } return 0; } + +my @msgid_new_entries; + +# For now, use a simple tab-separated format: +# +# $id\t$date\t$subject\n +sub msgid_cache_read { + my $fh; + my $line; + my @entries; + if (not open ($fh, '<', $msgid_cache_file)) { + # A non-existing cache file is ok, but should we warn if errno != ENOENT? + return (); + } + while ($line = <$fh>) { + chomp($line); + my ($id, $date, $subject) = split /\t/, $line; + my $epoch = str2time($date); + push @entries, {id=>$id, date=>$date, epoch=>$epoch, subject=>$subject}; + } + close($fh); + return @entries; +} +sub msgid_cache_write { + my $fh; + if (not open($fh, '>>', $msgid_cache_file)) { + warn "cannot open $msgid_cache_file for appending: $!"; + return; + } + printf $fh "%s\t%s\t%s\n", $_->{id}, $_->{date}, $_->{subject} for (@msgid_new_entries); + close($fh); +} +# Return an array of cached message-ids, ordered by "relevance". It +# might make sense to take the Subject of the new mail as an extra +# argument and do some kind of fuzzy matching against the old +# subjects, but for now "more relevant" simply means "newer". +sub msgid_cache_getmatches { + my ($maxentries) = @_; + $maxentries //= 10; + my @list = msgid_cache_read(); + @list = sort {$b->{epoch} <=> $a->{epoch}} @list; + @list = @list[0 .. $maxentries-1] if (@list > $maxentries); + return @list; +} + +sub msgid_cache_this { + my $msgid = shift; + my $subject = shift; + my $date = shift; + # Make sure there are no tabs which will confuse us, and save + # some valuable horizontal real-estate by removing redundant + # whitespace. + if ($subject) { + $subject =~ s/^\s+|\s+$//g; + $subject =~ s/\s+/ /g; + } + # Replace undef or the empty string by an actual string. Nobody uses "0" as the subject... + $subject ||= '(none)'; + $date //= format_2822_time(time()); + $date =~ s/\s+/ /g; + push @msgid_new_entries, {id => $msgid, subject => $subject, date => $date}; +} -- 1.8.4.rc3.2.gb900fc8 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html