Commit Graph

9 Commits (next)

Author SHA1 Message Date
Junio C Hamano 371c2e9c3b Merge branch 'tc/replay-linearize' into next
The 'git replay' command has been taught the '--linearize' option to
drop merge commits and linearize the replayed history, mimicking 'git
rebase --no-rebase-merges'.

* tc/replay-linearize:
  replay: offer an option to linearize the commit topology
  replay: resolve the replay base outside pick_regular_commit()
  replay: add helper to put entry into replayed_commits
2026-07-09 07:45:16 -07:00
Junio C Hamano 6fb84708a4 Merge branch 'ps/history-drop' into next
The experimental 'git history' command has been taught a new 'drop'
subcommand to remove a commit and replay its descendants onto its
parent.

* ps/history-drop:
  builtin/history: implement "drop" subcommand
  builtin/history: split handling of ref updates into two phases
  replay: expose `replay_result_queue_update()`
  reset: stop assuming that the caller passes in a clean index
  reset: allow the caller to specify the current HEAD object
  reset: introduce ability to skip updating HEAD
  reset: introduce dry-run mode
  reset: modernize flags passed to `reset_working_tree()`
  reset: rename `reset_head()`
  reset: drop `USE_THE_REPOSITORY_VARIABLE`
  read-cache: split out function to drop unmerged entries to stage 0
2026-07-08 12:07:01 -07:00
Toon Claes 5bf15ad0c0 replay: offer an option to linearize the commit topology
One of the stated goals of git-replay(1) is to allow implementing the
git-rebase(1) functionality on the server side.

The default mode of git-rebase(1) is to act as if `--no-rebase-merges`
was given. This mode drops merge commits instead of replaying them, and
linearizes the history into a sequence of regular (single-parent)
commits.

Add option `--linearize` to git-replay(1) to do the same. Each replayed
commit is stacked on top of the previously replayed one. When a merge is
encountered, the commits reachable from all of its sides are replayed
into the single line and the merge itself is dropped.

If a ref was pointing to a merge commit, that ref is updated to the
merge's last replayed ancestor.

git-replay(1) accepts multiple revision ranges, for example:

    $ git replay --onto main topic1 topic2

Without `--linearize` this replays 'topic1' and 'topic2' onto 'main'
independently and updates both refs.

With `--linearize` the whole set is flattened into one line: the ranges
are stacked on top of each other rather than replayed side by side, so
both refs end up pointing at different points along that single history.

Replaying all revision ranges into one single linear history is
intentional and it's the only way to ensure predictable results. A user
who wants to linearize ranges independently is advised to use separate
git-replay(1) invocations.

Linearizing is a distinct operation, and flattening merge commits is
just one aspect of that. Recreating merges would be a separate mode, so
rather than mirror git-rebase(1)'s `--rebase-merges[=<mode>]` interface,
git-replay(1) uses its own `--linearize` option.

Based-on-patches-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-07 13:31:40 -07:00
Patrick Steinhardt 27717e2069 replay: expose `replay_result_queue_update()`
Expose `replay_result_queue_update()`, which is used to append another
reference update to the replay result. This function will be used in a
subsequent commit.

Suggested-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-03 10:32:47 -07:00
Tuomas Ahola e6145d1241 docs: fix typos
Fix some typos and grammar errors in comments and documentation files.

Signed-off-by: Tuomas Ahola <taahol@utu.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-05 22:27:24 +09:00
Patrick Steinhardt 0cf4ad7cf5 replay: allow callers to control what happens with empty commits
When replaying commits it may happen that some of the commits become
empty relative to their parent. Such commits are for now automatically
dropped by the replay subsystem without much control from the user.

Introduce a new enum that allows the caller to drop, keep or abort in
this case.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-04-27 22:20:56 +09:00
Toon Claes 23d83f8ddb replay: allow to specify a ref with option --ref
When option '--onto' is passed to git-replay(1), the command will update
refs from the <revision-range> passed to the command. When using option
'--advance' or '--revert', the argument of that option is a ref that
will be updated.

To enable users to specify which ref to update, add option '--ref'. When
using option '--ref', the refs described above are left untouched and
instead the argument of this option is updated instead.

Because this introduces code paths in replay.c that jump to `out` before
init_basic_merge_options() is called on `merge_opt`, zero-initialize the
struct.

Signed-off-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-04-01 21:34:25 -07:00
Siddharth Asthana 2760ee4983 replay: add --revert mode to reverse commit changes
Add a `--revert <branch>` mode to git replay that undoes the changes
introduced by the specified commits. Like --onto and --advance, --revert
is a standalone mode: it takes a branch argument and updates that branch
with the newly created revert commits.

At GitLab, we need this in Gitaly for reverting commits directly on bare
repositories without requiring a working tree checkout.

The approach is the same as sequencer.c's do_pick_commit() -- cherry-pick
and revert are just the same three-way merge with swapped arguments:

  - Cherry-pick: merge(ancestor=parent, ours=current, theirs=commit)
  - Revert: merge(ancestor=commit, ours=current, theirs=parent)

We swap the base and pickme trees passed to merge_incore_nonrecursive()
to reverse the diff direction.

Reverts are processed newest-first (matching git revert behavior) to
reduce conflicts by peeling off changes from the top. Each revert
builds on the result of the previous one via the last_commit fallback
in the main replay loop, rather than relying on the parent-mapping
used for cherry-pick.

Revert commit messages follow the usual git revert conventions: prefixed
with "Revert" (or "Reapply" when reverting a revert), and including
"This reverts commit <hash>.". The author is set to the current user
rather than preserving the original author, matching git revert behavior.

Helped-by: Christian Couder <christian.couder@gmail.com>
Helped-by: Patrick Steinhardt <ps@pks.im>
Helped-by: Elijah Newren <newren@gmail.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Siddharth Asthana <siddharthasthana31@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-25 14:21:20 -07:00
Patrick Steinhardt 6aeda3cf5b builtin/replay: move core logic into "libgit.a"
Move the core logic used to replay commits into "libgit.a" so that it
can be easily reused by other commands. It will be used in a subsequent
commit where we're about to introduce a new git-history(1) command.

Note that with this change we have no sign-comparison warnings anymore,
and neither do we depend on `the_repository`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13 05:41:16 -08:00