replay: add --revert option to reverse commit changes
Hello
This MR adds revert functionality to git replay, enabling it to reverse commit changes in addition to cherry-picking them. This addresses GitLab issue #233 and complements the existing replay capabilities.
Current Limitation
The git replay command currently only supports replaying (cherry-picking) commits onto a new base. However, it cannot revert commits, which is a complementary operation that's quite similar. For server-side tools like Gitaly that plan to use git replay, having both forward (cherry-pick) and reverse (revert) operations through a single command would simplify their codebase significantly.
Our implementation
This patch introduces a --revert option that reverses the diff direction when replaying commits:
Cherry-pick commits (default behavior)
git replay --onto main feature~3..feature
Revert commits (new functionality)
git replay --revert --onto main feature~3..feature### Implementation Approach
The key insight from analyzing sequencer.c is that cherry-pick and revert are essentially the same merge operation but with swapped arguments:
-
Cherry-pick:
merge(parent, HEAD, commit)- applies the diff -
Revert:
merge(commit, HEAD, parent)- reverses the diff
By swapping the base and pickme trees in merge_incore_nonrecursive(), we effectively reverse the diff direction. This approach:
- Reuses all existing infrastructure (merge-ort backend, conflict handling, ref updates)
- Requires minimal code changes (~100 lines in
builtin/replay.c) - Works seamlessly with bare repositories (critical for server-side use)
- Generates appropriate commit messages:
- Regular revert:
Revert "Original subject" - Revert of revert:
Reapply "Original subject" - Includes the SHA of the commit being reverted
- Regular revert:
Compatibility
The --revert option is incompatible with --contained, as reverting multiple branches simultaneously would be semantically unclear. This is enforced using die_for_incompatible_opt2().
Testing
- 9 new tests added covering:
- Basic revert with
--ontoand--advancemodes - Bare repository support
- Revert of a revert (generates "Reapply" message)
- Conflict handling
- Multiple reverts in a single operation
- Basic revert with
Benefits for Gitaly
This feature will help simplify server-side code in tools like Gitaly by providing:
- Unified interface for both cherry-pick and revert operations
- No working tree required
- Efficient merge-ort backend
- Works in bare repositories out of the box