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: 8+ 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
2026-07-07 4:27 ` Jeff King
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.