replay: add replay.refAction config option

Add a configuration variable to control the default behavior of git replay for updating references. This allows users who prefer the traditional pipeline output to set it once in their config instead of passing --ref-action=print with every command. The config variable uses string values that mirror the behavior modes: * replay.refAction = update (default): atomic ref updates * replay.refAction = print: output commands for pipeline Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Elijah Newren <newren@gmail.com> Helped-by: Christian Couder <christian.couder@gmail.com> Helped-by: Phillip Wood <phillip.wood123@gmail.com> Signed-off-by: Siddharth Asthana <siddharthasthana31@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Siddharth Asthana committed Nov 6, 2025 at 00:46 UTC 336ac90c06ec757f613faae4ffc6c32578a99cd1
4 files changed +79 -4
Documentation/config/replay.adoc new
+11
@@ -0,0 +1,11 @@
1 +replay.refAction::
2 + Specifies the default mode for handling reference updates in
3 + `git replay`. The value can be:
4 ++
5 +--
6 + * `update`: Update refs directly using an atomic transaction (default behavior).
7 + * `print`: Output update-ref commands for pipeline use.
8 +--
9 ++
10 +This setting can be overridden with the `--ref-action` command-line option.
11 +When not configured, `git replay` defaults to `update` mode.
Documentation/git-replay.adoc
+2
@@ -51,6 +51,8 @@ which uses the target only as a starting point without updating it.
51 * `print`: Output update-ref commands for pipeline use. This is the
52 traditional behavior where output can be piped to `git update-ref --stdin`.
53 --
54 ++
55 +The default mode can be configured via the `replay.refAction` configuration variable.
56
57 <revision-range>::
58 Range of commits to replay. More than one <revision-range> can
builtin/replay.c
+20 -4
@@ -8,6 +8,7 @@
8 #include "git-compat-util.h"
9
10 #include "builtin.h"
11 +#include "config.h"
12 #include "environment.h"
13 #include "hex.h"
14 #include "lockfile.h"
@@ -298,6 +299,22 @@ static enum ref_action_mode parse_ref_action_mode(const char *ref_action, const
299 die(_("invalid %s value: '%s'"), source, ref_action);
300 }
301
302 +static enum ref_action_mode get_ref_action_mode(struct repository *repo, const char *ref_action)
303 +{
304 + const char *config_value = NULL;
305 +
306 + /* Command line option takes precedence */
307 + if (ref_action)
308 + return parse_ref_action_mode(ref_action, "--ref-action");
309 +
310 + /* Check config value */
311 + if (!repo_config_get_string_tmp(repo, "replay.refAction", &config_value))
312 + return parse_ref_action_mode(config_value, "replay.refAction");
313 +
314 + /* Default to update mode */
315 + return REF_ACTION_UPDATE;
316 +}
317 +
318 static int handle_ref_update(enum ref_action_mode mode,
319 struct ref_transaction *transaction,
320 const char *refname,
@@ -332,7 +349,7 @@ int cmd_replay(int argc,
349 const char *onto_name = NULL;
350 int contained = 0;
351 const char *ref_action = NULL;
335 - enum ref_action_mode ref_mode = REF_ACTION_UPDATE;
352 + enum ref_action_mode ref_mode;
353
354 struct rev_info revs;
355 struct commit *last_commit = NULL;
@@ -378,9 +395,8 @@ int cmd_replay(int argc,
395 die_for_incompatible_opt2(!!advance_name_opt, "--advance",
396 contained, "--contained");
397
381 - /* Parse ref action mode */
382 - if (ref_action)
383 - ref_mode = parse_ref_action_mode(ref_action, "--ref-action");
398 + /* Parse ref action mode from command line or config */
399 + ref_mode = get_ref_action_mode(repo, ref_action);
400
401 advance_name = xstrdup_or_null(advance_name_opt);
402
t/t3650-replay-basics.sh
+46
@@ -268,4 +268,50 @@ test_expect_success 'reflog message for --advance mode' '
268 test_cmp expect-reflog reflog-msg
269 '
270
271 +test_expect_success 'replay.refAction=print config option' '
272 + # Store original state
273 + START=$(git rev-parse topic2) &&
274 + test_when_finished "git branch -f topic2 $START" &&
275 +
276 + # Test with config set to print
277 + test_config replay.refAction print &&
278 + git replay --onto main topic1..topic2 >output &&
279 + test_line_count = 1 output &&
280 + test_grep "^update refs/heads/topic2 " output
281 +'
282 +
283 +test_expect_success 'replay.refAction=update config option' '
284 + # Store original state
285 + START=$(git rev-parse topic2) &&
286 + test_when_finished "git branch -f topic2 $START" &&
287 +
288 + # Test with config set to update
289 + test_config replay.refAction update &&
290 + git replay --onto main topic1..topic2 >output &&
291 + test_must_be_empty output &&
292 +
293 + # Verify ref was updated
294 + git log --format=%s topic2 >actual &&
295 + test_write_lines E D M L B A >expect &&
296 + test_cmp expect actual
297 +'
298 +
299 +test_expect_success 'command-line --ref-action overrides config' '
300 + # Store original state
301 + START=$(git rev-parse topic2) &&
302 + test_when_finished "git branch -f topic2 $START" &&
303 +
304 + # Set config to update but use --ref-action=print
305 + test_config replay.refAction update &&
306 + git replay --ref-action=print --onto main topic1..topic2 >output &&
307 + test_line_count = 1 output &&
308 + test_grep "^update refs/heads/topic2 " output
309 +'
310 +
311 +test_expect_success 'invalid replay.refAction value' '
312 + test_config replay.refAction invalid &&
313 + test_must_fail git replay --onto main topic1..topic2 2>error &&
314 + test_grep "invalid.*replay.refAction.*value" error
315 +'
316 +
317 test_done