builtin/history: split handling of ref updates into two phases

The function `handle_reference_updates()` is used by git-history(1) to update all references that refer to commits that have been rewritten. As such, it performs two steps: - It gathers the references that need to be updated in the first place. - It prepares and commits the reference transaction. In a subsequent commit we'll want to handle those two steps separately. Prepare for this by splitting up the function into two. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jul 1, 2026 at 13:35 UTC 46affdb8c74759697e9afef2a55854d3641953e1
1 file changed +62 -38
builtin/history.c
+62 -38
@@ -333,21 +333,17 @@ static int handle_ref_update(struct ref_transaction *transaction,
333 NULL, NULL, 0, reflog_msg, err);
334 }
335
336 -static int handle_reference_updates(struct rev_info *revs,
337 - enum ref_action action,
338 - struct commit *original,
339 - struct commit *rewritten,
340 - const char *reflog_msg,
341 - int dry_run,
342 - enum replay_empty_commit_action empty)
336 +static int compute_pending_ref_updates(struct rev_info *revs,
337 + enum ref_action action,
338 + struct commit *original,
339 + struct commit *rewritten,
340 + enum replay_empty_commit_action empty,
341 + struct replay_result *result)
342 {
343 const struct name_decoration *decoration;
344 struct replay_revisions_options opts = {
345 .empty = empty,
346 };
348 - struct replay_result result = { 0 };
349 - struct ref_transaction *transaction = NULL;
350 - struct strbuf err = STRBUF_INIT;
347 char hex[GIT_MAX_HEXSZ + 1];
348 bool detached_head;
349 int head_flags = 0;
@@ -359,34 +355,13 @@ static int handle_reference_updates(struct rev_info *revs,
355
356 opts.onto = oid_to_hex_r(hex, &rewritten->object.oid);
357
362 - ret = replay_revisions(revs, &opts, &result);
358 + ret = replay_revisions(revs, &opts, result);
359 if (ret)
364 - goto out;
360 + return ret;
361
362 if (action != REF_ACTION_BRANCHES && action != REF_ACTION_HEAD)
363 BUG("unsupported ref action %d", action);
364
369 - if (!dry_run) {
370 - transaction = ref_store_transaction_begin(get_main_ref_store(revs->repo), 0, &err);
371 - if (!transaction) {
372 - ret = error(_("failed to begin ref transaction: %s"), err.buf);
373 - goto out;
374 - }
375 - }
376 -
377 - for (size_t i = 0; i < result.updates_nr; i++) {
378 - ret = handle_ref_update(transaction,
379 - result.updates[i].refname,
380 - &result.updates[i].new_oid,
381 - &result.updates[i].old_oid,
382 - reflog_msg, &err);
383 - if (ret) {
384 - ret = error(_("failed to update ref '%s': %s"),
385 - result.updates[i].refname, err.buf);
386 - goto out;
387 - }
388 - }
389 -
365 /*
366 * `replay_revisions()` only updates references that are
367 * ancestors of `rewritten`, so we need to manually
@@ -414,14 +389,41 @@ static int handle_reference_updates(struct rev_info *revs,
389 !detached_head)
390 continue;
391
392 + replay_result_queue_update(result, decoration->name,
393 + &original->object.oid,
394 + &rewritten->object.oid);
395 + }
396 +
397 + return 0;
398 +}
399 +
400 +static int apply_pending_ref_updates(struct repository *repo,
401 + const struct replay_result *result,
402 + const char *reflog_msg,
403 + int dry_run)
404 +{
405 + struct ref_transaction *transaction = NULL;
406 + struct strbuf err = STRBUF_INIT;
407 + int ret;
408 +
409 + if (!dry_run) {
410 + transaction = ref_store_transaction_begin(get_main_ref_store(repo),
411 + 0, &err);
412 + if (!transaction) {
413 + ret = error(_("failed to begin ref transaction: %s"), err.buf);
414 + goto out;
415 + }
416 + }
417 +
418 + for (size_t i = 0; i < result->updates_nr; i++) {
419 ret = handle_ref_update(transaction,
418 - decoration->name,
419 - &rewritten->object.oid,
420 - &original->object.oid,
420 + result->updates[i].refname,
421 + &result->updates[i].new_oid,
422 + &result->updates[i].old_oid,
423 reflog_msg, &err);
424 if (ret) {
425 ret = error(_("failed to update ref '%s': %s"),
424 - decoration->name, err.buf);
426 + result->updates[i].refname, err.buf);
427 goto out;
428 }
429 }
@@ -435,11 +437,33 @@ static int handle_reference_updates(struct rev_info *revs,
437
438 out:
439 ref_transaction_free(transaction);
438 - replay_result_release(&result);
440 strbuf_release(&err);
441 return ret;
442 }
443
444 +static int handle_reference_updates(struct rev_info *revs,
445 + enum ref_action action,
446 + struct commit *original,
447 + struct commit *rewritten,
448 + const char *reflog_msg,
449 + int dry_run,
450 + enum replay_empty_commit_action empty)
451 +{
452 + struct replay_result result = { 0 };
453 + int ret;
454 +
455 + ret = compute_pending_ref_updates(revs, action, original, rewritten,
456 + empty, &result);
457 + if (ret)
458 + goto out;
459 +
460 + ret = apply_pending_ref_updates(revs->repo, &result, reflog_msg, dry_run);
461 +
462 +out:
463 + replay_result_release(&result);
464 + return ret;
465 +}
466 +
467 static int commit_became_empty(struct repository *repo,
468 struct commit *original,
469 struct tree *result)