{"thread":{"id":"65449","subject":"[PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","startedAt":"2026-04-07T03:13:36Z","lastAt":"2026-04-07T23:28:44Z","messageCount":12,"participants":["Matt Stark","Junio C Hamano","Nico Williams","Phillip Wood","brian m. carlson"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"541031","messageId":"CAH7WC73-4p0RrqKNSh2G-xfpfO7QHZiXHbU_UFRkM3Q=bMWTDw@mail.gmail.com","threadId":"65449","inReplyTo":null,"subject":"[PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Matt Stark","fromEmail":"msta@google.com","sentAt":"2026-04-07T03:13:18Z","receivedAt":"2026-04-07T03:13:36Z","isPatch":true,"body":"In the discussions on\nhttps://lore.kernel.org/git/Z_OGMb-1oV0Ex05e@pks.im/T/#m038be849b9b4020c16c562d810cf77bad91a2c87,\nit seems to be that:\n* There is consensus that a `change-id` header provides good value\n* There is not consenus on what precise format that should take\n\nThis commit, rather than attempting to standardize the format, simply\npreserves the change-id header in whatever format it used previously.\n\nIf we so choose, we can later decide on a standardized format, but since\ngit only preserves existing headers, this should not create backwards\nincompatibility.\n\nSigned-off-by: Matt Stark <msta@google.com>\n---\n sequencer.c                           | 39 ++++++++++++++++++++++-----\n t/t3400-rebase.sh                     | 20 ++++++++++++++\n t/t3501-revert-cherry-pick.sh         | 15 +++++++++++\n t/t7501-commit-basic-functionality.sh | 15 +++++++++++\n 4 files changed, 83 insertions(+), 6 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b7d8dca47f..093d47d42a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1530,12 +1530,12 @@ static int try_to_commit(struct repository *r,\n  struct strbuf *msg, const char *author,\n  const char *reflog_action,\n  struct replay_opts *opts, unsigned int flags,\n- struct object_id *oid)\n+ struct object_id *oid,\n+ struct commit_extra_header *extra)\n {\n  struct object_id tree;\n  struct commit *current_head = NULL;\n  struct commit_list *parents = NULL;\n- struct commit_extra_header *extra = NULL;\n  struct strbuf err = STRBUF_INIT;\n  struct strbuf commit_msg = STRBUF_INIT;\n  char *amend_author = NULL;\n@@ -1721,7 +1721,8 @@ static int do_commit(struct repository *r,\n       const char *msg_file, const char *author,\n       const char *reflog_action,\n       struct replay_opts *opts, unsigned int flags,\n-      struct object_id *oid)\n+      struct object_id *oid,\n+      struct commit_extra_header *extra_headers)\n {\n  int res = 1;\n\n@@ -1735,7 +1736,7 @@ static int do_commit(struct repository *r,\n     msg_file);\n\n  res = try_to_commit(r, msg_file ? &sb : NULL,\n-     author, reflog_action, opts, flags, &oid);\n+     author, reflog_action, opts, flags, &oid, extra_headers);\n  strbuf_release(&sb);\n  if (!res) {\n  refs_delete_ref(get_main_ref_store(r), \"\",\n@@ -2511,10 +2512,36 @@ static int do_pick_commit(struct repository *r,\n  oid_to_hex(&commit->object.oid), msg.subject);\n  } /* else allow == 0 and there's nothing special to do */\n  if (!opts->no_commit && !drop_commit) {\n- if (author || command == TODO_REVERT || (flags & AMEND_MSG))\n+ if (author || command == TODO_REVERT || (flags & AMEND_MSG)) {\n+ struct commit_extra_header *extra_headers = NULL;\n+ if (commit) {\n+ unsigned long size;\n+ const char *buffer = repo_get_commit_buffer(r, commit, &size);\n+ size_t out_len;\n+ // The Gerrit, GitButler, and Jujutsu projects all have a concept of\n+ // a \"change id\", and it behaves in a similar way between the three\n+ // tools. The change id is conceptually associated with a commit.\n+ // It follows a commit as its rewritten (e.g. by amending and\n+ // rebasing).\n+ // While git doesn't add this header itself, and currently has no plans\n+ // to do so, there is consensus that if the header is added by another\n+ // tool, git should at least preserve it.\n+ const char *header_value = find_commit_header(buffer, \"change-id\", &out_len);\n+ if (header_value) {\n+ extra_headers = xmalloc(sizeof(*extra_headers));\n+ *extra_headers = (struct commit_extra_header){\n+ .next = NULL,\n+ .key = xstrdup(\"change-id\"),\n+ .value = xmemdupz(header_value, out_len),\n+ .len = out_len\n+ };\n+ }\n+ repo_unuse_commit_buffer(r, commit, buffer);\n+ }\n  res = do_commit(r, msg_file, author, reflog_action,\n  opts, flags,\n- commit? &commit->object.oid : NULL);\n+ commit ? &commit->object.oid : NULL, extra_headers);\n+ }\n  else\n  res = error(_(\"unable to parse commit author\"));\n  *check_todo = !!(flags & EDIT_MSG);\ndiff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh\nindex c0c00fbb7b..6b5d6fe56f 100755\n--- a/t/t3400-rebase.sh\n+++ b/t/t3400-rebase.sh\n@@ -474,4 +474,24 @@ test_expect_success 'git rebase --update-ref with\ncore.commentChar and branch on\n  test_grep \"% Ref refs/heads/topic2 checked out at\" actual\n '\n\n+test_expect_success 'rebase preserves change-id header' '\n+ test_commit \"source-for-rebase\" file-rebase content-rebase &&\n+ git cat-file commit HEAD >commit_obj &&\n+ awk \"/^committer / { print; print \\\"change-id my-change-id\\\"; next\n}1\" commit_obj >commit_obj_mod &&\n+ new_commit=$(git hash-object -t commit -w commit_obj_mod) &&\n+ git branch -f source-branch $new_commit &&\n+\n+ git checkout -b target-branch HEAD^ &&\n+ echo \"unrelated\" >file-unrelated &&\n+ git add file-unrelated &&\n+ git commit -m \"unrelated\" &&\n+\n+ git checkout source-branch &&\n+ git rebase target-branch &&\n+\n+ git cat-file commit HEAD >result_obj &&\n+ grep \"^change-id my-change-id$\" result_obj\n+'\n+\n test_done\n+\ndiff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh\nindex 8025a28cfd..0ada99f216 100755\n--- a/t/t3501-revert-cherry-pick.sh\n+++ b/t/t3501-revert-cherry-pick.sh\n@@ -256,4 +256,19 @@ test_expect_success 'cherry-pick is unaware of\n--reference (for now)' '\n  grep \"^usage: git cherry-pick\" actual\n '\n\n+test_expect_success 'cherry-pick preserves change-id header' '\n+ test_commit \"source-for-cherry\" file-cherry content-cherry &&\n+ git cat-file commit HEAD >commit_obj &&\n+ awk \"/^committer / { print; print \\\"change-id my-change-id\\\"; next\n}1\" commit_obj >commit_obj_mod &&\n+ new_commit=$(git hash-object -t commit -w commit_obj_mod) &&\n+ git branch -f source-branch $new_commit &&\n+\n+ git checkout -b target-branch HEAD^ &&\n+ git cherry-pick source-branch &&\n+\n+ git cat-file commit HEAD >result_obj &&\n+ grep \"^change-id my-change-id$\" result_obj\n+'\n+\n test_done\n+\ndiff --git a/t/t7501-commit-basic-functionality.sh\nb/t/t7501-commit-basic-functionality.sh\nindex a37509f004..e25dd9dc6f 100755\n--- a/t/t7501-commit-basic-functionality.sh\n+++ b/t/t7501-commit-basic-functionality.sh\n@@ -793,4 +793,19 @@ test_expect_success '--dry-run --short' '\n  git commit --dry-run --short\n '\n\n+test_expect_success 'amend preserves change-id header' '\n+ test_commit \"source-for-amend\" file-amend content-amend &&\n+ git cat-file commit HEAD >commit_obj &&\n+ awk \"/^committer / { print; print \\\"change-id my-change-id\\\"; next\n}1\" commit_obj >commit_obj_mod &&\n+ new_commit=$(git hash-object -t commit -w commit_obj_mod) &&\n+ git reset --hard $new_commit &&\n+\n+ echo \"amended content\" >>file-amend &&\n+ git add file-amend &&\n+ git commit --amend --no-edit &&\n+\n+ git cat-file commit HEAD >result_obj &&\n+ grep \"^change-id my-change-id$\" result_obj\n+'\n+\n test_done\n-- \n2.53.0.1213.gd9a14994de-goog\n"},{"id":"541033","messageId":"xmqqqzor76nh.fsf@gitster.g","threadId":"65449","inReplyTo":"CAH7WC73-4p0RrqKNSh2G-xfpfO7QHZiXHbU_UFRkM3Q=bMWTDw@mail.gmail.com","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-04-07T04:09:54Z","receivedAt":"2026-04-07T04:09:57Z","isPatch":true,"body":"Matt Stark <msta@google.com> writes:\n\n> In the discussions on\n> https://lore.kernel.org/git/Z_OGMb-1oV0Ex05e@pks.im/T/#m038be849b9b4020c16c562d810cf77bad91a2c87,\n> it seems to be that:\n> * There is consensus that a `change-id` header provides good value\n\nI doubt it.\n\nThere are multiple people who wanted it, but as far as I can recall,\nI did not get the sense that they had the same semantics in mind.\n\n> * There is not consenus on what precise format that should take\n\nFormat is one thing, but what it means is much more important.  When\nis it inherited?  What happens when you split a single commit into\nthree pieces, which piece, if any, among the resulting three will\ninherit thee parent's?  Should rebase, cherry-pick, and replay\nbehave the same way (IIRC, rebase and cherry-pick behaves\ndifferently while propagating notes).  Etc., etc.\n\n\n"},{"id":"541036","messageId":"adSPznztKWo63Tjr@ubby","threadId":"65449","inReplyTo":"adSO6zPwtFOWBcOw@ubby","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Nico Williams","fromEmail":"nico@cryptonector.com","sentAt":"2026-04-07T05:02:06Z","receivedAt":"2026-04-07T05:02:17Z","isPatch":true,"body":"On Mon, Apr 06, 2026 at 11:58:19PM -0500, Nico Williams wrote:\n> Maybe that's the trick: local configuration for determining the\n> copy-or-drop semantic for different operations, and maybe hooks for\n> altering when copying.  [...]\n\nI should add that I would want an original-change-id header that could\nbe used (again, optionally) to relate commits that get cherry-picked or\nrebased but end up having different change-ids.\n"},{"id":"541047","messageId":"adSO6zPwtFOWBcOw@ubby","threadId":"65449","inReplyTo":"xmqqqzor76nh.fsf@gitster.g","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Nico Williams","fromEmail":"nico@cryptonector.com","sentAt":"2026-04-07T04:58:19Z","receivedAt":"2026-04-07T08:23:18Z","isPatch":true,"body":"On Mon, Apr 06, 2026 at 09:09:54PM -0700, Junio C Hamano wrote:\n> Matt Stark <msta@google.com> writes:\n> \n> > In the discussions on\n> > https://lore.kernel.org/git/Z_OGMb-1oV0Ex05e@pks.im/T/#m038be849b9b4020c16c562d810cf77bad91a2c87,\n> > it seems to be that:\n> > * There is consensus that a `change-id` header provides good value\n> \n> I doubt it.\n> \n> There are multiple people who wanted it, but as far as I can recall,\n> I did not get the sense that they had the same semantics in mind.\n\nThe less semantics it has, the more acceptable it might be :)\nBut then what would need patching?  So it needs _some_ semantics.\n\nFinding the minimal acceptable semantics for this header is the trick to\npull.\n\n> > * There is not consenus on what precise format that should take\n> \n> Format is one thing, but what it means is much more important.  When\n> is it inherited?  What happens when you split a single commit into\n> three pieces, which piece, if any, among the resulting three will\n> inherit thee parent's?  Should rebase, cherry-pick, and replay\n> behave the same way (IIRC, rebase and cherry-pick behaves\n> differently while propagating notes).  Etc., etc.\n\nExactly.  I remember I argued that cherry-pick and rebase should have\nthe same behavior given that rebase is logically a script of\ncherry-picks, but others had strong arguments that the two should not\nhave the same behavior (something which is not hard to implement if you\nmake the inherittance / non-inherittance an option to cherry-pick has\ndifferent defaults for cherry-pick than for rebase).\n\nThat the value of this header should not have a format imposed -- that\nmuch is certainly the case as far as consensus goes, I think.  Basically\nit should be site-local, for some definition of site.  But the tooling\ncan just treat it as opaque, perhaps with hooks to do any interpretation\nof those values.\n\nMaybe that's the trick: local configuration for determining the\ncopy-or-drop semantic for different operations, and maybe hooks for\naltering when copying.  Thus for example splitting a commit (something\njj supports directly but Git doesn't, unless I missed something) could\nderive or create new change-id values from the original using hooks.  A\nhook might do things like create child or sibling problem tickets, or\nmight only qualify the original with some qualifier.  A hook might even\ninteract with the user to create new change-ids as needed.\n\nThe risk here is that this could yield too much configuration and be\nmore annoying than useful, but I think that wouldn't turn out to be the\ncase.\n\nNico\n-- \n"},{"id":"541049","messageId":"8f485b7f-3f6c-454c-8e87-d96ad8fa616c@gmail.com","threadId":"65449","inReplyTo":"xmqqqzor76nh.fsf@gitster.g","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-04-07T09:41:43Z","receivedAt":"2026-04-07T09:41:46Z","isPatch":true,"body":"On 07/04/2026 05:09, Junio C Hamano wrote:\n> Matt Stark <msta@google.com> writes:\n> \n>> In the discussions on\n>> https://lore.kernel.org/git/Z_OGMb-1oV0Ex05e@pks.im/T/#m038be849b9b4020c16c562d810cf77bad91a2c87,\n>> it seems to be that:\n>> * There is consensus that a `change-id` header provides good value\n> \n> I doubt it.\n> \n> There are multiple people who wanted it, but as far as I can recall,\n> I did not get the sense that they had the same semantics in mind.\n> \n>> * There is not consenus on what precise format that should take\n> \n> Format is one thing, but what it means is much more important.  When\n> is it inherited?  What happens when you split a single commit into\n> three pieces, which piece, if any, among the resulting three will\n> inherit thee parent's?  Should rebase, cherry-pick, and replay\n> behave the same way (IIRC, rebase and cherry-pick behaves\n> differently while propagating notes).  Etc., etc.\n\nIndeed, copying the header is easy (though the patch does not support \ncopying the header when the commit message is edited), but agreeing on \nthe semantics seems to be much harder.\n\nThanks\n\nPhillip\n\n\n"},{"id":"541050","messageId":"68e5a1eb-ec7b-43ca-98d1-ffdf7fef013f@gmail.com","threadId":"65449","inReplyTo":"adSO6zPwtFOWBcOw@ubby","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2026-04-07T09:55:00Z","receivedAt":"2026-04-07T09:55:03Z","isPatch":true,"body":"On 07/04/2026 05:58, Nico Williams wrote:\n> \n> Maybe that's the trick: local configuration for determining the\n> copy-or-drop semantic for different operations, and maybe hooks for\n> altering when copying.\n\nI think the danger with making it configurable is that you cannot rely \non the semantics because they vary between commits created by different \nauthors. If we could get agreement on\n\n  - Should cherry-pick copy the header\n\n  - What to do with the header when a commit is split. Three options\n    spring to mind (1) create new change-ids for all the new commits (2)\n    create new change-ids but also copy the old one (3) allow the user to\n    specify which new commit should copy the existing change-id and\n    create new change-ids for the other commits.\n\n  - What to do when commits are squashed - should the new commit copy all\n    of change-ids? Should it have a new change-id?\n\nThen I think it'd be much clearer what the implementation should do.\n\nThanks\n\nPhillip\n\n"},{"id":"541067","messageId":"xmqqh5pm7sd1.fsf@gitster.g","threadId":"65449","inReplyTo":"adSPznztKWo63Tjr@ubby","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-04-07T14:33:14Z","receivedAt":"2026-04-07T14:33:17Z","isPatch":true,"body":"Nico Williams <nico@cryptonector.com> writes:\n\n> On Mon, Apr 06, 2026 at 11:58:19PM -0500, Nico Williams wrote:\n>> Maybe that's the trick: local configuration for determining the\n>> copy-or-drop semantic for different operations, and maybe hooks for\n>> altering when copying.  [...]\n>\n> I should add that I would want an original-change-id header that could\n> be used (again, optionally) to relate commits that get cherry-picked or\n> rebased but end up having different change-ids.\n\nWith these people with (possibly just slightly) different wants\ndifferent project may have, wouldn't it work to record this kind of\nrandom pieces of information either in notes (the benefit being that\nit can be corrected without having to rewrite history) or in\ntrailers?\n\n"},{"id":"541068","messageId":"xmqqcy0a7rya.fsf@gitster.g","threadId":"65449","inReplyTo":"adSO6zPwtFOWBcOw@ubby","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-04-07T14:42:05Z","receivedAt":"2026-04-07T14:42:08Z","isPatch":true,"body":"Nico Williams <nico@cryptonector.com> writes:\n\n>> Format is one thing, but what it means is much more important.  When\n>> is it inherited?  What happens when you split a single commit into\n>> three pieces, which piece, if any, among the resulting three will\n>> inherit thee parent's?  Should rebase, cherry-pick, and replay\n>> behave the same way (IIRC, rebase and cherry-pick behaves\n>> differently while propagating notes).  Etc., etc.\n>\n> Exactly.  I remember I argued that cherry-pick and rebase should have\n> the same behavior given that rebase is logically a script of\n> cherry-picks, but others had strong arguments that the two should not\n> have the same behavior (something which is not hard to implement if you\n> make the inherittance / non-inherittance an option to cherry-pick has\n> different defaults for cherry-pick than for rebase).\n\nYes.  Even though I often feel irritated when I use cherry-pick and\nsee the \"amlog\" note not propagate when I should have used rebase, I\nthink it makes sense to allow cherry-pick and rebase to behavve\ndifferently.  This is because rebase is a rewriting operation, where\nthe old incarnation of the topic is discarded (other than that it\ncan be resurrected from the reflog of the branch for the topic) and\nonly the new incarnation will stay in the history, while cherry-pick\nis a duplicating operation, where the new copy is an adaptation of\nthe original commit into a different context and both of them will\nstay in the history serving different purpose.\n\n> That the value of this header should not have a format imposed -- that\n> much is certainly the case as far as consensus goes, I think.  Basically\n> it should be site-local, for some definition of site.  But the tooling\n> can just treat it as opaque, perhaps with hooks to do any interpretation\n> of those values.\n\nAnd there is nothing to prevent us from doing all of the above (and\nmore) with trailers.  The existing interpret-trailers mechanism may\nbe lacking, but hopefully it gives enough framework to build on top\nto allow projects to customize what they want them to mean and how\nthey behave.\n"},{"id":"541072","messageId":"adUoR/T17fKr+YLN@ubby","threadId":"65449","inReplyTo":"68e5a1eb-ec7b-43ca-98d1-ffdf7fef013f@gmail.com","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Nico Williams","fromEmail":"nico@cryptonector.com","sentAt":"2026-04-07T15:52:39Z","receivedAt":"2026-04-07T15:52:53Z","isPatch":true,"body":"On Tue, Apr 07, 2026 at 10:55:00AM +0100, Phillip Wood wrote:\n> On 07/04/2026 05:58, Nico Williams wrote:\n> > \n> > Maybe that's the trick: local configuration for determining the\n> > copy-or-drop semantic for different operations, and maybe hooks for\n> > altering when copying.\n> \n> I think the danger with making it configurable is that you cannot rely on\n> the semantics because they vary between commits created by different\n> authors. [...]\n\nWell, I said \"site-local\" and \"for some definition of site\", and the one\nI had in mind is that the upstream provides this [default] configuration\nfor clones.  Sure, authors could override this locally, but presumably\nthey wouldn't, and presumably upstreams would check for adherence to\ntheir rules.\n\n>   [...]. If we could get agreement on\n\nThat's proven difficult to do.\n\nNico\n-- \n"},{"id":"541075","messageId":"xmqqtstm68to.fsf@gitster.g","threadId":"65449","inReplyTo":"adUoR/T17fKr+YLN@ubby","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-04-07T16:20:35Z","receivedAt":"2026-04-07T16:20:38Z","isPatch":true,"body":"Nico Williams <nico@cryptonector.com> writes:\n\n> On Tue, Apr 07, 2026 at 10:55:00AM +0100, Phillip Wood wrote:\n>> On 07/04/2026 05:58, Nico Williams wrote:\n>> > \n>> > Maybe that's the trick: local configuration for determining the\n>> > copy-or-drop semantic for different operations, and maybe hooks for\n>> > altering when copying.\n>> \n>> I think the danger with making it configurable is that you cannot rely on\n>> the semantics because they vary between commits created by different\n>> authors. [...]\n>\n> Well, I said \"site-local\" and \"for some definition of site\", and the one\n> I had in mind is that the upstream provides this [default] configuration\n> for clones.  Sure, authors could override this locally, but presumably\n> they wouldn't, and presumably upstreams would check for adherence to\n> their rules.\n\nThis does sound quite sensible.  What you called \"site\", I called\n\"project\" in my earlier responses.\n\nSome projects do already check that the changes are signed off with\nthe \"Signed-off-by\" trailers.  If change-id or original-change-id or\nwhatnot are deemed essential to a project, and are expected to be\nformatted in certain ways, the project will certainly validate them.\n\nNone of that requires us to hide this information in the commit\nobject header, by the way.  And indeed, it is easier to validate\nwhat is in the \"git log\" output (where optional header elements like\n\"encoding\" are not shown).\n\n\n"},{"id":"541091","messageId":"adVlc/y8HjvSG8KQ@ubby","threadId":"65449","inReplyTo":"xmqqtstm68to.fsf@gitster.g","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"Nico Williams","fromEmail":"nico@cryptonector.com","sentAt":"2026-04-07T20:13:39Z","receivedAt":"2026-04-07T20:13:50Z","isPatch":true,"body":"On Tue, Apr 07, 2026 at 09:20:35AM -0700, Junio C Hamano wrote:\n> Nico Williams <nico@cryptonector.com> writes:\n> > Well, I said \"site-local\" and \"for some definition of site\", and the one\n> > I had in mind is that the upstream provides this [default] configuration\n> > for clones.  Sure, authors could override this locally, but presumably\n> > they wouldn't, and presumably upstreams would check for adherence to\n> > their rules.\n> \n> This does sound quite sensible.  What you called \"site\", I called\n> \"project\" in my earlier responses.\n> \n> Some projects do already check that the changes are signed off with\n> the \"Signed-off-by\" trailers.  If change-id or original-change-id or\n> whatnot are deemed essential to a project, and are expected to be\n> formatted in certain ways, the project will certainly validate them.\n\nCool!  Maybe we can achieve consensus.  Here's a strawman:\n\n - upstreams publish (where?) a set of policies for\n\n    - change-id\n    - original-change-id\n\n   regarding:\n\n    - commit splits\n    - commit squashes\n    - cherry-picks\n    - rebases\n\n - these policies should reference named hooks that have to be locally\n   installed in the clone (that way the upstream can't just cause\n   arbitrary remote execution clone-side) -- hooks that can transform\n   change IDs\n\nWe should probably also have options for cherry-pick and rebase that a\nuser can use to provide useful context such as \"this is a backport to\n...\", or \"this is for <ticket>\" (adds change-id).\n\nHooks could do things like create child tickets, etc.\n\nPunting all semantics to hooks and upstream policies leaves only generic\nthings to decide, namely: what operations call what hooks.  And that\nshould leave us nothing to argue passionately over.\n\n> None of that requires us to hide this information in the commit\n> object header, by the way.  And indeed, it is easier to validate\n> what is in the \"git log\" output (where optional header elements like\n> \"encoding\" are not shown).\n\nYes, for sure, this could just be commit message formatting practices\nenforced by hooks.  In this case there should be a hook for extracting\nchange ID(s) from a commit message.\n\nNico\n-- \n"},{"id":"541109","messageId":"adWTKt20ISC3qz2g@fruit.crustytoothpaste.net","threadId":"65449","inReplyTo":"CAH7WC73-4p0RrqKNSh2G-xfpfO7QHZiXHbU_UFRkM3Q=bMWTDw@mail.gmail.com","subject":"Re: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2026-04-07T23:28:42Z","receivedAt":"2026-04-07T23:28:44Z","isPatch":true,"body":"On 2026-04-07 at 03:13:18, Matt Stark wrote:\n> In the discussions on\n> https://lore.kernel.org/git/Z_OGMb-1oV0Ex05e@pks.im/T/#m038be849b9b4020c16c562d810cf77bad91a2c87,\n> it seems to be that:\n> * There is consensus that a `change-id` header provides good value\n\nI'm not sure I agree.\n\nAbsent some well-defined documentation describing what it means, I don't\nsee how it could provide good value.  It sounds like you're saying a\npersistent commit ID is generally useful, but I don't see the value and\nI associate persistent IDs with online tracking and advertisements,\nwhich are neither useful to me nor particularly ethical.  Since nobody\nhas explained the compelling reasons in documentation, I am left to\nspeculate on them myself and have come up empty.\n\n> * There is not consenus on what precise format that should take\n\nI think stabilizing this before a format is defined is a mistake.\n\nEven if, for the sake of argument, we agree that this is a generally\nuseful thing to have, we'd want to have a standard format (ideally\nproduced in a deterministic way for the reproducibility of the testsuite\nand downstream projects), which we don't have, before we persist this.\nWe would probably want to have `git fsck` verify that the format is\ncorrect and this is not being used as a way to store random information\nas part of the initial change.  I assure you that users will very much\ntry to shovel random, arbitrary, malformed information in there\notherwise, since I've seen this in the author and committer headers[0].\n\n> This commit, rather than attempting to standardize the format, simply\n> preserves the change-id header in whatever format it used previously.\n> \n> If we so choose, we can later decide on a standardized format, but since\n> git only preserves existing headers, this should not create backwards\n> incompatibility.\n\nAs I mentioned before in other threads, this needs to be off by default\nor configurable.  This kind of ID provides tracking of commits, which is\nuseful in some situations but may also be undesirable for privacy or\nother reasons.  Unlike other headers in commits, it is not easily\nvisible (one can easily tell if a commit is signed, for instance, or\nwhat its tree is) and so therefore has potential privacy implications.\n\nThis is especially true since historically a great deal of information\nhas been automatically rewritten when rebasing or cherry-picking\n(leaving only author and message alone), so users will have come to\nexpect this.\n\nThis is also a great way to leak information, such as secret keys.  I\ncan shovel sensitive keys or IDs into a commit (in a possibly encrypted\nform), push them somewhere I have access to, and then exploit them.\nNobody will ever notice since corporate firewalls don't actually see the\nraw object information, only the compressed and deltified packfile.  I\ncan even have my colleague rebase my commit with --reset-author and push\nit so I have plausible deniability.\n\nAs an example of a problematic situation, say user A creates a commit\nand publishes it somewhere on a remote.  It doesn't get picked up into\nthe main branch.  A year later, user A changes their name (because they\ntransition, marry, acquire a new citizenship[1], or for any other good\nand valuable reason) and suddenly go by the name B.  Six months later,\nthey rebase the patch on the current main branch and, because the\nproject has advanced quite a bit, it looks completely different (so `git\ncherry` will no longer identify it in any meaningful way).  They adjust\nthe message substantially due to the change and sign it off as user B\nand submit it.\n\nThe user in this case may not have wanted the two commits to be\nassociated (very especially so if they transitioned), so this poses a\nsubstantial risk of unintended disclosure.  The fact that Git makes this\na problem already is not a good excuse for making it worse here; to the\ncontrary, we should be making the situation better, not piling on.\n\n[0] For instance, some people want to provide timestamps that are larger\nthan 2^64, despite the fact that it is remarkably unlikely that humans\nwill still exist 5×10^11 years in the future, let alone that Git will\nstill be in use.  Unsurprisingly, most programming languages don't\nappreciate these timestamps, so problems ensue.\n[1] Some countries require that citizens have a name which can decline\ngrammatically in the native language or otherwise meets linguistic or\ncultural norms in that country.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"}]}