Subject: [PATCH v6 0/3] replay: make atomic ref updates the default

From: Siddharth Asthana <[email-masked]>
Date: Fri, 31 Oct 2025 00:49:28 +0530

This is v6 of the git-replay atomic updates series.

This version addresses Christian's feedback from v5 regarding code
consistency and test patterns. Thanks to Christian, Junio, Phillip,
Elijah, Patrick, and Karthik for the thorough reviews.

## Changes in v6

**Fixed parameter naming inconsistency**

Christian pointed out that parse_ref_action_mode() used `mode_str` as the
parameter name while the rest of the code used `ref_action`. Changed to
use `ref_action` consistently throughout for better code readability.

**Improved test cleanup pattern**

Replaced manual `git config --unset` with `test_when_finished` pattern with
`test_config` helper in the replay.refAction config test. The test_config
helper automatically handles cleanup via test_when_finished, providing
better test isolation and following Git test suite best practices.

These are code quality improvements that don't change functionality but
make the code more consistent with Git's established patterns.

## Technical Implementation

Same as v5, using Git's ref transaction API:

- ref_store_transaction_begin() with default atomic behavior
- ref_transaction_update() to stage each update
- ref_transaction_commit() for atomic application

The helper functions provide clean separation:

- parse_ref_action_mode(): Validates strings and converts to enum
- get_ref_action_mode(): Implements command-line > config > default precedence
- handle_ref_update(): Uses type-safe enum with switch statement

Config reading uses repo_config_get_string_tmp() for simplicity while
maintaining proper precedence behavior.

## Testing

All tests pass:

- t3650-replay-basics.sh (20 tests pass)
- Config tests now use test_config for automatic cleanup
- Atomic behavior tests verify direct ref updates
- Backward compatibility maintained for pipeline workflow

CI results: https://gitlab.com/gitlab-org/git/-/pipelines/2130504045

Siddharth Asthana (3):
  replay: use die_for_incompatible_opt2() for option validation
  replay: make atomic ref updates the default behavior
  replay: add replay.refAction config option

 Documentation/config/replay.adoc |  11 +++
 Documentation/git-replay.adoc    |  65 +++++++++++------
 builtin/replay.c                 | 121 +++++++++++++++++++++++++++----
 t/t3650-replay-basics.sh         |  90 +++++++++++++++++++++--
 4 files changed, 244 insertions(+), 43 deletions(-)
 create mode 100644 Documentation/config/replay.adoc

Range-diff against v5:
1:  3e27d07d3b = 1:  1f0fad0cac replay: use die_for_incompatible_opt2() for option validation
2:  643d9ca86a = 2:  bfc6188234 replay: make atomic ref updates the default behavior
3:  334da71911 ! 3:  6b2a44c72c replay: add replay.refAction config option
    @@ Metadata
     Author: Siddharth Asthana <[email-masked]>
     
      ## Commit message ##
         replay: add replay.refAction config option
     
         [Commit message unchanged]
     
      ## builtin/replay.c ##
     @@ builtin/replay.c: static struct commit *pick_regular_commit
      	return create_commit(repo, result->tree, pickme, replayed_base);
      }
      
    -+static enum ref_action_mode parse_ref_action_mode(const char *mode_str, const char *source)
    ++static enum ref_action_mode parse_ref_action_mode(const char *ref_action, const char *source)
     +{
    -+	if (!mode_str || !strcmp(mode_str, "update"))
    ++	if (!ref_action || !strcmp(ref_action, "update"))
     +		return REF_ACTION_UPDATE;
    -+	if (!strcmp(mode_str, "print"))
    ++	if (!strcmp(ref_action, "print"))
     +		return REF_ACTION_PRINT;
    -+	die(_("invalid %s value: '%s'"), source, mode_str);
    ++	die(_("invalid %s value: '%s'"), source, ref_action);
     +}
     +
     +static enum ref_action_mode get_ref_action_mode(struct repository *repo, const char *ref_action_str)
     
      ## t/t3650-replay-basics.sh ##
     @@ t/t3650-replay-basics.sh
     +test_expect_success 'replay.refAction config option' '
     +	START=$(git rev-parse topic2) &&
     +	test_when_finished "git branch -f topic2 $START" &&
    -+	test_when_finished "git config --unset replay.refAction || true" &&
     +
    -+	git config replay.refAction print &&
    ++	test_config replay.refAction print &&
     +	git replay --onto main topic1..topic2 >output &&
     +	test_line_count = 1 output &&
     +	test_grep "^update refs/heads/topic2 " output &&
     +
     +	git branch -f topic2 $START &&
    -+	git config replay.refAction update &&
    ++	test_config replay.refAction update &&
     +	git replay --onto main topic1..topic2 >output &&
     
     +test_expect_success 'command-line --ref-action overrides config' '
-- 
2.51.0

base-commit: 57da342c78d8bf00259d2b720292e5b3035dadcc

Thanks
- Siddharth