From: Trevor Gross <tg@trevorgross.com>
To: git@vger.kernel.org
Cc: Trevor Gross <tg@trevorgross.com>, Jeff King <peff@peff.net>,
Junio C Hamano <gitster@pobox.com>,
Stefan Haller <lists@haller-berlin.de>,
Derrick Stolee <stolee@gmail.com>,
Phillip Wood <phillip.wood@dunelm.org.uk>
Subject: [PATCH] rebase -i: introduce `pick -x` to add "cherry picked from commit ..."
Date: Sun, 5 Jul 2026 14:09:06 +0000 [thread overview]
Message-ID: <20260705140931.98262-2-tg@trevorgross.com> (raw)
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 reply other threads:[~2026-07-05 14:15 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-05 14:09 Trevor Gross [this message]
2026-07-05 18:58 ` [PATCH] rebase -i: introduce `pick -x` to add "cherry picked from commit ..." Junio C Hamano
2026-07-05 22:23 ` Matt Hunter
2026-07-05 20:52 ` Junio C Hamano
2026-07-06 0:24 ` Jeff King
2026-07-06 10:08 ` Phillip Wood
2026-07-06 20:24 ` Junio C Hamano
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260705140931.98262-2-tg@trevorgross.com \
--to=tg@trevorgross.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=lists@haller-berlin.de \
--cc=peff@peff.net \
--cc=phillip.wood@dunelm.org.uk \
--cc=stolee@gmail.com \
/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.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox