fetch: extract out reference committing logic

The `do_fetch()` function contains the core of the `git-fetch(1)` logic. Part of this is to fetch and store references. This is done by 1. Creating a reference transaction (non-atomic mode uses batched updates). 2. Adding individual reference updates to the transaction. 3. Committing the transaction. 4. When using batched updates, handling the rejected updates. The following commit, will fix a bug wherein fetching tags with conflicts was causing other reference updates to fail. Fixing this requires utilizing this logic in different regions of the function. In preparation of the follow up commit, extract the committing and rejection handling logic into a separate function called `commit_ref_transaction()`. Helped-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Nov 21, 2025 at 12:13 UTC c3cf8e5907adb55380801007ff14f0e3b7cf7152
1 file changed +33 -26
builtin/fetch.c
+33 -26
@@ -1686,6 +1686,36 @@ static void ref_transaction_rejection_handler(const char *refname,
1686 *data->retcode = 1;
1687 }
1688
1689 +/*
1690 + * Commit the reference transaction. If it isn't an atomic transaction, handle
1691 + * rejected updates as part of using batched updates.
1692 + */
1693 +static int commit_ref_transaction(struct ref_transaction **transaction,
1694 + bool is_atomic, const char *remote_name,
1695 + struct strbuf *err)
1696 +{
1697 + int retcode = ref_transaction_commit(*transaction, err);
1698 + if (retcode)
1699 + goto out;
1700 +
1701 + if (!is_atomic) {
1702 + struct ref_rejection_data data = {
1703 + .conflict_msg_shown = 0,
1704 + .remote_name = remote_name,
1705 + .retcode = &retcode,
1706 + };
1707 +
1708 + ref_transaction_for_each_rejected_update(*transaction,
1709 + ref_transaction_rejection_handler,
1710 + &data);
1711 + }
1712 +
1713 +out:
1714 + ref_transaction_free(*transaction);
1715 + *transaction = NULL;
1716 + return retcode;
1717 +}
1718 +
1719 static int do_fetch(struct transport *transport,
1720 struct refspec *rs,
1721 const struct fetch_config *config)
@@ -1858,33 +1888,10 @@ static int do_fetch(struct transport *transport,
1888 if (retcode)
1889 goto cleanup;
1890
1861 - retcode = ref_transaction_commit(transaction, &err);
1862 - if (retcode) {
1863 - /*
1864 - * Explicitly handle transaction cleanup to avoid
1865 - * aborting an already closed transaction.
1866 - */
1867 - ref_transaction_free(transaction);
1868 - transaction = NULL;
1891 + retcode = commit_ref_transaction(&transaction, atomic_fetch,
1892 + transport->remote->name, &err);
1893 + if (retcode)
1894 goto cleanup;
1870 - }
1871 -
1872 - if (!atomic_fetch) {
1873 - struct ref_rejection_data data = {
1874 - .retcode = &retcode,
1875 - .conflict_msg_shown = 0,
1876 - .remote_name = transport->remote->name,
1877 - };
1878 -
1879 - ref_transaction_for_each_rejected_update(transaction,
1880 - ref_transaction_rejection_handler,
1881 - &data);
1882 - if (retcode) {
1883 - ref_transaction_free(transaction);
1884 - transaction = NULL;
1885 - goto cleanup;
1886 - }
1887 - }
1895
1896 commit_fetch_head(&fetch_head);
1897