replay: resolve the replay base outside pick_regular_commit()

Depending on what gets passed into the function pick_regular_commit(), it decides the new base for the replayed commit. It first tries to find the replayed results of `pickme`'s parent in the `replayed_commits` map. If not found, it falls back to `onto`. When using git-replay(1) with --onto, the fallback is the revision passed in with this option, but when using --revert, the fallback is `last_commit`. It's rather confusing the base is decided partly inside pick_regular_commit() and partly by its caller. Move the base selection completely into the caller: replay_revisions(). This bundles all the logic of deciding on the base together. Also, this reduces the number of parameters of pick_regular_commit(), making its interface cleaner. This refactoring doesn't bring any behavior changes. Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Toon Claes committed Jul 28, 2026 at 17:45 UTC 959a273edd9cd9ca48b6c1a6f0922ffb56d37835
1 file changed +21 -13
replay.c
+21 -13
@@ -284,25 +284,19 @@ static void put_mapped_commit(kh_oid_map_t *replayed_commits,
284
285 static struct commit *pick_regular_commit(struct repository *repo,
286 struct commit *pickme,
287 - kh_oid_map_t *replayed_commits,
288 - struct commit *onto,
287 + struct commit *replayed_base,
288 struct merge_options *merge_opt,
289 struct merge_result *result,
290 enum replay_mode mode,
291 enum replay_empty_commit_action empty)
292 {
294 - struct commit *base, *replayed_base;
293 struct tree *pickme_tree, *base_tree, *replayed_base_tree;
294
297 - if (pickme->parents) {
298 - base = pickme->parents->item;
299 - base_tree = repo_get_commit_tree(repo, base);
300 - } else {
301 - base = NULL;
295 + if (pickme->parents)
296 + base_tree = repo_get_commit_tree(repo, pickme->parents->item);
297 + else
298 base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
303 - }
299
305 - replayed_base = get_mapped_commit(replayed_commits, base, onto);
300 replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
301 pickme_tree = repo_get_commit_tree(repo, pickme);
302
@@ -443,12 +437,26 @@ int replay_revisions(struct rev_info *revs,
437 while ((commit = get_revision(revs))) {
438 const struct name_decoration *decoration;
439
440 + /*
441 + * Decide where to replay this commit on.
442 + * If the parent commit was replayed already, the replayed result
443 + * can be found in `replayed_commits`. Otherwise fall back to `onto`.
444 + * When reverting, commits are replayed in reverse order and thus
445 + * its parent isn't replayed yet. Therefore revert commits are
446 + * always replayed onto `last_commit`.
447 + */
448 + struct commit *parent = commit->parents ? commit->parents->item : NULL;
449 + struct commit *base = get_mapped_commit(replayed_commits, parent, onto);
450 +
451 + if (mode == REPLAY_MODE_REVERT)
452 + base = last_commit;
453 +
454 if (commit->parents && commit->parents->next)
455 die(_("replaying merge commits is not supported yet!"));
456
449 - last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
450 - mode == REPLAY_MODE_REVERT ? last_commit : onto,
451 - &merge_opt, &result, mode, opts->empty);
457 + last_commit = pick_regular_commit(revs->repo, commit, base,
458 + &merge_opt, &result,
459 + mode, opts->empty);
460 if (!last_commit)
461 break;
462