Skip to content

Commit 3955b0b

Browse files
committed
Merge branch 'kn/fix-fetch-backfill-tag-with-batched-ref-updates' into next
"git fetch" that involves fetching tags, when a tag being fetched needs to overwrite existing one, failed to fetch other tags, which has been corrected. * kn/fix-fetch-backfill-tag-with-batched-ref-updates: fetch: fix failed batched updates skipping operations fetch: fix non-conflicting tags not being committed fetch: extract out reference committing logic
2 parents 2c9d72e + b7b17ec commit 3955b0b

File tree

2 files changed

+195
-26
lines changed

2 files changed

+195
-26
lines changed

builtin/fetch.c

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,36 @@ static void ref_transaction_rejection_handler(const char *refname,
16811681
*data->retcode = 1;
16821682
}
16831683

1684+
/*
1685+
* Commit the reference transaction. If it isn't an atomic transaction, handle
1686+
* rejected updates as part of using batched updates.
1687+
*/
1688+
static int commit_ref_transaction(struct ref_transaction **transaction,
1689+
bool is_atomic, const char *remote_name,
1690+
struct strbuf *err)
1691+
{
1692+
int retcode = ref_transaction_commit(*transaction, err);
1693+
if (retcode)
1694+
goto out;
1695+
1696+
if (!is_atomic) {
1697+
struct ref_rejection_data data = {
1698+
.conflict_msg_shown = 0,
1699+
.remote_name = remote_name,
1700+
.retcode = &retcode,
1701+
};
1702+
1703+
ref_transaction_for_each_rejected_update(*transaction,
1704+
ref_transaction_rejection_handler,
1705+
&data);
1706+
}
1707+
1708+
out:
1709+
ref_transaction_free(*transaction);
1710+
*transaction = NULL;
1711+
return retcode;
1712+
}
1713+
16841714
static int do_fetch(struct transport *transport,
16851715
struct refspec *rs,
16861716
const struct fetch_config *config)
@@ -1853,33 +1883,14 @@ static int do_fetch(struct transport *transport,
18531883
if (retcode)
18541884
goto cleanup;
18551885

1856-
retcode = ref_transaction_commit(transaction, &err);
1857-
if (retcode) {
1858-
/*
1859-
* Explicitly handle transaction cleanup to avoid
1860-
* aborting an already closed transaction.
1861-
*/
1862-
ref_transaction_free(transaction);
1863-
transaction = NULL;
1886+
retcode = commit_ref_transaction(&transaction, atomic_fetch,
1887+
transport->remote->name, &err);
1888+
/*
1889+
* With '--atomic', bail out if the transaction fails. Without '--atomic',
1890+
* continue to fetch head and perform other post-fetch operations.
1891+
*/
1892+
if (retcode && atomic_fetch)
18641893
goto cleanup;
1865-
}
1866-
1867-
if (!atomic_fetch) {
1868-
struct ref_rejection_data data = {
1869-
.retcode = &retcode,
1870-
.conflict_msg_shown = 0,
1871-
.remote_name = transport->remote->name,
1872-
};
1873-
1874-
ref_transaction_for_each_rejected_update(transaction,
1875-
ref_transaction_rejection_handler,
1876-
&data);
1877-
if (retcode) {
1878-
ref_transaction_free(transaction);
1879-
transaction = NULL;
1880-
goto cleanup;
1881-
}
1882-
}
18831894

18841895
commit_fetch_head(&fetch_head);
18851896

@@ -1945,6 +1956,14 @@ static int do_fetch(struct transport *transport,
19451956
}
19461957

19471958
cleanup:
1959+
/*
1960+
* When using batched updates, we want to commit the non-rejected
1961+
* updates and also handle the rejections.
1962+
*/
1963+
if (retcode && !atomic_fetch && transaction)
1964+
commit_ref_transaction(&transaction, false,
1965+
transport->remote->name, &err);
1966+
19481967
if (retcode) {
19491968
if (err.len) {
19501969
error("%s", err.buf);

t/t5510-fetch.sh

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,7 @@ test_expect_success CASE_INSENSITIVE_FS,REFFILES 'D/F conflict on case insensiti
15521552
'
15531553

15541554
test_expect_success REFFILES 'D/F conflict on case sensitive filesystem with lock' '
1555+
test_when_finished rm -rf base repo &&
15551556
(
15561557
git init --ref-format=reftable base &&
15571558
cd base &&
@@ -1577,6 +1578,155 @@ test_expect_success REFFILES 'D/F conflict on case sensitive filesystem with loc
15771578
)
15781579
'
15791580

1581+
test_expect_success 'fetch --tags fetches existing tags' '
1582+
test_when_finished rm -rf base repo &&
1583+
1584+
git init base &&
1585+
git -C base commit --allow-empty -m "empty-commit" &&
1586+
1587+
git clone --bare base repo &&
1588+
1589+
git -C base tag tag-1 &&
1590+
git -C repo for-each-ref >out &&
1591+
test_grep ! "tag-1" out &&
1592+
git -C repo fetch --tags &&
1593+
git -C repo for-each-ref >out &&
1594+
test_grep "tag-1" out
1595+
'
1596+
1597+
test_expect_success 'fetch --tags fetches non-conflicting tags' '
1598+
test_when_finished rm -rf base repo &&
1599+
1600+
git init base &&
1601+
git -C base commit --allow-empty -m "empty-commit" &&
1602+
git -C base tag tag-1 &&
1603+
1604+
git clone --bare base repo &&
1605+
1606+
git -C base tag tag-2 &&
1607+
git -C repo for-each-ref >out &&
1608+
test_grep ! "tag-2" out &&
1609+
1610+
git -C base commit --allow-empty -m "second empty-commit" &&
1611+
git -C base tag -f tag-1 &&
1612+
1613+
test_must_fail git -C repo fetch --tags 2>out &&
1614+
test_grep "tag-1 (would clobber existing tag)" out &&
1615+
git -C repo for-each-ref >out &&
1616+
test_grep "tag-2" out
1617+
'
1618+
1619+
test_expect_success "backfill tags when providing a refspec" '
1620+
test_when_finished rm -rf source target &&
1621+
1622+
git init source &&
1623+
git -C source commit --allow-empty --message common &&
1624+
git clone file://"$(pwd)"/source target &&
1625+
(
1626+
cd source &&
1627+
test_commit history &&
1628+
test_commit fetch-me
1629+
) &&
1630+
1631+
# The "history" tag is backfilled even though we requested
1632+
# to only fetch HEAD
1633+
git -C target fetch origin HEAD:branch &&
1634+
git -C target tag -l >actual &&
1635+
cat >expect <<-\EOF &&
1636+
fetch-me
1637+
history
1638+
EOF
1639+
test_cmp expect actual
1640+
'
1641+
1642+
test_expect_success REFFILES "FETCH_HEAD is updated even if ref updates fail" '
1643+
test_when_finished rm -rf base repo &&
1644+
1645+
git init base &&
1646+
(
1647+
cd base &&
1648+
test_commit "updated" &&
1649+
1650+
git update-ref refs/heads/foo @ &&
1651+
git update-ref refs/heads/branch @
1652+
) &&
1653+
1654+
git init --bare repo &&
1655+
(
1656+
cd repo &&
1657+
rm -f FETCH_HEAD &&
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 &&
1662+
test_grep "branch ${SQ}branch${SQ} of ../base" FETCH_HEAD &&
1663+
test_grep "branch ${SQ}foo${SQ} of ../base" FETCH_HEAD
1664+
)
1665+
'
1666+
1667+
test_expect_success "upstream tracking info is added with --set-upstream" '
1668+
test_when_finished rm -rf base repo &&
1669+
1670+
git init --initial-branch=main base &&
1671+
test_commit -C base "updated" &&
1672+
1673+
git init --bare --initial-branch=main repo &&
1674+
(
1675+
cd repo &&
1676+
git remote add origin ../base &&
1677+
git fetch origin --set-upstream main &&
1678+
git config get branch.main.remote >actual &&
1679+
echo "origin" >expect &&
1680+
test_cmp expect actual
1681+
)
1682+
'
1683+
1684+
test_expect_success REFFILES "upstream tracking info is added even with conflicts" '
1685+
test_when_finished rm -rf base repo &&
1686+
1687+
git init --initial-branch=main base &&
1688+
test_commit -C base "updated" &&
1689+
1690+
git init --bare --initial-branch=main repo &&
1691+
(
1692+
cd repo &&
1693+
git remote add origin ../base &&
1694+
test_must_fail git config get branch.main.remote &&
1695+
1696+
mkdir -p refs/remotes/origin &&
1697+
>refs/remotes/origin/main.lock &&
1698+
test_must_fail git fetch origin --set-upstream main &&
1699+
git config get branch.main.remote >actual &&
1700+
echo "origin" >expect &&
1701+
test_cmp expect actual
1702+
)
1703+
'
1704+
1705+
test_expect_success REFFILES "HEAD is updated even with conflicts" '
1706+
test_when_finished rm -rf base repo &&
1707+
1708+
git init base &&
1709+
(
1710+
cd base &&
1711+
test_commit "updated" &&
1712+
1713+
git update-ref refs/heads/foo @ &&
1714+
git update-ref refs/heads/branch @
1715+
) &&
1716+
1717+
git init --bare repo &&
1718+
(
1719+
cd repo &&
1720+
git remote add origin ../base &&
1721+
1722+
test_path_is_missing refs/remotes/origin/HEAD &&
1723+
mkdir -p refs/remotes/origin &&
1724+
>refs/remotes/origin/branch.lock &&
1725+
test_must_fail git fetch origin &&
1726+
test -f refs/remotes/origin/HEAD
1727+
)
1728+
'
1729+
15801730
. "$TEST_DIRECTORY"/lib-httpd.sh
15811731
start_httpd
15821732

0 commit comments

Comments
 (0)