From: Siddharth Asthana <siddharthasthana31@gmail.com>
To: git@vger.kernel.org
Cc: christian.couder@gmail.com, ps@pks.im, newren@gmail.com,
gitster@pobox.com, phillip.wood123@gmail.com,
karthik.188@gmail.com, johannes.schindelin@gmx.de,
toon@iotcl.com, Siddharth Asthana <siddharthasthana31@gmail.com>
Subject: [PATCH v5 0/2] replay: add --revert mode to reverse commit changes
Date: Wed, 25 Mar 2026 03:33:59 +0530 [thread overview]
Message-ID: <20260324220401.47040-1-siddharthasthana31@gmail.com> (raw)
In-Reply-To: <20260313054035.26605-1-siddharthasthana31@gmail.com>
Hi,
git replay currently supports cherry-picking (--advance) and rebasing
(--onto), but not reverting. We need this at GitLab for Gitaly to
reverse commits directly on bare repositories without a checkout.
The approach is the same as sequencer.c -- cherry-pick and revert are
just the same three-way merge with swapped arguments. We swap the base
and pickme trees passed to merge_incore_nonrecursive() to reverse the
diff direction.
Patch 1 extracts the full revert message formatting logic into a new
sequencer_format_revert_message() function that handles everything in
one shared function rather than just the header. refer_to_commit() is
updated to take a struct repository and a bool instead of replay_opts
so it works outside the sequencer.
Patch 2 adds --revert <branch> as a standalone mode. Reverts are
processed newest-first (matching git revert) to reduce conflicts by
peeling off changes from the top.
The series is based on top of d181b9354c (The 13th batch, 2026-03-07).
Changes in v5:
- Made sequencer_format_revert_message() header comment more concise,
using single quotes to avoid nested escaped double quotes
- Moved desired_reverse declaration up and removed the bare braces, so
the same named variable is used when setting and checking
revs.reverse
- Used die_for_incompatible_opt2() for --advance/--contained and
--revert/--contained instead of die("--contained requires --onto")
- Dropped the erroneously re-added ellipsis from <revision-range>...
in the SYNOPSIS
- Restored #define the_repository DO_NOT_USE_THE_REPOSITORY guard in
replay.c
- Used ${SQ} instead of '"'"' for single-quote escaping in tests
- Link to v4: https://lore.kernel.org/git/20260313054035.26605-1-siddharthasthana31@gmail.com/
- Link to v3: https://public-inbox.org/git/20260218234215.89326-1-siddharthasthana31@gmail.com/
- Link to v2: https://public-inbox.org/git/20251202201611.22137-1-siddharthasthana31@gmail.com/
- Link to v1: https://public-inbox.org/git/20251125170056.34489-1-siddharthasthana31@gmail.com/
Thanks,
Siddharth
---
Siddharth Asthana (2):
sequencer: extract revert message formatting into shared function
replay: add --revert mode to reverse commit changes
Documentation/git-replay.adoc | 43 ++++++++-
builtin/replay.c | 35 ++++++--
replay.c | 161 +++++++++++++++++++++++++---------
replay.h | 11 ++-
sequencer.c | 78 +++++++++-------
sequencer.h | 13 +++
t/t3650-replay-basics.sh | 111 +++++++++++++++++++++--
7 files changed, 355 insertions(+), 97 deletions(-)
Range-diff versus v4:
1: bdc710b265 ! 1: 6bd2ce4515 sequencer: extract revert message formatting into shared function
@@ sequencer.h: int sequencer_determine_whence(struct repository *r, enum commit_wh
int sequencer_get_update_refs_state(const char *wt_dir, struct string_list *refs);
+/*
-+ * Formats a complete revert commit message following standard Git conventions.
-+ * Handles regular reverts ("Revert \"<subject>\""), revert of revert cases
-+ * ("Reapply \"<subject>\""), and the --reference style. Appends "This reverts
-+ * commit <ref>." using either the abbreviated or full commit reference
-+ * depending on use_commit_reference. Also handles merge-parent references.
++ * Format a revert commit message with appropriate 'Revert "<subject>"' or
++ * 'Reapply "<subject>"' prefix and 'This reverts commit <ref>.' body.
++ * When use_commit_reference is set, <ref> is an abbreviated hash with
++ * subject and date; otherwise the full hex hash is used.
+ */
+void sequencer_format_revert_message(struct repository *r,
+ const char *subject,
2: bea6229575 ! 2: 9fd92497b9 replay: add --revert mode to reverse commit changes
@@ Documentation/git-replay.adoc: git-replay - EXPERIMENTAL: Replay commits on a ne
--------
[verse]
-(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) [--ref-action[=<mode>]] <revision-range>
-+(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range>...
++(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range>
DESCRIPTION
-----------
@@ builtin/replay.c: int cmd_replay(int argc,
const char *const replay_usage[] = {
N_("(EXPERIMENTAL!) git replay "
- "([--contained] --onto <newbase> | --advance <branch>) "
-- "[--ref-action[=<mode>]] <revision-range>"),
+ "([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) "
-+ "[--ref-action[=<mode>]] <revision-range>..."),
+ "[--ref-action[=<mode>]] <revision-range>"),
NULL
};
- struct option replay_options[] = {
@@ builtin/replay.c: int cmd_replay(int argc,
N_("replay onto given commit")),
OPT_BOOL(0, "contained", &opts.contained,
@@ builtin/replay.c: int cmd_replay(int argc,
usage_with_options(replay_usage, replay_options);
}
-- die_for_incompatible_opt2(!!opts.advance, "--advance",
-- opts.contained, "--contained");
-- die_for_incompatible_opt2(!!opts.advance, "--advance",
-- !!opts.onto, "--onto");
+ die_for_incompatible_opt3(!!opts.onto, "--onto",
+ !!opts.advance, "--advance",
+ !!opts.revert, "--revert");
-+ if (opts.contained && !opts.onto)
-+ die(_("--contained requires --onto"));
+ die_for_incompatible_opt2(!!opts.advance, "--advance",
+ opts.contained, "--contained");
+- die_for_incompatible_opt2(!!opts.advance, "--advance",
+- !!opts.onto, "--onto");
++ die_for_incompatible_opt2(!!opts.revert, "--revert",
++ opts.contained, "--contained");
/* Parse ref action mode from command line or config */
ref_mode = get_ref_action_mode(repo, ref_action);
-@@ builtin/replay.c: int cmd_replay(int argc,
- * some options changing these values if we think they could
- * be useful.
- */
-- revs.reverse = 1;
+
+ /*
+ * Cherry-pick/rebase need oldest-first ordering so that each
+ * replayed commit can build on its already-replayed parent.
+ * Revert needs newest-first ordering (like git revert) to
+ * reduce conflicts by peeling off changes from the top.
+ */
-+ revs.reverse = opts.revert ? 0 : 1;
++ int desired_reverse = !opts.revert;
++
+ repo_init_revisions(repo, &revs, prefix);
+
+ /*
+@@ builtin/replay.c: int cmd_replay(int argc,
+ * some options changing these values if we think they could
+ * be useful.
+ */
+- revs.reverse = 1;
++ revs.reverse = desired_reverse;
revs.sort_order = REV_SORT_IN_GRAPH_ORDER;
revs.topo_order = 1;
revs.simplify_history = 0;
@@ builtin/replay.c: int cmd_replay(int argc,
* walking options.
*/
- if (revs.reverse != 1) {
-- warning(_("some rev walking options will be overridden as "
-- "'%s' bit in 'struct rev_info' will be forced"),
-- "reverse");
++ if (revs.reverse != desired_reverse) {
+ warning(_("some rev walking options will be overridden as "
+ "'%s' bit in 'struct rev_info' will be forced"),
+ "reverse");
- revs.reverse = 1;
-+ {
-+ int desired_reverse = opts.revert ? 0 : 1;
-+ if (revs.reverse != desired_reverse) {
-+ warning(_("some rev walking options will be overridden as "
-+ "'%s' bit in 'struct rev_info' will be forced"),
-+ "reverse");
-+ revs.reverse = desired_reverse;
-+ }
++ revs.reverse = desired_reverse;
}
if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) {
warning(_("some rev walking options will be overridden as "
@@ replay.c
#include "strmap.h"
#include "tree.h"
--/*
-- * We technically need USE_THE_REPOSITORY_VARIABLE for DEFAULT_ABBREV, but
-- * do not want to use the_repository.
-- */
--#define the_repository DO_NOT_USE_THE_REPOSITORY
+@@
+ */
+ #define the_repository DO_NOT_USE_THE_REPOSITORY
+
+enum replay_mode {
+ REPLAY_MODE_PICK,
+ REPLAY_MODE_REVERT,
+};
-
++
static const char *short_commit_name(struct repository *repo,
struct commit *commit)
+ {
@@ replay.c: static char *get_author(const char *message)
return NULL;
}
@@ t/t3650-replay-basics.sh: test_expect_success 'no base or negative ref gives no-
-test_expect_success 'options --advance and --contained cannot be used together' '
- printf "fatal: options ${SQ}--advance${SQ} " >expect &&
- printf "and ${SQ}--contained${SQ} cannot be used together\n" >>expect &&
-+test_expect_success '--contained requires --onto' '
-+ echo "fatal: --contained requires --onto" >expect &&
++test_expect_success '--advance and --contained cannot be used together' '
test_must_fail git replay --advance=main --contained \
topic1..topic2 2>actual &&
- test_cmp expect actual
+- test_cmp expect actual
++ test_grep "cannot be used together" actual
'
test_expect_success 'cannot advance target ... ordering would be ill-defined' '
- echo "fatal: cannot advance target with multiple sources because ordering would be ill-defined" >expect &&
-+ cat >expect <<-\EOF &&
-+ fatal: '"'"'--advance'"'"' cannot be used with multiple revision ranges because the ordering would be ill-defined
-+ EOF
++ echo "fatal: ${SQ}--advance${SQ} cannot be used with multiple revision ranges because the ordering would be ill-defined" >expect &&
test_must_fail git replay --advance=main main topic1 topic2 2>actual &&
test_cmp expect actual
'
@@ t/t3650-replay-basics.sh: test_expect_success 'invalid replay.refAction value' '
+'
+
+test_expect_success 'cannot revert with multiple sources' '
-+ cat >expect <<-\EOF &&
-+ fatal: '"'"'--revert'"'"' cannot be used with multiple revision ranges because the ordering would be ill-defined
-+ EOF
++ echo "fatal: ${SQ}--revert${SQ} cannot be used with multiple revision ranges because the ordering would be ill-defined" >expect &&
+ test_must_fail git replay --revert main main topic1 topic2 2>actual &&
+ test_cmp expect actual
+'
@@ t/t3650-replay-basics.sh: test_expect_success 'invalid replay.refAction value' '
+
+test_expect_success 'git replay --revert incompatible with --contained' '
+ test_must_fail git replay --revert topic4 --contained topic4~1..topic4 2>error &&
-+ test_grep "requires --onto" error
++ test_grep "cannot be used together" error
+'
+
+test_expect_success 'git replay --revert incompatible with --onto' '
base-commit: d181b9354cf85b44455ce3ca9e6af0b9559e0ae2
--
2.51.0
next prev parent reply other threads:[~2026-03-24 22:04 UTC|newest]
Thread overview: 92+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-25 17:00 [PATCH 0/1] replay: add --revert option to reverse commit changes Siddharth Asthana
2025-11-25 17:00 ` [PATCH 1/1] " Siddharth Asthana
2025-11-25 19:22 ` Junio C Hamano
2025-11-25 19:30 ` Junio C Hamano
2025-11-25 19:39 ` Junio C Hamano
2025-11-25 20:06 ` Junio C Hamano
2025-11-26 19:31 ` Siddharth Asthana
2025-11-26 19:28 ` Siddharth Asthana
2025-11-26 19:26 ` Siddharth Asthana
2025-11-26 21:13 ` Junio C Hamano
2025-11-27 19:23 ` Siddharth Asthana
2025-11-26 11:10 ` Phillip Wood
2025-11-26 17:35 ` Elijah Newren
2025-11-26 18:41 ` Junio C Hamano
2025-11-26 21:17 ` Junio C Hamano
2025-11-26 23:06 ` Elijah Newren
2025-11-26 23:14 ` Junio C Hamano
2025-11-26 23:57 ` Elijah Newren
2025-11-26 19:50 ` Siddharth Asthana
2025-11-26 19:39 ` Siddharth Asthana
2025-11-27 16:21 ` Phillip Wood
2025-11-27 19:24 ` Siddharth Asthana
2025-11-25 17:25 ` [PATCH 0/1] " Johannes Schindelin
2025-11-25 18:02 ` Junio C Hamano
2025-11-26 19:18 ` Siddharth Asthana
2025-11-26 21:04 ` Junio C Hamano
2025-11-27 19:21 ` Siddharth Asthana
2025-11-27 20:17 ` Junio C Hamano
2025-11-28 8:07 ` Elijah Newren
2025-11-28 8:24 ` Siddharth Asthana
2025-11-28 16:35 ` Junio C Hamano
2025-11-28 17:07 ` Elijah Newren
2025-11-28 20:50 ` Junio C Hamano
2025-11-28 22:03 ` Elijah Newren
2025-11-29 5:59 ` Junio C Hamano
2025-12-02 20:16 ` [PATCH v2 0/2] replay: add --revert mode " Siddharth Asthana
2025-12-02 20:16 ` [PATCH v2 1/2] sequencer: extract revert message formatting into shared function Siddharth Asthana
2025-12-05 11:33 ` Patrick Steinhardt
2025-12-07 23:00 ` Siddharth Asthana
2025-12-08 7:07 ` Patrick Steinhardt
2026-02-11 13:03 ` Toon Claes
2026-02-11 13:40 ` Patrick Steinhardt
2026-02-11 15:23 ` Kristoffer Haugsbakk
2026-02-11 17:41 ` Junio C Hamano
2026-02-18 22:53 ` Siddharth Asthana
2025-12-02 20:16 ` [PATCH v2 2/2] replay: add --revert mode to reverse commit changes Siddharth Asthana
2025-12-05 11:33 ` Patrick Steinhardt
2025-12-07 23:03 ` Siddharth Asthana
2025-12-16 16:23 ` Phillip Wood
2026-02-18 23:42 ` [PATCH v3 0/2] " Siddharth Asthana
2026-02-18 23:42 ` [PATCH v3 1/2] sequencer: extract revert message formatting into shared function Siddharth Asthana
2026-02-20 17:01 ` Toon Claes
2026-02-25 21:53 ` Junio C Hamano
2026-03-06 4:55 ` Siddharth Asthana
2026-03-06 4:31 ` Siddharth Asthana
2026-02-26 14:27 ` Phillip Wood
2026-03-06 5:00 ` Siddharth Asthana
2026-02-18 23:42 ` [PATCH v3 2/2] replay: add --revert mode to reverse commit changes Siddharth Asthana
2026-02-20 17:35 ` Toon Claes
2026-02-20 20:23 ` Junio C Hamano
2026-02-23 9:13 ` Christian Couder
2026-02-23 11:23 ` Toon Claes
2026-03-06 5:05 ` Siddharth Asthana
2026-02-26 14:45 ` Phillip Wood
2026-03-06 5:28 ` Siddharth Asthana
2026-03-06 15:52 ` Phillip Wood
2026-03-06 16:20 ` Siddharth Asthana
2026-03-13 5:40 ` [PATCH v4 0/2] " Siddharth Asthana
2026-03-13 5:40 ` [PATCH v4 1/2] sequencer: extract revert message formatting into shared function Siddharth Asthana
2026-03-13 15:53 ` Junio C Hamano
2026-03-16 19:12 ` Toon Claes
2026-03-16 16:57 ` Phillip Wood
2026-03-13 5:40 ` [PATCH v4 2/2] replay: add --revert mode to reverse commit changes Siddharth Asthana
2026-03-16 16:57 ` Phillip Wood
2026-03-16 19:52 ` Toon Claes
2026-03-17 10:11 ` Phillip Wood
2026-03-16 16:59 ` [PATCH v4 0/2] " Phillip Wood
2026-03-16 19:53 ` Toon Claes
2026-03-24 22:03 ` Siddharth Asthana [this message]
2026-03-24 22:04 ` [PATCH v5 1/2] sequencer: extract revert message formatting into shared function Siddharth Asthana
2026-03-24 22:04 ` [PATCH v5 2/2] replay: add --revert mode to reverse commit changes Siddharth Asthana
2026-03-25 6:29 ` Junio C Hamano
2026-03-25 15:10 ` Toon Claes
2026-03-25 15:38 ` Siddharth Asthana
2026-03-25 16:44 ` Phillip Wood
2026-03-25 15:36 ` Siddharth Asthana
2026-03-25 20:23 ` [PATCH v6 0/2] " Siddharth Asthana
2026-03-25 20:23 ` [PATCH v6 1/2] sequencer: extract revert message formatting into shared function Siddharth Asthana
2026-03-25 20:23 ` [PATCH v6 2/2] replay: add --revert mode to reverse commit changes Siddharth Asthana
2026-03-28 4:33 ` Tian Yuchen
2026-03-25 20:23 ` [PATCH v6 1/2] sequencer: extract revert message formatting into shared function Siddharth Asthana
2026-03-25 20:23 ` [PATCH v6 2/2] replay: add --revert mode to reverse commit changes Siddharth Asthana
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=20260324220401.47040-1-siddharthasthana31@gmail.com \
--to=siddharthasthana31@gmail.com \
--cc=christian.couder@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=johannes.schindelin@gmx.de \
--cc=karthik.188@gmail.com \
--cc=newren@gmail.com \
--cc=phillip.wood123@gmail.com \
--cc=ps@pks.im \
--cc=toon@iotcl.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