Skip to content

Commit 8ff2eef

Browse files
KarthikNayakgitster
authored andcommitted
fetch: fix non-conflicting tags not being committed
The commit 0e358de (fetch: use batched reference updates, 2025-05-19) updated the 'git-fetch(1)' command to use batched updates. This batches updates to gain performance improvements. When fetching references, each update is added to the transaction. Finally, when committing, individual updates are allowed to fail with reason, while the transaction itself succeeds. One scenario which was missed here, was fetching tags. When fetching conflicting tags, the `fetch_and_consume_refs()` function returns '1', which skipped committing the transaction and directly jumped to the cleanup section. This mean that no updates were applied. This also extends to backfilling tags which is done when fetching specific refspecs which contains tags in their history. Fix this by committing the transaction when we have an error code and not using an atomic transaction. This ensures other references are applied even when some updates fail. The cleanup section is reached with `retcode` set in several scenarios: - `truncate_fetch_head()`, `open_fetch_head()` and `prune_refs()` set `retcode` before the transaction is created, so no commit is attempted. - `fetch_and_consume_refs()` and `backfill_tags()` are the primary cases this fix targets, both setting a positive `retcode` to trigger the committing of the transaction. This simplifies error handling and ensures future modifications to `do_fetch()` don't need special handling for batched updates. Add tests to check for this regression. While here, add a missing cleanup from previous test. Reported-by: David Bohman <[email protected]> Helped-by: Patrick Steinhardt <[email protected]> Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c3cf8e5 commit 8ff2eef

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

builtin/fetch.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,14 @@ static int do_fetch(struct transport *transport,
19571957
}
19581958

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

t/t5510-fetch.sh

Lines changed: 62 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,67 @@ 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+
15801642
. "$TEST_DIRECTORY"/lib-httpd.sh
15811643
start_httpd
15821644

0 commit comments

Comments
 (0)