receive-pack: utilize rejected ref error details

In 9d2962a7c4 (receive-pack: use batched reference updates, 2025-05-19), git-receive-pack(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. Now that the updates also contain detailed error message, propagate those to the client via 'rp_error'. The detailed error messages can be very verbose, for e.g. in the files backend, when trying to write a non-commit object to a branch, you would see: ! [remote rejected] 3eaec9ccf3a53f168362a6b3fdeb73426fb9813d -> branch (cannot update ref 'refs/heads/branch': trying to write non-commit object 3eaec9ccf3a53f168362a6b3fdeb73426fb9813d to branch 'refs/heads/branch') Here the refname is repeated multiple times due to how error messages are propagated and filled over the code stack. This potentially can be cleaned up in a future commit. Reported-by: Elijah Newren <newren@gmail.com> Co-authored-by: Jeff King <peff@peff.net> Signed-off-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 2ea49f21e39de63481a6faf93e82a4b35f0e0ca2
2 files changed +21 -2
builtin/receive-pack.c
+6 -2
@@ -1813,11 +1813,14 @@ static void ref_transaction_rejection_handler(const char *refname,
1813 const char *old_target UNUSED,
1814 const char *new_target UNUSED,
1815 enum ref_transaction_error err,
1816 - const char *details UNUSED,
1816 + const char *details,
1817 void *cb_data)
1818 {
1819 struct strmap *failed_refs = cb_data;
1820
1821 + if (details)
1822 + rp_error("%s", details);
1823 +
1824 strmap_put(failed_refs, refname, (char *)ref_transaction_error_msg(err));
1825 }
1826
@@ -1884,6 +1887,7 @@ static void execute_commands_non_atomic(struct command *commands,
1887 }
1888
1889 ref_transaction_for_each_rejected_update(transaction,
1890 +
1891 ref_transaction_rejection_handler,
1892 &failed_refs);
1893
@@ -1895,7 +1899,7 @@ static void execute_commands_non_atomic(struct command *commands,
1899 if (reported_error)
1900 cmd->error_string = reported_error;
1901 else if (strmap_contains(&failed_refs, cmd->ref_name))
1898 - cmd->error_string = strmap_get(&failed_refs, cmd->ref_name);
1902 + cmd->error_string = cmd->error_string_owned = xstrdup(strmap_get(&failed_refs, cmd->ref_name));
1903 }
1904
1905 cleanup:
t/t5516-fetch-push.sh
+15
@@ -1882,4 +1882,19 @@ test_expect_success 'push with F/D conflict with deletion and creation' '
1882 git push testrepo :refs/heads/branch/conflict refs/heads/branch
1883 '
1884
1885 +test_expect_success 'pushing non-commit objects should report error' '
1886 + test_when_finished "rm -rf dest repo" &&
1887 + git init dest &&
1888 + git init repo &&
1889 +
1890 + (
1891 + cd repo &&
1892 + test_commit --annotate test &&
1893 +
1894 + tagsha=$(git rev-parse test^{tag}) &&
1895 + test_must_fail git push ../dest "$tagsha:refs/heads/branch" 2>err &&
1896 + test_grep "trying to write non-commit object $tagsha to branch ${SQ}refs/heads/branch${SQ}" err
1897 + )
1898 +'
1899 +
1900 test_done