replay: support replaying down from root commit
git-replay(1) doesn't allow replaying commits all the way down to the root commit. Fix that. Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Toon Claes committed
Mar 24, 2026 at 20:35 UTC
e8b79a96ebaa2113391d14bfcdabe239f6ff8611
2 files changed
+24
-13
replay.c
+17
-10
@@ -209,7 +209,10 @@ static struct commit *mapped_commit(kh_oid_map_t *replayed_commits,
209
struct commit *commit,
210
struct commit *fallback)
211
{
212
- khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid);
212
+ khint_t pos;
213
+ if (!commit)
214
+ return fallback;
215
+ pos = kh_get_oid_map(replayed_commits, commit->object.oid);
216
if (pos == kh_end(replayed_commits))
217
return fallback;
218
return kh_value(replayed_commits, pos);
@@ -225,16 +228,24 @@ static struct commit *pick_regular_commit(struct repository *repo,
228
struct commit *base, *replayed_base;
229
struct tree *pickme_tree, *base_tree, *replayed_base_tree;
230
228
- base = pickme->parents->item;
229
- replayed_base = mapped_commit(replayed_commits, base, onto);
231
+ if (pickme->parents) {
232
+ base = pickme->parents->item;
233
+ base_tree = repo_get_commit_tree(repo, base);
234
+ } else {
235
+ base = NULL;
236
+ base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
237
+ }
238
239
+ replayed_base = mapped_commit(replayed_commits, base, onto);
240
replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
241
pickme_tree = repo_get_commit_tree(repo, pickme);
233
- base_tree = repo_get_commit_tree(repo, base);
242
243
merge_opt->branch1 = short_commit_name(repo, replayed_base);
244
merge_opt->branch2 = short_commit_name(repo, pickme);
237
- merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
245
+ if (pickme->parents)
246
+ merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2);
247
+ else
248
+ merge_opt->ancestor = xstrdup("empty tree");
249
250
merge_incore_nonrecursive(merge_opt,
251
base_tree,
@@ -293,8 +304,6 @@ int replay_revisions(struct rev_info *revs,
304
set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto,
305
&detached_head, &advance, &onto, &update_refs);
306
296
- /* FIXME: Should allow replaying commits with the first as a root commit */
297
-
307
if (prepare_revision_walk(revs) < 0) {
308
ret = error(_("error preparing revisions"));
309
goto out;
@@ -309,9 +318,7 @@ int replay_revisions(struct rev_info *revs,
318
khint_t pos;
319
int hr;
320
312
- if (!commit->parents)
313
- die(_("replaying down from root commit is not supported yet!"));
314
- if (commit->parents->next)
321
+ if (commit->parents && commit->parents->next)
322
die(_("replaying merge commits is not supported yet!"));
323
324
last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
t/t3650-replay-basics.sh
+7
-3
@@ -81,9 +81,13 @@ test_expect_success 'option --onto or --advance is mandatory' '
81
test_cmp expect actual
82
'
83
84
-test_expect_success 'no base or negative ref gives no-replaying down to root error' '
85
- echo "fatal: replaying down from root commit is not supported yet!" >expect &&
86
- test_must_fail git replay --onto=topic1 topic2 2>actual &&
84
+test_expect_success 'replay down to root onto another branch' '
85
+ git replay --ref-action=print --onto main topic2 >result &&
86
+
87
+ test_line_count = 1 result &&
88
+
89
+ git log --format=%s $(cut -f 3 -d " " result) >actual &&
90
+ test_write_lines E D C M L B A >expect &&
91
test_cmp expect actual
92
'
93