git send-email --notmuch expr
Notmuch is a new mail system with fast indexing and tagging, see notmuchmail.org. It has a command-line tool to build replies to emails, and I wrote a patch to format-patch that enables
git format-patch --notmuch EXPR
where EXPR is any notmuch query, but usually id:<Message-ID> to match a specific message. This will set up several headers, notably In-Reply-To, References, To, Cc, Bcc. This works great, and the the patch follows this message.
But I really want
git send-email --notmuch EXPR
This sort-of works, but the interactive part prompts for the various headers (even though format-patch supplies them), and then duplicates the To header (both the possibly empty field the user provided, plus the one coming from format-patch). I had a brief look at git-send-email.perl, and I'm a little confused.
} elsif (/^(?:To|Cc|Bcc):/i) {
print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
next;
}This regex doesn't match these headers (is the leading ?: a typo?) so there is no warning. But it's important that these headers *not* be ignored, getting them set automatically is a key feature of the --notmuch option. I'm having trouble discerning whether I would cause problems by just using these headers coming out of format-patch. Presumably there was a reason why they were (intended to be) explicitly ignored, and my poor perl skills are not helping. Could someone enlighten me?
Jed
PATCH 1/2 Added the --notmuch option to git format-patch.
The typical use case for this is
git format-patch --notmuch id:<MESSAGE-ID>
which will format your patch with all threading, references, and To, Cc fields appropriate for a reply to the given message.
--- builtin-log.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 57 insertions(+), 4 deletions(-)
builtin-log.c +57 -4
@@ -754,6 +754,53 @@ static const char *clean_message_id(const char *msg_id)return xmemdupz (a, z - a); } +static int get_notmuch_reply (struct strbuf *buf,struct string_list *ref_message_ids,const char *query) +{ +struct child_process notmuch; +struct strbuf notmuch_out; +char *p, *other_headers, *rstart; +ssize_t len; +const char *argv[] = {"notmuch" ,"reply" ,"--format=headers-only" , query,NULL }; + +memset (¬much,0 ,sizeof (notmuch)); + + notmuch.argv = argv; + notmuch.no_stdin =1 ; + notmuch.out =-1 ; + +if (start_command (¬much)) +return error ("could not run notmuch." ); + +strbuf_init (¬much_out,4096 ); + len =strbuf_read (¬much_out, notmuch.out,4096 ); +close (notmuch.out); + +if (finish_command (¬much) || !len || len <0 ) +return error ("notmuch did not return any headers" ); + +/* Harvest the referenced message IDs, all on the first line */ + p = notmuch_out.buf; + +if (!strncmp (p,"References: " ,sizeof ("References: " ))) +return error ("notmuch response malformed" ); + + other_headers =strstr (p,"To: " ); +if (!other_headers) +return error ("notmuch provided no other headers" ); + +while ((rstart =strchr (p,'<' )) && (p =strchr (rstart,'>' )) && p < other_headers-1 ) { +if (!p) +error ("notmuch returned malformed references" ); + p++; + *p++ =0 ; +string_list_append (clean_message_id (rstart), ref_message_ids); + } +/* Add everything after the first line */ +strbuf_addstr (buf, other_headers); +strbuf_release (¬much_out); +return 0 ; +} +static const char *set_outdir (const char *prefix,const char *output_directory) {if (output_directory &&is_absolute_path (output_directory))
@@ -893,7 +940,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)int boundary_count =0 ;int no_binary_diff =0 ;struct commit *origin =NULL , *head =NULL ; -const char *in_reply_to =NULL ; +const char *in_reply_to =NULL , *notmuch =NULL ;struct patch_ids ids;char *add_signoff =NULL ;struct strbuf buf =STRBUF_INIT ;
@@ -940,6 +987,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) PARSE_OPT_NONEG, cc_callback },OPT_STRING (0 ,"in-reply-to" , &in_reply_to,"message-id" ,"make first mail a reply to <message-id>" ), +OPT_STRING (0 ,"notmuch" , ¬much,"query" , +"make first mail a reply to messages matched by <query>" ), { OPTION_CALLBACK, 0, "attach", &rev, "boundary","attach the patch" ,PARSE_OPT_OPTARG , attach_callback },
@@ -1015,8 +1064,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)strbuf_addch (&buf,'\n' ); } - rev.extra_headers =strbuf_detach (&buf,NULL ); -if (start_number <0 ) start_number =1 ;
@@ -1135,12 +1182,18 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) numbered =1 ;if (numbered) rev.total = total + start_number -1 ; -if (in_reply_to || thread || cover_letter) +if (in_reply_to || thread || cover_letter || notmuch) rev.ref_message_ids =xcalloc (1 ,sizeof (struct string_list ));if (in_reply_to) {const char *msgid =clean_message_id (in_reply_to);string_list_append (msgid, rev.ref_message_ids); } +if (notmuch) { +get_notmuch_reply (&buf, rev.ref_message_ids, notmuch); + } + + rev.extra_headers =strbuf_detach (&buf,NULL ); + rev.numbered_files = numbered_files; rev.patch_suffix = fmt_patch_suffix;if (cover_letter) {
Signature · 1 line
1.6.5.3
PATCH 2/2 Documentation for format-patch --notmuch.
--- Documentation/git-format-patch.txt | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
Documentation/git-format-patch.txt +7 -0
@@ -171,6 +171,13 @@ will want to ensure that threading is disabled for `git send-email`. to any configured headers, and may be used multiple times. For example, `--add-header="Organization: git-foo"` +--notmuch=<expr>:: + Generate headers appropriate for a reply to a notmuch search for the + given expression. Usually an expression of the form id:<Message-ID> + will be used to match an exact message. The reply will set + In-Reply-To, References, To, Cc, Bcc, and possibly other headers + (depending on notmuch configuration). + --cover-letter:: In addition to the patches, generate a cover letter file containing the shortlog and the overall diffstat. You can
Signature · 1 line
1.6.5.3
Quoted 7 lines · Jed Brown
} elsif (/^(?:To|Cc|Bcc):/i) { print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n"; next; }This regex doesn't match these headers (is the leading ?: a typo?) so there is no warning.
(?: ... ) is in Perl non-capturing grouping
Quoted 1 line
From perlre(1)
"(?:pattern)"
"(?imsx-imsx:pattern)"
This is for clustering, not capturing; it groups subexpressions like "()", but doesn't make backreferences as "()" does.
So it is not a bug, and it definitely should match... unless implicit variable $_ (the default input and pattern-searching space) got mangled. It would be better to use explicit form:
$variable =~ /pattern/
Signature · 3 lines
Jakub Narebski Poland ShadeHawk on #git
Quoted 1 line · Jakub Narebski
(?: ... ) is in Perl non-capturing grouping
Thanks. Actually that code only executes under --compose, the headers provided by format-patch all just come through untouched (but all the interesting ones are duplicated). So it looks like we just need to actually parse the relevant headers from format-patch, before the part where the user gets prompted. These are a little harder because they can span multiple lines. The current validation with --compose is a bit
broken: suppose the user sets the perfectly valid header
To: [email protected],
[email protected]
The validation will strip the first line while issuing warning, but send the second through untouched.
Jed