git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Matt Stark <msta@google.com>
To: git@vger.kernel.org
Cc: ps@pks.im, gitster@pobox.com, phillip.wood@dunelm.org.uk,
	 Martin von Zweigbergk <martinvonz@google.com>,
	remo@buenzli.dev, Edwin Kempin <ekempin@google.com>,
	 schacon@gmail.com, philipmetzger@bluewin.ch,
	konstantin@linuxfoundation.org,  newren@gmail.com, tytso@mit.edu,
	nico@cryptonector.com,  rikingcoding@gmail.com,
	Matt Stark <msta@google.com>
Subject: [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick.
Date: Tue, 7 Apr 2026 13:13:18 +1000	[thread overview]
Message-ID: <CAH7WC73-4p0RrqKNSh2G-xfpfO7QHZiXHbU_UFRkM3Q=bMWTDw@mail.gmail.com> (raw)

In the discussions on
https://lore.kernel.org/git/Z_OGMb-1oV0Ex05e@pks.im/T/#m038be849b9b4020c16c562d810cf77bad91a2c87,
it seems to be that:
* There is consensus that a `change-id` header provides good value
* There is not consenus on what precise format that should take

This commit, rather than attempting to standardize the format, simply
preserves the change-id header in whatever format it used previously.

If we so choose, we can later decide on a standardized format, but since
git only preserves existing headers, this should not create backwards
incompatibility.

Signed-off-by: Matt Stark <msta@google.com>
---
 sequencer.c                           | 39 ++++++++++++++++++++++-----
 t/t3400-rebase.sh                     | 20 ++++++++++++++
 t/t3501-revert-cherry-pick.sh         | 15 +++++++++++
 t/t7501-commit-basic-functionality.sh | 15 +++++++++++
 4 files changed, 83 insertions(+), 6 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index b7d8dca47f..093d47d42a 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1530,12 +1530,12 @@ static int try_to_commit(struct repository *r,
  struct strbuf *msg, const char *author,
  const char *reflog_action,
  struct replay_opts *opts, unsigned int flags,
- struct object_id *oid)
+ struct object_id *oid,
+ struct commit_extra_header *extra)
 {
  struct object_id tree;
  struct commit *current_head = NULL;
  struct commit_list *parents = NULL;
- struct commit_extra_header *extra = NULL;
  struct strbuf err = STRBUF_INIT;
  struct strbuf commit_msg = STRBUF_INIT;
  char *amend_author = NULL;
@@ -1721,7 +1721,8 @@ static int do_commit(struct repository *r,
       const char *msg_file, const char *author,
       const char *reflog_action,
       struct replay_opts *opts, unsigned int flags,
-      struct object_id *oid)
+      struct object_id *oid,
+      struct commit_extra_header *extra_headers)
 {
  int res = 1;

@@ -1735,7 +1736,7 @@ static int do_commit(struct repository *r,
     msg_file);

  res = try_to_commit(r, msg_file ? &sb : NULL,
-     author, reflog_action, opts, flags, &oid);
+     author, reflog_action, opts, flags, &oid, extra_headers);
  strbuf_release(&sb);
  if (!res) {
  refs_delete_ref(get_main_ref_store(r), "",
@@ -2511,10 +2512,36 @@ static int do_pick_commit(struct repository *r,
  oid_to_hex(&commit->object.oid), msg.subject);
  } /* else allow == 0 and there's nothing special to do */
  if (!opts->no_commit && !drop_commit) {
- if (author || command == TODO_REVERT || (flags & AMEND_MSG))
+ if (author || command == TODO_REVERT || (flags & AMEND_MSG)) {
+ struct commit_extra_header *extra_headers = NULL;
+ if (commit) {
+ unsigned long size;
+ const char *buffer = repo_get_commit_buffer(r, commit, &size);
+ size_t out_len;
+ // The Gerrit, GitButler, and Jujutsu projects all have a concept of
+ // a "change id", and it behaves in a similar way between the three
+ // tools. The change id is conceptually associated with a commit.
+ // It follows a commit as its rewritten (e.g. by amending and
+ // rebasing).
+ // While git doesn't add this header itself, and currently has no plans
+ // to do so, there is consensus that if the header is added by another
+ // tool, git should at least preserve it.
+ const char *header_value = find_commit_header(buffer, "change-id", &out_len);
+ if (header_value) {
+ extra_headers = xmalloc(sizeof(*extra_headers));
+ *extra_headers = (struct commit_extra_header){
+ .next = NULL,
+ .key = xstrdup("change-id"),
+ .value = xmemdupz(header_value, out_len),
+ .len = out_len
+ };
+ }
+ repo_unuse_commit_buffer(r, commit, buffer);
+ }
  res = do_commit(r, msg_file, author, reflog_action,
  opts, flags,
- commit? &commit->object.oid : NULL);
+ commit ? &commit->object.oid : NULL, extra_headers);
+ }
  else
  res = error(_("unable to parse commit author"));
  *check_todo = !!(flags & EDIT_MSG);
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index c0c00fbb7b..6b5d6fe56f 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -474,4 +474,24 @@ test_expect_success 'git rebase --update-ref with
core.commentChar and branch on
  test_grep "% Ref refs/heads/topic2 checked out at" actual
 '

+test_expect_success 'rebase preserves change-id header' '
+ test_commit "source-for-rebase" file-rebase content-rebase &&
+ git cat-file commit HEAD >commit_obj &&
+ awk "/^committer / { print; print \"change-id my-change-id\"; next
}1" commit_obj >commit_obj_mod &&
+ new_commit=$(git hash-object -t commit -w commit_obj_mod) &&
+ git branch -f source-branch $new_commit &&
+
+ git checkout -b target-branch HEAD^ &&
+ echo "unrelated" >file-unrelated &&
+ git add file-unrelated &&
+ git commit -m "unrelated" &&
+
+ git checkout source-branch &&
+ git rebase target-branch &&
+
+ git cat-file commit HEAD >result_obj &&
+ grep "^change-id my-change-id$" result_obj
+'
+
 test_done
+
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
index 8025a28cfd..0ada99f216 100755
--- a/t/t3501-revert-cherry-pick.sh
+++ b/t/t3501-revert-cherry-pick.sh
@@ -256,4 +256,19 @@ test_expect_success 'cherry-pick is unaware of
--reference (for now)' '
  grep "^usage: git cherry-pick" actual
 '

+test_expect_success 'cherry-pick preserves change-id header' '
+ test_commit "source-for-cherry" file-cherry content-cherry &&
+ git cat-file commit HEAD >commit_obj &&
+ awk "/^committer / { print; print \"change-id my-change-id\"; next
}1" commit_obj >commit_obj_mod &&
+ new_commit=$(git hash-object -t commit -w commit_obj_mod) &&
+ git branch -f source-branch $new_commit &&
+
+ git checkout -b target-branch HEAD^ &&
+ git cherry-pick source-branch &&
+
+ git cat-file commit HEAD >result_obj &&
+ grep "^change-id my-change-id$" result_obj
+'
+
 test_done
+
diff --git a/t/t7501-commit-basic-functionality.sh
b/t/t7501-commit-basic-functionality.sh
index a37509f004..e25dd9dc6f 100755
--- a/t/t7501-commit-basic-functionality.sh
+++ b/t/t7501-commit-basic-functionality.sh
@@ -793,4 +793,19 @@ test_expect_success '--dry-run --short' '
  git commit --dry-run --short
 '

+test_expect_success 'amend preserves change-id header' '
+ test_commit "source-for-amend" file-amend content-amend &&
+ git cat-file commit HEAD >commit_obj &&
+ awk "/^committer / { print; print \"change-id my-change-id\"; next
}1" commit_obj >commit_obj_mod &&
+ new_commit=$(git hash-object -t commit -w commit_obj_mod) &&
+ git reset --hard $new_commit &&
+
+ echo "amended content" >>file-amend &&
+ git add file-amend &&
+ git commit --amend --no-edit &&
+
+ git cat-file commit HEAD >result_obj &&
+ grep "^change-id my-change-id$" result_obj
+'
+
 test_done
-- 
2.53.0.1213.gd9a14994de-goog


             reply	other threads:[~2026-04-07  3:13 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-07  3:13 Matt Stark [this message]
2026-04-07  4:09 ` [PATCH] headers: Preserve 'change-id' header in rebase / cherry-pick Junio C Hamano
2026-04-07  4:58   ` Nico Williams
2026-04-07  5:02     ` Nico Williams
2026-04-07 14:33       ` Junio C Hamano
2026-04-07  9:55     ` Phillip Wood
2026-04-07 15:52       ` Nico Williams
2026-04-07 16:20         ` Junio C Hamano
2026-04-07 20:13           ` Nico Williams
2026-04-07 14:42     ` Junio C Hamano
2026-04-07  9:41   ` Phillip Wood
2026-04-07 23:28 ` brian m. carlson

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

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAH7WC73-4p0RrqKNSh2G-xfpfO7QHZiXHbU_UFRkM3Q=bMWTDw@mail.gmail.com' \
    --to=msta@google.com \
    --cc=ekempin@google.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=konstantin@linuxfoundation.org \
    --cc=martinvonz@google.com \
    --cc=newren@gmail.com \
    --cc=nico@cryptonector.com \
    --cc=philipmetzger@bluewin.ch \
    --cc=phillip.wood@dunelm.org.uk \
    --cc=ps@pks.im \
    --cc=remo@buenzli.dev \
    --cc=rikingcoding@gmail.com \
    --cc=schacon@gmail.com \
    --cc=tytso@mit.edu \
    /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.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).