{"thread":{"id":"63736","subject":"[PATCH v2 1/2] pretty: add X-Change-ID to mail formats","startedAt":"2025-07-03T11:35:29Z","lastAt":"2025-08-21T08:52:24Z","messageCount":17,"participants":["Drew DeVault","Jeff King","Aditya Garg","Junio C Hamano","Martin von Zweigbergk","Remo Senekowitsch"],"isPatch":true,"patchVersion":2,"patchTotal":2},"messages":[{"id":"521264","messageId":"20250703113505.11889-1-drew@ddevault.org","threadId":"63736","inReplyTo":null,"subject":"[PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-07-03T11:29:51Z","receivedAt":"2025-07-03T11:35:29Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"Introduce the X-Change-ID header to emails prepared by git (i.e. via\nformat-patch, send-email). This allows tools which work with those\nemails (e.g. patchwork, sourcehut) to meaningfully integrate with tools\nthat assign change IDs to commits.\n\nWith some follow-up work, this is also the first step towards ensuring\nthat those change IDs are preserved through from git-send-email to\ngit-am as a change moves through its review lifecycle.\n\nSigned-off-by: Drew DeVault <drew@ddevault.org>\n---\nv2 is unchanged from v1.\n\nOne remark that occurs to me upon spinning v2 is that I'm not sure how\nto test this behavior. There is no obvious way to cause git upstream to\nproduce a commit with a change-id -- presently these are only ever added\nby third-party tools.\n\n pretty.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/pretty.c b/pretty.c\nindex 0bc8ad8a9a..70fba7b023 100644\n--- a/pretty.c\n+++ b/pretty.c\n@@ -2045,7 +2045,7 @@ static void pp_header(struct pretty_print_context *pp,\n \tint parents_shown = 0;\n \n \tfor (;;) {\n-\t\tconst char *name, *line = *msg_p;\n+\t\tconst char *name, *change_id, *line = *msg_p;\n \t\tint linelen = get_one_line(*msg_p);\n \n \t\tif (!linelen)\n@@ -2089,6 +2089,11 @@ static void pp_header(struct pretty_print_context *pp,\n \t\t\tstrbuf_grow(sb, linelen + 80);\n \t\t\tpp_user_info(pp, \"Commit\", sb, name, encoding);\n \t\t}\n+\t\tif (skip_prefix(line, \"change-id \", &change_id) &&\n+\t\t    cmit_fmt_is_mail(pp->fmt)) {\n+\t\t\tstrbuf_addf(sb, \"X-Change-ID: %.*s\\n\",\n+\t\t\t\t    linelen - 11, change_id);\n+\t\t}\n \t}\n }\n \n-- \n2.50.0\n\n"},{"id":"521265","messageId":"20250703113505.11889-3-drew@ddevault.org","threadId":"63736","inReplyTo":"20250703113505.11889-1-drew@ddevault.org","subject":"[PATCH v2 2/2] am: import X-Change-ID from email headers","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-07-03T11:29:53Z","receivedAt":"2025-07-03T11:35:33Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"When parsing emails formatted by git-send-email which include a\nchange-id commit header, the change-id is written as X-Change-ID to the\nmail headers. This change causes git-am to grab the X-Change-ID email\nheader and write it to the commit header as change-id.\n\nThis completes the loop to ensure that sending and receiving patches via\nemail preserves the change-id header, if present.\n\nSigned-off-by: Drew DeVault <drew@ddevault.org>\n---\nI'm pretty sure I got all of the lifecycle details including --resume\ncorrect, but I would appreciate a second set of eyes.\n\nAlso not sure how to test this one, though this time it's less because\nof any fundamental limitations and has more to do with me not really\nunderstanding the test suite design. Perfectly acceptable for reviewers\nto insist on a v3 which adds the necessary tests but I will wait to\nendure that headache until some more discussion lands on this proposal\nin general.\n\n builtin/am.c | 46 ++++++++++++++++++++++++++++++++++++++++++----\n mailinfo.c   |  4 +++-\n 2 files changed, 45 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex a800003340..04cc9ef581 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -119,6 +119,7 @@ struct am_state {\n \tchar *author_name;\n \tchar *author_email;\n \tchar *author_date;\n+\tchar *change_id;\n \tchar *msg;\n \tsize_t msg_len;\n \n@@ -186,6 +187,7 @@ static void am_state_release(struct am_state *state)\n \tfree(state->author_name);\n \tfree(state->author_email);\n \tfree(state->author_date);\n+\tfree(state->change_id);\n \tfree(state->msg);\n \tstrvec_clear(&state->git_apply_opts);\n }\n@@ -408,6 +410,11 @@ static void am_load(struct am_state *state)\n \n \tread_commit_msg(state);\n \n+\tif (read_state_file(&sb, state, \"change-id\", 1) != -1) {\n+\t\tassert(!state->change_id);\n+\t\tstate->change_id = strbuf_detach(&sb, NULL);\n+\t}\n+\n \tif (read_state_file(&sb, state, \"original-commit\", 1) < 0)\n \t\toidclr(&state->orig_commit, the_repository->hash_algo);\n \telse if (get_oid_hex(sb.buf, &state->orig_commit) < 0)\n@@ -1119,11 +1126,13 @@ static void am_next(struct am_state *state)\n \tFREE_AND_NULL(state->author_name);\n \tFREE_AND_NULL(state->author_email);\n \tFREE_AND_NULL(state->author_date);\n+\tFREE_AND_NULL(state->change_id);\n \tFREE_AND_NULL(state->msg);\n \tstate->msg_len = 0;\n \n \tunlink(am_path(state, \"author-script\"));\n \tunlink(am_path(state, \"final-commit\"));\n+\tunlink(am_path(state, \"change-id\"));\n \n \toidclr(&state->orig_commit, the_repository->hash_algo);\n \tunlink(am_path(state, \"original-commit\"));\n@@ -1210,6 +1219,7 @@ static int parse_mail(struct am_state *state, const char *mail)\n \tstruct strbuf author_name = STRBUF_INIT;\n \tstruct strbuf author_date = STRBUF_INIT;\n \tstruct strbuf author_email = STRBUF_INIT;\n+\tstruct strbuf change_id = STRBUF_INIT;\n \tint ret = 0;\n \tstruct mailinfo mi;\n \n@@ -1288,6 +1298,8 @@ static int parse_mail(struct am_state *state, const char *mail)\n \t\t\tstrbuf_addstr(&author_email, x);\n \t\telse if (skip_prefix(sb.buf, \"Date: \", &x))\n \t\t\tstrbuf_addstr(&author_date, x);\n+\t\telse if (skip_prefix(sb.buf, \"X-Change-ID: \", &x))\n+\t\t\tstrbuf_addstr(&change_id, x);\n \t}\n \tfclose(fp);\n \n@@ -1310,6 +1322,11 @@ static int parse_mail(struct am_state *state, const char *mail)\n \tassert(!state->author_date);\n \tstate->author_date = strbuf_detach(&author_date, NULL);\n \n+\tif (change_id.len) {\n+\t\tassert(!state->change_id);\n+\t\tstate->change_id = strbuf_detach(&change_id, NULL);\n+\t}\n+\n \tassert(!state->msg);\n \tstate->msg = strbuf_detach(&msg, &state->msg_len);\n \n@@ -1318,6 +1335,7 @@ static int parse_mail(struct am_state *state, const char *mail)\n \tstrbuf_release(&author_date);\n \tstrbuf_release(&author_email);\n \tstrbuf_release(&author_name);\n+\tstrbuf_release(&change_id);\n \tstrbuf_release(&sb);\n \tclear_mailinfo(&mi);\n \treturn ret;\n@@ -1345,13 +1363,14 @@ static int get_mail_commit_oid(struct object_id *commit_id, const char *mail)\n }\n \n /**\n- * Sets state->msg, state->author_name, state->author_email, state->author_date\n- * to the commit's respective info.\n+ * Sets state->msg, state->author_name, state->author_email, state->author_date,\n+ * and state->change_id to the commit's respective info.\n  */\n static void get_commit_info(struct am_state *state, struct commit *commit)\n {\n-\tconst char *buffer, *ident_line, *msg;\n+\tconst char *buffer, *ident_line, *change_id, *msg;\n \tsize_t ident_len;\n+\tsize_t change_id_len;\n \tstruct ident_split id;\n \n \tbuffer = repo_logmsg_reencode(the_repository, commit, NULL,\n@@ -1381,6 +1400,12 @@ static void get_commit_info(struct am_state *state, struct commit *commit)\n \tassert(!state->author_date);\n \tstate->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL)));\n \n+\tchange_id = find_commit_header(buffer, \"change-id\", &change_id_len);\n+\tif (change_id && change_id_len) {\n+\t\tassert(!state->change_id);\n+\t\tstate->change_id = xmemdupz(change_id, change_id_len);\n+\t}\n+\n \tassert(!state->msg);\n \tmsg = strstr(buffer, \"\\n\\n\");\n \tif (!msg)\n@@ -1668,6 +1693,8 @@ static void do_commit(const struct am_state *state)\n \tstruct commit_list *parents = NULL;\n \tconst char *reflog_msg, *author, *committer = NULL;\n \tstruct strbuf sb = STRBUF_INIT;\n+\tstruct commit_extra_header change_id_hdr = {0};\n+\tstruct commit_extra_header *extra = NULL;\n \n \tif (!state->no_verify && run_hooks(the_repository, \"pre-applypatch\"))\n \t\texit(1);\n@@ -1699,9 +1726,16 @@ static void do_commit(const struct am_state *state)\n \t\t\t\t\t\t\t : state->author_date,\n \t\t\t\t      IDENT_STRICT);\n \n+\tif (state->change_id && strlen(state->change_id) != 0) {\n+\t\tchange_id_hdr.key = \"change-id\";\n+\t\tchange_id_hdr.value = state->change_id;\n+\t\tchange_id_hdr.len = strlen(state->change_id);\n+\t\textra = &change_id_hdr;\n+\t}\n+\n \tif (commit_tree_extended(state->msg, state->msg_len, &tree, parents,\n \t\t\t\t &commit, author, committer, state->sign_commit,\n-\t\t\t\t NULL))\n+\t\t\t\t extra))\n \t\tdie(_(\"failed to write commit object\"));\n \n \treflog_msg = getenv(\"GIT_REFLOG_ACTION\");\n@@ -1853,6 +1887,10 @@ static void am_run(struct am_state *state, int resume)\n \n \t\t\twrite_author_script(state);\n \t\t\twrite_commit_msg(state);\n+\n+\t\t\tif (state->change_id)\n+\t\t\t\twrite_state_text(state, \"change-id\",\n+\t\t\t\t\t\t state->change_id);\n \t\t}\n \n \t\tif (state->interactive && do_interactive(state))\ndiff --git a/mailinfo.c b/mailinfo.c\nindex b4e815b2d8..46d68a03d6 100644\n--- a/mailinfo.c\n+++ b/mailinfo.c\n@@ -351,7 +351,7 @@ static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject)\n }\n \n static const char * const header[] = {\n-\t\"From\", \"Subject\", \"Date\",\n+\t\"From\", \"Subject\", \"Date\", \"X-Change-ID\",\n };\n \n static inline int skip_header(const struct strbuf *line, const char *hdr,\n@@ -1183,6 +1183,8 @@ static void handle_info(struct mailinfo *mi)\n \t\t\thandle_from(mi, hdr);\n \t\t\tfprintf(mi->output, \"Author: %s\\n\", mi->name.buf);\n \t\t\tfprintf(mi->output, \"Email: %s\\n\", mi->email.buf);\n+\t\t} else if (!strcmp(header[i], \"X-Change-ID\")) {\n+\t\t\toutput_header_lines(mi->output, \"X-Change-ID\", hdr);\n \t\t} else {\n \t\t\tcleanup_space(hdr);\n \t\t\tfprintf(mi->output, \"%s: %s\\n\", header[i], hdr->buf);\n-- \n2.50.0\n\n"},{"id":"521355","messageId":"20250706033710.GD3041790@coredump.intra.peff.net","threadId":"63736","inReplyTo":"20250703113505.11889-1-drew@ddevault.org","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-07-06T03:37:10Z","receivedAt":"2025-07-06T03:37:12Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jul 03, 2025 at 01:29:51PM +0200, Drew DeVault wrote:\n\n> One remark that occurs to me upon spinning v2 is that I'm not sure how\n> to test this behavior. There is no obvious way to cause git upstream to\n> produce a commit with a change-id -- presently these are only ever added\n> by third-party tools.\n\nI don't have any opinion on the feature itself, but the plumbing way to\ndo it would perhaps be:\n\n  # make some vanilla commit...\n  git commit -m foo &&\n\n  # make a new variant with the change id\n  commit=$(\n    git cat-file commit HEAD |\n    perl -lpe 'print \"change-id foo\" unless length' |\n    git hash-object -w --stdin -t commit\n  ) &&\n\n  # replace the old one\n  git update-ref HEAD $commit\n\nwhich would be enough for Git's test suite. If this is something that\nother third-party tools are going to start adding, it might be worth\nadding some tests to Git's suite anyway to make sure it is handled\ncorrectly. (I didn't follow the discussion on whether a new commit\nheader was something the Git project wanted to endorse, versus sticking\nit in a trailer line, so don't take this as either a positive or\nnegative on the approach).\n\n-Peff\n"},{"id":"521357","messageId":"PN3PR01MB9597069B8CF014BFE01B53F3B84CA@PN3PR01MB9597.INDPRD01.PROD.OUTLOOK.COM","threadId":"63736","inReplyTo":"20250703113505.11889-1-drew@ddevault.org","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Aditya Garg","fromEmail":"gargaditya08@live.com","sentAt":"2025-07-06T06:20:57Z","receivedAt":"2025-07-06T06:21:04Z","isPatch":true,"sender":{"key":"gargaditya08@live.com","avatar":"https://avatars.githubusercontent.com/u/85610623?v=4"},"body":"\n\nOn 03-07-2025 04:59 pm, Drew DeVault wrote:\n> Introduce the X-Change-ID header to emails prepared by git (i.e. via\n> format-patch, send-email). This allows tools which work with those\n> emails (e.g. patchwork, sourcehut) to meaningfully integrate with tools\n> that assign change IDs to commits.\n> \n> With some follow-up work, this is also the first step towards ensuring\n> that those change IDs are preserved through from git-send-email to\n> git-am as a change moves through its review lifecycle.\n> \n> Signed-off-by: Drew DeVault <drew@ddevault.org>\n> ---\n> v2 is unchanged from v1.\n> \n> One remark that occurs to me upon spinning v2 is that I'm not sure how\n> to test this behavior. There is no obvious way to cause git upstream to\n> produce a commit with a change-id -- presently these are only ever added\n> by third-party tools.\n\nI don't think we should add it to email headers. There are many email providers\nwhich do not allow custom headers in the emails. For example if you are using\nprotonmail bridge or any third party protonmail client, the headers are not\npreserved. Similarly, if you are using MS Graph to send emails, headers are\nagain not preserved. We should also consider cases when people use Thunderbird,\nMutt or something similar to send emails, rather than git send-email.\n\nThe headers IMO should include the standard ones like From, Subject etc.\nCustom headers should be a part of body, just like we do Signed-off-by, Link etc.\n"},{"id":"521358","messageId":"DB4WQTRHWZN3.3VG20AZDK8VN@ddevault.org","threadId":"63736","inReplyTo":"PN3PR01MB9597069B8CF014BFE01B53F3B84CA@PN3PR01MB9597.INDPRD01.PROD.OUTLOOK.COM","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-07-06T10:41:50Z","receivedAt":"2025-07-06T10:42:08Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"On Sun Jul 6, 2025 at 8:20 AM CEST, Aditya Garg wrote:\n> I don't think we should add it to email headers. There are many email providers\n> which do not allow custom headers in the emails. For example if you are using\n> protonmail bridge or any third party protonmail client, the headers are not\n> preserved. Similarly, if you are using MS Graph to send emails, headers are\n> again not preserved. We should also consider cases when people use Thunderbird,\n> Mutt or something similar to send emails, rather than git send-email.\n\nAs far as I can tell, this isn't actually true. I looked into it and\nprotonmail and MS Graph both seem to support custom headers. I have also\nverified that mutt will preserve the header when you edit the email\nnormally with mutt -H. If you're sending an email with Thunderbird, none\nof these things are preserved (including From, Subject, etc), and the\nbest you can hope for is attaching the patch, in which case X-Change-ID\nwill be preserved unmolested.\n\nMoreover, if the change-id header is lost, it's not the end of the\nworld, it just degrades to the present-day state of affairs, in which\nyou cannot use it to associate patches with prior versions.\n\n> The headers IMO should include the standard ones like From, Subject etc.\n> Custom headers should be a part of body, just like we do Signed-off-by, Link etc.\n\nTrailers and headers are different. The main point of the change-id\ndiscussion earlier on this list was to avoid adding trailers.\n\nI also suspect that if we added this as an \"inbody header\" that older\ngit implementations would ingest the X-Change-ID header into the commit\nmessage, which is not a desirable behavior.\n\nIMO the right way forward is to use a mail header.\n"},{"id":"521359","messageId":"DB4WU136IYR2.3ELSGQUDD6QI8@ddevault.org","threadId":"63736","inReplyTo":"20250706033710.GD3041790@coredump.intra.peff.net","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-07-06T10:46:01Z","receivedAt":"2025-07-06T10:46:08Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"On Sun Jul 6, 2025 at 5:37 AM CEST, Jeff King wrote:\n> I don't have any opinion on the feature itself, but the plumbing way to\n> do it would perhaps be:\n>\n>   # make some vanilla commit...\n>   git commit -m foo &&\n>\n>   # make a new variant with the change id\n>   commit=$(\n>     git cat-file commit HEAD |\n>     perl -lpe 'print \"change-id foo\" unless length' |\n>     git hash-object -w --stdin -t commit\n>   ) &&\n>\n>   # replace the old one\n>   git update-ref HEAD $commit\n\nThanks! I'll incorporate this into the tests in the next patch version\nafter some further discussion.\n"},{"id":"521404","messageId":"xmqqfrf8ait6.fsf@gitster.g","threadId":"63736","inReplyTo":"DB4WQTRHWZN3.3VG20AZDK8VN@ddevault.org","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-07T01:30:45Z","receivedAt":"2025-07-07T01:30:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Drew DeVault\" <drew@ddevault.org> writes:\n\n> Trailers and headers are different. The main point of the change-id\n> discussion earlier on this list was to avoid adding trailers.\n\nIt sounds like this \"avoiding trailers\" is the root cause of the\nproblem.  If change-id is something not precious as you earlier\nsaid, having various operations lose it by design or by accident\nmay not hurt a lot, but then doesn't it make it harder to notice\nby hiding it in a non-standard commit header field?  There is no\neasy way to tell \"git log --format\" to show such a custom header,\nyou'd need to tell amend, rebase, cherry-pick, etc. to carry the\ncustom header forward (or not---have all the change-id loving\ncommunities agreed on when to propagate and when not to?).\n\nKeeping it as one of the trailer fields will always make it\navailable [*], propagate existing one by default with any existing\ntool, and because it is in the same place as log message, the user\ncan easily remove it if it is not appropriate to keep it.\n\n    Side note: [*] Unless you are doing \"log --oneline\", that is,\n    but I'd say at that point you are hiding it deliberately.\n\n> I also suspect that if we added this as an \"inbody header\" that older\n> git implementations would ingest the X-Change-ID header into the commit\n> message, which is not a desirable behavior.\n\nOf course not.  If the thing is a trailer, you do not even have to\nworry about such sillyness caused by adding it as a new in-body\nheader.\n\n> IMO the right way forward is to use a mail header.\n\nNo.  In the change-id case, trailer is the right way to go.\n\nHaving said all that, you may sense that I am not all that impressed\nby the previous rounds of dicsussions arguing for recording\nchange-id as an extra non-standard commit header.  We should think\ntwice or more before making anything that structurally does not\ncause Git to behave differently taking advantage of the information\nrecorded there an extra commit header field.\n\nBut after thinking thrice, we may find a set of good pieces of\ninformation that should be added as new commit header that are\nstructurally more meaningful, and there will be times when we need\nto convey them over e-mailed workflow to allow patch recipient not\nto lose such information.\n\nIn such a case, I fully agree that embedding in an e-mail header\nwould be the way to go.\n\nI would suggest a lot more generic implementation to solve it once\nand for all.  How about doing it more like this:\n\n   \"git format-patch --extra-headers\" grabs all extra headers\n   (i.e. those that are not the bog-standard \"tree\", \"parent\",\n   \"author\", \"committer\") and emit these\n\n    X-git-extra-commit-header: encoding=iso8859-1\n    X-git-extra-commit-header: frotz=nitfol\n\n   next to \"Subject:\", etc.\n\n"},{"id":"521410","messageId":"xmqqfrf88s28.fsf@gitster.g","threadId":"63736","inReplyTo":"xmqqfrf8ait6.fsf@gitster.g","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-07T05:53:51Z","receivedAt":"2025-07-07T05:53:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> IMO the right way forward is to use a mail header.\n>\n> No.  In the change-id case, trailer is the right way to go.\n> ...\n> But after thinking thrice, we may find a set of good pieces of\n> information that should be added as new commit header ...\n> ... and there will be times when we need\n> to convey them over e-mailed workflow to allow patch recipient not\n> to lose such information.\n\nOr a third-party software may add a new commit header without\ngauging and waiting for the community consensus anyway, which may or\nmay not have much structural meaning, and then we may want to extract\nthat piece of information hidden in the commit header out, because\nit was not written as trailer (in which case there wouldn't have\nneeded any extra effort to extract it in the first place).\n\nThis part can use a bit of clarification.\n\nMy endorsement below to use an extra e-mail header applies when some\ncommit objects ended up with extra non-standard headers holding\npieces of information that we want to send as part of a patch,\nwhether it is a good idea or a bad idea to place that particular\nkind of information in a commit header.  And the question is \"Now,\nwhat is the best way to transfer it over a patched e-mail?\"\n\nIf it were a good idea to place that particular kind of information\nin a header, that is of course an effort worth investing in.\n\nIf it were a horrible idea to place it in a header, it still is\nworth investing in an effort to give ourselves a way to salvage such\ninformation out of the header, even though we wouldn't have needed\nsuch extra tool if they didn't hide it in the header.\n\nBut once a generic mechanism is written, then Git does not have to\nbehave differently if an extra commit header is something a more\nrecent versions of Git tools started using after the idea gained\ncommunity consensus, or a third-party software unilaterally added\nwithout gauging or waiting for community consensus.  The same single\nmechanism can be used to extract the information and carry it in\ne-mails, and mailinfo can be told to extract it out.  It can be left\nup to the consumer after mailinfo disects the pieces of information\nout of the e-mail.\n\n> In such a case, I fully agree that embedding in an e-mail header\n> would be the way to go.\n>\n> I would suggest a lot more generic implementation to solve it once\n> and for all.  How about doing it more like this:\n>\n>    \"git format-patch --extra-headers\" grabs all extra headers\n>    (i.e. those that are not the bog-standard \"tree\", \"parent\",\n>    \"author\", \"committer\") and emit these\n>\n>     X-git-extra-commit-header: encoding=iso8859-1\n>     X-git-extra-commit-header: frotz=nitfol\n>\n>    next to \"Subject:\", etc.\n"},{"id":"521413","messageId":"CAESOdVAGEBCYOnFGUFojRk=6s=7RHc0i2jzuOVdBd91dXsCTEQ@mail.gmail.com","threadId":"63736","inReplyTo":"xmqqfrf88s28.fsf@gitster.g","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@google.com","sentAt":"2025-07-07T06:57:07Z","receivedAt":"2025-07-07T06:57:20Z","isPatch":true,"sender":{"key":"martinvonz@google.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Sun, 6 Jul 2025 at 22:53, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> >> IMO the right way forward is to use a mail header.\n> >\n> > No.  In the change-id case, trailer is the right way to go.\n> > ...\n> > But after thinking thrice, we may find a set of good pieces of\n> > information that should be added as new commit header ...\n> > ... and there will be times when we need\n> > to convey them over e-mailed workflow to allow patch recipient not\n> > to lose such information.\n>\n> Or a third-party software may add a new commit header without\n> gauging and waiting for the community consensus anyway, which may or\n> may not have much structural meaning, and then we may want to extract\n> that piece of information hidden in the commit header out, because\n> it was not written as trailer (in which case there wouldn't have\n> needed any extra effort to extract it in the first place).\n>\n> This part can use a bit of clarification.\n>\n> My endorsement below to use an extra e-mail header applies when some\n> commit objects ended up with extra non-standard headers holding\n> pieces of information that we want to send as part of a patch,\n> whether it is a good idea or a bad idea to place that particular\n> kind of information in a commit header.  And the question is \"Now,\n> what is the best way to transfer it over a patched e-mail?\"\n>\n> If it were a good idea to place that particular kind of information\n> in a header, that is of course an effort worth investing in.\n>\n> If it were a horrible idea to place it in a header, it still is\n> worth investing in an effort to give ourselves a way to salvage such\n> information out of the header, even though we wouldn't have needed\n> such extra tool if they didn't hide it in the header.\n\n+1\n\nDoes this also apply to commit signatures? I just created a signed\ncommit and checked what `git format-patch` produces. I was a bit\nsurprised to see that it doesn't seem to show up anywhere. Is it not\nsupported or did I miss some flag or config?\n\n>\n> But once a generic mechanism is written, then Git does not have to\n> behave differently if an extra commit header is something a more\n> recent versions of Git tools started using after the idea gained\n> community consensus, or a third-party software unilaterally added\n> without gauging or waiting for community consensus.  The same single\n> mechanism can be used to extract the information and carry it in\n> e-mails, and mailinfo can be told to extract it out.  It can be left\n> up to the consumer after mailinfo disects the pieces of information\n> out of the e-mail.\n>\n> > In such a case, I fully agree that embedding in an e-mail header\n> > would be the way to go.\n\n\nIs it another option to put it somewhere in the body? Could we fit\nadditional headers (e.g. signatures and third-party ones) somewhere\nbetween the `---` line and the additional diff? Or how about after the\nfinal `--` line? I haven't checked the specification. I just saw these\nlines in the `git format-patch` output.\n\n> >\n> > I would suggest a lot more generic implementation to solve it once\n> > and for all.  How about doing it more like this:\n> >\n> >    \"git format-patch --extra-headers\" grabs all extra headers\n> >    (i.e. those that are not the bog-standard \"tree\", \"parent\",\n> >    \"author\", \"committer\") and emit these\n> >\n> >     X-git-extra-commit-header: encoding=iso8859-1\n> >     X-git-extra-commit-header: frotz=nitfol\n> >\n> >    next to \"Subject:\", etc.\n"},{"id":"521414","messageId":"CAESOdVD-gWts6H-pSFBQfVn02nPBT1b0Xpfzp8Hea-QXsEA_TQ@mail.gmail.com","threadId":"63736","inReplyTo":"CAESOdVAGEBCYOnFGUFojRk=6s=7RHc0i2jzuOVdBd91dXsCTEQ@mail.gmail.com","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@google.com","sentAt":"2025-07-07T06:59:58Z","receivedAt":"2025-07-07T07:00:11Z","isPatch":true,"sender":{"key":"martinvonz@google.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Sun, 6 Jul 2025 at 23:57, Martin von Zweigbergk\n<martinvonz@google.com> wrote:\n>\n> On Sun, 6 Jul 2025 at 22:53, Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > Junio C Hamano <gitster@pobox.com> writes:\n> >\n> > >> IMO the right way forward is to use a mail header.\n> > >\n> > > No.  In the change-id case, trailer is the right way to go.\n> > > ...\n> > > But after thinking thrice, we may find a set of good pieces of\n> > > information that should be added as new commit header ...\n> > > ... and there will be times when we need\n> > > to convey them over e-mailed workflow to allow patch recipient not\n> > > to lose such information.\n> >\n> > Or a third-party software may add a new commit header without\n> > gauging and waiting for the community consensus anyway, which may or\n> > may not have much structural meaning, and then we may want to extract\n> > that piece of information hidden in the commit header out, because\n> > it was not written as trailer (in which case there wouldn't have\n> > needed any extra effort to extract it in the first place).\n> >\n> > This part can use a bit of clarification.\n> >\n> > My endorsement below to use an extra e-mail header applies when some\n> > commit objects ended up with extra non-standard headers holding\n> > pieces of information that we want to send as part of a patch,\n> > whether it is a good idea or a bad idea to place that particular\n> > kind of information in a commit header.  And the question is \"Now,\n> > what is the best way to transfer it over a patched e-mail?\"\n> >\n> > If it were a good idea to place that particular kind of information\n> > in a header, that is of course an effort worth investing in.\n> >\n> > If it were a horrible idea to place it in a header, it still is\n> > worth investing in an effort to give ourselves a way to salvage such\n> > information out of the header, even though we wouldn't have needed\n> > such extra tool if they didn't hide it in the header.\n>\n> +1\n>\n> Does this also apply to commit signatures? I just created a signed\n> commit and checked what `git format-patch` produces. I was a bit\n> surprised to see that it doesn't seem to show up anywhere. Is it not\n> supported or did I miss some flag or config?\n\nOh, perhaps they're deliberately not included because the commit\ntimestamp is not included in the patch so the signatures would be\ninvalid even if the patch was applied to the right parent?\n\n>\n> >\n> > But once a generic mechanism is written, then Git does not have to\n> > behave differently if an extra commit header is something a more\n> > recent versions of Git tools started using after the idea gained\n> > community consensus, or a third-party software unilaterally added\n> > without gauging or waiting for community consensus.  The same single\n> > mechanism can be used to extract the information and carry it in\n> > e-mails, and mailinfo can be told to extract it out.  It can be left\n> > up to the consumer after mailinfo disects the pieces of information\n> > out of the e-mail.\n> >\n> > > In such a case, I fully agree that embedding in an e-mail header\n> > > would be the way to go.\n>\n>\n> Is it another option to put it somewhere in the body? Could we fit\n> additional headers (e.g. signatures and third-party ones) somewhere\n> between the `---` line and the additional diff? Or how about after the\n> final `--` line? I haven't checked the specification. I just saw these\n> lines in the `git format-patch` output.\n>\n> > >\n> > > I would suggest a lot more generic implementation to solve it once\n> > > and for all.  How about doing it more like this:\n> > >\n> > >    \"git format-patch --extra-headers\" grabs all extra headers\n> > >    (i.e. those that are not the bog-standard \"tree\", \"parent\",\n> > >    \"author\", \"committer\") and emit these\n> > >\n> > >     X-git-extra-commit-header: encoding=iso8859-1\n> > >     X-git-extra-commit-header: frotz=nitfol\n> > >\n> > >    next to \"Subject:\", etc.\n"},{"id":"521415","messageId":"DB5MUUDPF6C0.3OR02N6JQB8H8@ddevault.org","threadId":"63736","inReplyTo":"xmqqfrf8ait6.fsf@gitster.g","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-07-07T07:09:34Z","receivedAt":"2025-07-07T07:09:46Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"On Mon Jul 7, 2025 at 3:30 AM CEST, Junio C Hamano wrote:\n> I would suggest a lot more generic implementation to solve it once\n> and for all.  How about doing it more like this:\n>\n>    \"git format-patch --extra-headers\" grabs all extra headers\n>    (i.e. those that are not the bog-standard \"tree\", \"parent\",\n>    \"author\", \"committer\") and emit these\n>\n>     X-git-extra-commit-header: encoding=iso8859-1\n>     X-git-extra-commit-header: frotz=nitfol\n>\n>    next to \"Subject:\", etc.\n\n+1. I particularly like how this approach throws out a bunch of arguing\nover the utility of the specific use-case -- clever :)\n\nDo you think there's any reason not to throw all extra headers into\nX-git-extra-commit-header (or whatever) unconditionally? Does it need to\nbe behind a flag or config option? If some tool added the extra commit\nheaders, they presumably have a good reason for doing so and we ought to\nencode that information so we can reproduce the commit properly, same as\nwe would with the rest of the commit headers.\n\nI suppose there is a scenario where this breaks something because\nsomeone has a poorly thought-out string munging parser for git\nformat-patch output that will barf upon encountering the unexpected, or\nsome mail provider rejects emails rather than silently dropping headers\nit doesn't like, but both possibilities seem remote -- especially when\nconsidering that these hypothetical edge cases have to be combined with\na use-case which deploys extra commit headers in the first place.\n"},{"id":"521416","messageId":"DB5MWRN5WUCZ.1DZ2SZ6WT3981@ddevault.org","threadId":"63736","inReplyTo":"CAESOdVAGEBCYOnFGUFojRk=6s=7RHc0i2jzuOVdBd91dXsCTEQ@mail.gmail.com","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-07-07T07:12:04Z","receivedAt":"2025-07-07T07:12:09Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"On Mon Jul 7, 2025 at 8:57 AM CEST, Martin von Zweigbergk wrote:\n> +1\n>\n> Does this also apply to commit signatures? I just created a signed\n> commit and checked what `git format-patch` produces. I was a bit\n> surprised to see that it doesn't seem to show up anywhere. Is it not\n> supported or did I miss some flag or config?\n\nThere is, to the best of my understanding, no serious effort being made\ntowards causing commit signatures to survive the git-format-patch/git-am\nprocess. There's also some confusion that often occurs here because\ncommit signatures are unrelated to PGP signatures and it is not possible\nto make either system meaningfully aware of the other.\n\n>> > In such a case, I fully agree that embedding in an e-mail header\n>> > would be the way to go.\n>\n> Is it another option to put it somewhere in the body? Could we fit\n> additional headers (e.g. signatures and third-party ones) somewhere\n> between the `---` line and the additional diff? Or how about after the\n> final `--` line? I haven't checked the specification. I just saw these\n> lines in the `git format-patch` output.\n\nI really think that it would be much wiser of us to put it in the email\nheaders, which already exist as a well-defined structured data format\nfor this purpose, rather than introduce something like commit trailers\nto the timely commentary section.\n"},{"id":"521436","messageId":"xmqqecus6uno.fsf@gitster.g","threadId":"63736","inReplyTo":"CAESOdVD-gWts6H-pSFBQfVn02nPBT1b0Xpfzp8Hea-QXsEA_TQ@mail.gmail.com","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-07T12:40:43Z","receivedAt":"2025-07-07T12:40:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Martin von Zweigbergk <martinvonz@google.com> writes:\n\n>> Does this also apply to commit signatures? I just created a signed\n>> commit and checked what `git format-patch` produces. I was a bit\n>> surprised to see that it doesn't seem to show up anywhere. Is it not\n>> supported or did I miss some flag or config?\n>\n> Oh, perhaps they're deliberately not included because the commit\n> timestamp is not included in the patch so the signatures would be\n> invalid even if the patch was applied to the right parent?\n\nAn excellent observation.\n\nYes, it was a deliberate design decision to omit cryptographic\nsignature(s) on the commit object itself, based on the assumption\nthat the primary motivation behind e-mailed patch workflow is to\nconvey the essense of the change in a readable form, while allowing\nminor modifications to both proposed log message and the diff\nwhile/before applying it, and on top of a state that is slightly\ndifferent from the state the patch was taken from.  It was out of\nscope to reproduce the history bit-for-bit identically.  Besides the\nauthor timestamp, the committer timestamp and identity (the\nrecipient of an e-mailed patch is likely to be different from the\nsender), \"git am -s --whitespace=fix\" would be a common thing a\nrecipient would want to clean up the patch sent over the e-mail\nanyway.\n\nBut with many past design decisions, the underlying assumptions are\nworth reevaluating from time to time.  They may have become\noutdated, or new use cases may have emerged that the tools are good\nfit to support them with enhancements.\n\nInstead of only limiting to convey the essense in a readable form,\nwe could also aim to support the exact bit-for-bit reproducibility\nunder certain conditions, e.g., the recipient has exact objects\nnamed by the \"tree\" and \"parent\" headers in the original commit\nobject.  Think of it as an e-mailable bundle file whose contents can\nbe inspected before unbundling.\n\nAnd for such a \"inspectable and e-mailable bundle that can be used\nto fully reproduce the state bit-for-bit\", ...\n\n>> Is it another option to put it somewhere in the body? Could we fit\n>> additional headers (e.g. signatures and third-party ones) somewhere\n>> between the `---` line and the additional diff? Or how about after the\n>> final `--` line? I haven't checked the specification. I just saw these\n>> lines in the `git format-patch` output.\n\n... I think a far simpler and more robust approach is to dump the\nwhole \"git cat-file commit\" output somewhere in the e-mail, not\nlimiting ourselves to \"extra\" headers, in some \"less susceptible to\ncorruption\" form (e.g. base85).  The standard headers like \"tree\"\nand \"parent\" are good thing to have if a new requirement is to allow\nexact reproducibility when the objects the patch was based on exist,\nand the raw commit message with trailing whitespaces would be needed\nin a protected form as well, since e-mailed patches can lose them\nduring transit.\n\nAnd below the three-dash line as you suggested is one good place to\nkeep such an extra piece of information.\n\nOr the \"cat-file commit\" dump can be on an extra e-mail header.\n\nSome e-mail environments will deliberately hide anything after the\nfinal \"-- \" (signature) line, and saving a message may even lose it,\nso it is not a good place to store anything that is worth feeding\n\"git am\" with.\n\nThanks.\n"},{"id":"524458","messageId":"DC6LB8FINRXH.1TMZPB1XKPQWQ@buenzli.dev","threadId":"63736","inReplyTo":"20250703113505.11889-1-drew@ddevault.org","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Remo Senekowitsch","fromEmail":"remo@buenzli.dev","sentAt":"2025-08-19T17:45:34Z","receivedAt":"2025-08-19T17:45:42Z","isPatch":true,"sender":{"key":"remo@buenzli.dev","avatar":"https://gravatar.com/avatar/7df680b096206886db5a2dc983926f314985bbee662eb406cf23c65322cf98b7?d=mp&s=160"},"body":"On Thu Jul 3, 2025 at 1:29 PM CEST, Drew DeVault wrote:\n> Introduce the X-Change-ID header to emails prepared by git (i.e. via\n> format-patch, send-email). This allows tools which work with those\n> emails (e.g. patchwork, sourcehut) to meaningfully integrate with tools\n> that assign change IDs to commits.\n>\n> With some follow-up work, this is also the first step towards ensuring\n> that those change IDs are preserved through from git-send-email to\n> git-am as a change moves through its review lifecycle.\n\nHi Drew,\n\nDo you intend to keep working on this by any chance? I'm writing a code\nreview tool that relies on Jujutsu's change-id header for its \"killer\nfeature\". It will also support different code review platforms as\nbackends, including GitHub, mailing lists and more. As long as mailing\nlists do not preserve the change-id header though, users of them will\nfundamentally have a degraded experience.\n\nThat is to say, your efforts here are much appreciated. :-)\n\nBest,\nRemo\n"},{"id":"524513","messageId":"DC72UF1IMIUF.2F7CNYOHYDGVJ@ddevault.org","threadId":"63736","inReplyTo":"DC6LB8FINRXH.1TMZPB1XKPQWQ@buenzli.dev","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-08-20T07:29:56Z","receivedAt":"2025-08-20T07:36:40Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"Hey Remo! I haven't gotten much actionable feedback on this patch yet,\nso there's not much to do here but wait for more reviewers.\n"},{"id":"524593","messageId":"xmqq4iu17b1y.fsf@gitster.g","threadId":"63736","inReplyTo":"DC72UF1IMIUF.2F7CNYOHYDGVJ@ddevault.org","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-08-21T00:50:17Z","receivedAt":"2025-08-21T00:50:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Drew DeVault\" <drew@ddevault.org> writes:\n\n> I haven't gotten much actionable feedback on this patch yet,\n> so there's not much to do here but wait for more reviewers.\n\nFor a topic that is older than 6 weeks, I am afraid that is a losing\nstrategy.  People who might have cared about the topic said all they\nwanted to say, new people are less likely to discover the topic than\nit was fresh, and unless you make an action (e.g., posting the \"next\npatch version\" you mentioned in [*1*]), it is highly unlikely for\nanything to happen while you are passive.  Even a small update that\naddresses all the little feedback would serve as a \"ping\" to reignite\ninterests.\n\nYou seem to have liked the approach to generalize and encode all the\ncommit object headers (except for of course the object name and\nauthor and committer ident, which already have place to be in the\nformat-patch output) on an e-mail header in [*2*].  That should be\nsufficient for a small update that tries to reignite interests.\n\n\n[References]\n\n*1* https://lore.kernel.org/git/DB4WU136IYR2.3ELSGQUDD6QI8@ddevault.org/\n*2* https://lore.kernel.org/git/DB5MUUDPF6C0.3OR02N6JQB8H8@ddevault.org/\n"},{"id":"524631","messageId":"DC7Z7YVT66NC.3RUJ7HXX2HSLW@ddevault.org","threadId":"63736","inReplyTo":"xmqq4iu17b1y.fsf@gitster.g","subject":"Re: [PATCH v2 1/2] pretty: add X-Change-ID to mail formats","fromName":"Drew DeVault","fromEmail":"drew@ddevault.org","sentAt":"2025-08-21T08:52:14Z","receivedAt":"2025-08-21T08:52:24Z","isPatch":true,"sender":{"key":"drew@ddevault.org","avatar":null},"body":"On Thu Aug 21, 2025 at 2:50 AM CEST, Junio C Hamano wrote:\n> For a topic that is older than 6 weeks, I am afraid that is a losing\n> strategy.  People who might have cared about the topic said all they\n> wanted to say, new people are less likely to discover the topic than\n> it was fresh, and unless you make an action (e.g., posting the \"next\n> patch version\" you mentioned in [*1*]), it is highly unlikely for\n> anything to happen while you are passive.  Even a small update that\n> addresses all the little feedback would serve as a \"ping\" to reignite\n> interests.\n>\n> You seem to have liked the approach to generalize and encode all the\n> commit object headers (except for of course the object name and\n> author and committer ident, which already have place to be in the\n> format-patch output) on an e-mail header in [*2*].  That should be\n> sufficient for a small update that tries to reignite interests.\n\nOh, of course. For some reason I had had the notion that I had already\nwritten a v3 based on this feedback and it was awaiting further\ncomments. But in fact I have done no such thing. I'll put this back on\nmy todo list and get a v3 out in the foreseeable future.\n"}]}