git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] rebase -i: introduce `pick -x` to add "cherry picked from commit ..."

From
TGTrevor Gross <tg@trevorgross.com>
Date
Jul 5, 2026, 14:09 UTC
Message-ID
<20260705140931.98262-2-tg@trevorgross.com>

It is sometimes useful to do cherry picks via rebases when there is a sequence of picks or other git operations to combine. However, there is no interactive rebase equivalent to the cherry-pick `-x` flag, which adds a line to the commit body indicating the original commit.

Using `exec git cherry-pick ... -x` does work, but is not as nice because it interrupts rebase flow; after resolving a conflict, both `git cherry-pick --continue` and `git rebase --continue` must be run.

To improve this, introduce `-x` to the pick, reword, and edit todo rebase commands. This uses the same logic as cherry-pick to add a "(cherry picked from commit ...)" note to the commit body.

Of note is that rebase will fastforward wherever possible, meaning the check for TODO_RECORD_ORIGIN doesn't get hit and the message will not get amended. This differs from the cherry-pick logic, which will add "cherry picked from ..." even if a rewrite isn't otherwise necessary.

Signed-off-by: Trevor Gross <tg@trevorgross.com>
---
Link to PR with the CI runs: https://github.com/git/git/pull/2194
 Documentation/git-rebase.adoc | 16 +++++++++++++
 rebase-interactive.c          |  9 +++++---
 sequencer.c                   | 19 ++++++++++++++--
 t/t3404-rebase-interactive.sh | 42 +++++++++++++++++++++++++++++++++++
 4 files changed, 81 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
index f6c22d1598..d8a8e2c2d6 100644
--- a/Documentation/git-rebase.adoc
+++ b/Documentation/git-rebase.adoc
@@ -978,6 +978,22 @@ pick f4593f9 four
 exec make test
 --------------------

+Similar to `git cherry-pick`, `-x` can be specified to append a "(cherry
+picked from commit …​)" line to the commit body if the the commit base
+changes. That is, the following todo list:
+
+--------------
+pick 123456 -x
+edit 654321 -x
+--------------
+
+acts the same as:
+
+---------------------------
+$ git cherry-pick -x 123456
+$ git cherry-pick -xe 654321
+---------------------------
+
 SPLITTING COMMITS
 -----------------

diff --git a/rebase-interactive.c b/rebase-interactive.c
index 809f76a87b..6a86ab5a94 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -47,9 +47,9 @@ void append_todo_help(int command_count,
 		      struct strbuf *buf)
 {
 	const char *msg = _("\nCommands:\n"
-"p, pick <commit> = use commit\n"
-"r, reword <commit> = use commit, but edit the commit message\n"
-"e, edit <commit> = use commit, but stop for amending\n"
+"p, pick   [ -x ] <commit> = use commit\n"
+"r, reword [ -x ] <commit> = use commit, but edit the commit message\n"
+"e, edit   [ -x ] <commit> = use commit, but stop for amending\n"
 "s, squash <commit> = use commit, but meld into previous commit\n"
 "f, fixup [-C | -c] <commit> = like \"squash\" but keep only the previous\n"
 "                   commit's log message, unless -C is used, in which case\n"
@@ -68,6 +68,9 @@ void append_todo_help(int command_count,
 "                      to this position in the new commits. The <ref> is\n"
 "                      updated at the end of the rebase\n"
 "\n"
+"With pick, reword, or edit, -x will append a line that says \"(cherry\n"
+"picked from commit <sha>)\", similar to git-cherry-pick."
+"\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
 	unsigned edit_todo = !(shortrevisions && shortonto);

diff --git a/sequencer.c b/sequencer.c
index 57855b0066..fde09dd77d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1884,6 +1884,7 @@ enum todo_item_flags {
 	TODO_EDIT_MERGE_MSG    = (1 << 0),
 	TODO_REPLACE_FIXUP_MSG = (1 << 1),
 	TODO_EDIT_FIXUP_MSG    = (1 << 2),
+	TODO_RECORD_ORIGIN     = (1 << 3),
 };

 static const char first_commit_msg_str[] = N_("This is the 1st commit message:");
@@ -2390,7 +2391,7 @@ static int do_pick_commit(struct repository *r,
 		if (find_commit_subject(msg.message, &p))
 			strbuf_addstr(&ctx->message, p);

-		if (opts->record_origin) {
+		if (opts->record_origin || (item->flags & TODO_RECORD_ORIGIN)) {
 			strbuf_complete_line(&ctx->message);
 			if (!has_conforming_footer(&ctx->message, NULL, 0))
 				strbuf_addch(&ctx->message, '\n');
@@ -2758,6 +2759,14 @@ static int parse_insn_line(struct repository *r, struct replay_opts *opts,
 		return error(_("missing arguments for %s"),
 			     command_to_string(item->command));

+	if (item->command == TODO_PICK || item->command == TODO_REWORD ||
+	    item->command == TODO_EDIT) {
+		if (skip_prefix(bol, "-x", &bol)) {
+			bol += strspn(bol, " \t");
+			item->flags |= TODO_RECORD_ORIGIN;
+		}
+	}
+
 	if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
 	    item->command == TODO_RESET || item->command == TODO_UPDATE_REF) {
 		int ret = 0;
@@ -5524,7 +5533,7 @@ static int single_pick(struct repository *r,
 		       struct replay_opts *opts)
 {
 	int check_todo;
-	struct todo_item item;
+	struct todo_item item = { 0 };

 	item.command = opts->action == REPLAY_PICK ?
 			TODO_PICK : TODO_REVERT;
@@ -6340,6 +6349,12 @@ static void todo_list_to_strbuf(struct repository *r,
 					  short_commit_name(r, item->commit) :
 					  oid_to_hex(&item->commit->object.oid);

+			if (item->command == TODO_PICK || item->command == TODO_EDIT ||
+			    item->command == TODO_REWORD) {
+				if (item->flags & TODO_RECORD_ORIGIN)
+					strbuf_addstr(buf, " -x");
+			}
+
 			if (item->command == TODO_FIXUP) {
 				if (item->flags & TODO_EDIT_FIXUP_MSG)
 					strbuf_addstr(buf, " -c");
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 58b3bb0c27..3ff86ebaae 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -2337,6 +2337,48 @@ test_expect_success 'non-merge commands reject merge commits' '
 	test_cmp expect actual
 '

+
+test_expect_success 'rebase -i with pick -x' '
+	git checkout A &&
+	orig_j="$(git rev-parse J)" &&
+	orig_k="$(git rev-parse K)" &&
+	orig_l="$(git rev-parse L)" &&
+	cat >fake-todo <<-EOF &&
+	# No message since this is a fastforward
+	pick -x F
+	# The rest should get the "cherry picked from " message
+	pick -x J
+	reword -x K
+	edit -x L
+	EOF
+	(
+		set_replace_editor fake-todo &&
+		git rebase -i HEAD
+	) &&
+	git log --format="---%n%s%n%b" >actual &&
+	cat >expect <<-EOF &&
+	---
+	L
+	(cherry picked from commit $orig_l)
+
+	---
+	K
+	(cherry picked from commit $orig_k)
+
+	---
+	J
+	(cherry picked from commit $orig_j)
+
+	---
+	F
+
+	---
+	A
+
+	EOF
+	test_cmp expect actual
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
 	test_editor_unchanged
--
2.50.1 (Apple Git-155)
Next: Junio C Hamano
Message 1 of 8 in “rebase -i: introduce `pick -x` to add "cherry picked from commit ..."”
  1. rebase -i: introduce `pick -x` to add "cherry picked from commit ..."Trevor Gross, Jul 5, 2026
  2. Junio C HamanoJul 5, 2026
  3. Matt HunterJul 5, 2026
  4. Junio C HamanoJul 5, 2026
  5. Jeff KingJul 6, 2026
  6. Phillip WoodJul 6, 2026
  7. Junio C HamanoJul 6, 2026
  8. Jeff KingJul 7, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.