builtin/fetch: avoid aborting closed reference transaction

As part of the reference transaction commit phase, the transaction is set to a closed state regardless of whether it was successful of not. Attempting to abort a closed transaction via `ref_transaction_abort()` results in a `BUG()`. In c92abe71df (builtin/fetch: fix leaking transaction with `--atomic`, 2024-08-22), logic to free a transaction after the commit phase is moved to the centralized exit path. In cases where the transaction commit failed, this results in a closed transaction being aborted and signaling a bug. Free the transaction and set it to NULL when the commit fails. This allows the exit path to correctly handle the error without attempting to abort the transaction. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Justin Tobler committed Mar 20, 2025 at 19:44 UTC b9fadeead74df1f4fa4a4177e478903d63e600f5
2 files changed +21 -1
builtin/fetch.c
+8 -1
@@ -1732,8 +1732,15 @@ static int do_fetch(struct transport *transport,
1732 goto cleanup;
1733
1734 retcode = ref_transaction_commit(transaction, &err);
1735 - if (retcode)
1735 + if (retcode) {
1736 + /*
1737 + * Explicitly handle transaction cleanup to avoid
1738 + * aborting an already closed transaction.
1739 + */
1740 + ref_transaction_free(transaction);
1741 + transaction = NULL;
1742 goto cleanup;
1743 + }
1744 }
1745
1746 commit_fetch_head(&fetch_head);
t/t5510-fetch.sh
+13
@@ -345,6 +345,19 @@ test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' '
345 test_cmp expected atomic/.git/FETCH_HEAD
346 '
347
348 +test_expect_success REFFILES 'fetch --atomic fails transaction if reference locked' '
349 + test_when_finished "rm -rf upstream repo" &&
350 +
351 + git init upstream &&
352 + git -C upstream commit --allow-empty -m 1 &&
353 + git -C upstream switch -c foobar &&
354 + git clone --mirror upstream repo &&
355 + git -C upstream commit --allow-empty -m 2 &&
356 + touch repo/refs/heads/foobar.lock &&
357 +
358 + test_must_fail git -C repo fetch --atomic origin
359 +'
360 +
361 test_expect_success '--refmap="" ignores configured refspec' '
362 cd "$TRASH_DIRECTORY" &&
363 git clone "$D" remote-refs &&