fetch: utilize rejected ref error details

In 0e358de64a (fetch: use batched reference updates, 2025-05-19), git-fetch(1) switched to using batched reference updates. This also introduced a regression wherein instead of providing detailed error messages for failed referenced updates, the users were provided generic error messages based on the error type. Similar to the previous commit, switch to using detailed error messages if present for failed reference updates to fix this regression. Reported-by: Elijah Newren <newren@gmail.com> Co-authored-by: Jeff King <peff@peff.net> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Jan 25, 2026 at 23:52 UTC 274f4355527275515a2c7c5f5a1214cf57b06338
2 files changed +10 -8
builtin/fetch.c
+6 -4
@@ -1649,7 +1649,7 @@ static void ref_transaction_rejection_handler(const char *refname,
1649 const char *old_target UNUSED,
1650 const char *new_target UNUSED,
1651 enum ref_transaction_error err,
1652 - const char *details UNUSED,
1652 + const char *details,
1653 void *cb_data)
1654 {
1655 struct ref_rejection_data *data = cb_data;
@@ -1674,9 +1674,11 @@ static void ref_transaction_rejection_handler(const char *refname,
1674 "branches"), data->remote_name);
1675 data->conflict_msg_shown = true;
1676 } else {
1677 - const char *reason = ref_transaction_error_msg(err);
1678 -
1679 - error(_("fetching ref %s failed: %s"), refname, reason);
1677 + if (details)
1678 + error("%s", details);
1679 + else
1680 + error(_("fetching ref %s failed: %s"),
1681 + refname, ref_transaction_error_msg(err));
1682 }
1683
1684 *data->retcode = 1;
t/t5510-fetch.sh
+4 -4
@@ -1516,7 +1516,7 @@ test_expect_success REFFILES 'existing reference lock in repo' '
1516 git remote add origin ../base &&
1517 touch refs/heads/foo.lock &&
1518 test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
1519 - test_grep "error: fetching ref refs/heads/foo failed: reference already exists" err &&
1519 + test_grep -e "error: cannot lock ref ${SQ}refs/heads/foo${SQ}: Unable to create" -e "refs/heads/foo.lock${SQ}: File exists." err &&
1520 git rev-parse refs/heads/main >expect &&
1521 git rev-parse refs/heads/branch >actual &&
1522 test_cmp expect actual
@@ -1530,7 +1530,7 @@ test_expect_success CASE_INSENSITIVE_FS,REFFILES 'F/D conflict on case insensiti
1530 cd case_insensitive &&
1531 git remote add origin -- ../case_sensitive_fd &&
1532 test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
1533 - test_grep "failed: refname conflict" err &&
1533 + test_grep "cannot process ${SQ}refs/remotes/origin/foo${SQ} and ${SQ}refs/remotes/origin/foo/bar${SQ} at the same time" err &&
1534 git rev-parse refs/heads/main >expect &&
1535 git rev-parse refs/heads/foo/bar >actual &&
1536 test_cmp expect actual
@@ -1544,7 +1544,7 @@ test_expect_success CASE_INSENSITIVE_FS,REFFILES 'D/F conflict on case insensiti
1544 cd case_insensitive &&
1545 git remote add origin -- ../case_sensitive_df &&
1546 test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
1547 - test_grep "failed: refname conflict" err &&
1547 + test_grep "cannot lock ref ${SQ}refs/remotes/origin/foo${SQ}: there is a non-empty directory ${SQ}./refs/remotes/origin/foo${SQ} blocking reference ${SQ}refs/remotes/origin/foo${SQ}" err &&
1548 git rev-parse refs/heads/main >expect &&
1549 git rev-parse refs/heads/Foo/bar >actual &&
1550 test_cmp expect actual
@@ -1658,7 +1658,7 @@ test_expect_success REFFILES "FETCH_HEAD is updated even if ref updates fail" '
1658 git remote add origin ../base &&
1659 >refs/heads/foo.lock &&
1660 test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
1661 - test_grep "error: fetching ref refs/heads/foo failed: reference already exists" err &&
1661 + test_grep -e "error: cannot lock ref ${SQ}refs/heads/foo${SQ}: Unable to create" -e "refs/heads/foo.lock${SQ}: File exists." err &&
1662 test_grep "branch ${SQ}branch${SQ} of ../base" FETCH_HEAD &&
1663 test_grep "branch ${SQ}foo${SQ} of ../base" FETCH_HEAD
1664 )