mirror of
https://github.com/git/git.git
synced 2026-01-09 01:34:00 +00:00
Merge branch 'kn/fix-fetch-backfill-tag-with-batched-ref-updates'
"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
This commit is contained in:
@@ -1681,6 +1681,36 @@ static void ref_transaction_rejection_handler(const char *refname,
|
|||||||
*data->retcode = 1;
|
*data->retcode = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commit the reference transaction. If it isn't an atomic transaction, handle
|
||||||
|
* rejected updates as part of using batched updates.
|
||||||
|
*/
|
||||||
|
static int commit_ref_transaction(struct ref_transaction **transaction,
|
||||||
|
bool is_atomic, const char *remote_name,
|
||||||
|
struct strbuf *err)
|
||||||
|
{
|
||||||
|
int retcode = ref_transaction_commit(*transaction, err);
|
||||||
|
if (retcode)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!is_atomic) {
|
||||||
|
struct ref_rejection_data data = {
|
||||||
|
.conflict_msg_shown = 0,
|
||||||
|
.remote_name = remote_name,
|
||||||
|
.retcode = &retcode,
|
||||||
|
};
|
||||||
|
|
||||||
|
ref_transaction_for_each_rejected_update(*transaction,
|
||||||
|
ref_transaction_rejection_handler,
|
||||||
|
&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
ref_transaction_free(*transaction);
|
||||||
|
*transaction = NULL;
|
||||||
|
return retcode;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_fetch(struct transport *transport,
|
static int do_fetch(struct transport *transport,
|
||||||
struct refspec *rs,
|
struct refspec *rs,
|
||||||
const struct fetch_config *config)
|
const struct fetch_config *config)
|
||||||
@@ -1853,33 +1883,14 @@ static int do_fetch(struct transport *transport,
|
|||||||
if (retcode)
|
if (retcode)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
retcode = ref_transaction_commit(transaction, &err);
|
retcode = commit_ref_transaction(&transaction, atomic_fetch,
|
||||||
if (retcode) {
|
transport->remote->name, &err);
|
||||||
/*
|
/*
|
||||||
* Explicitly handle transaction cleanup to avoid
|
* With '--atomic', bail out if the transaction fails. Without '--atomic',
|
||||||
* aborting an already closed transaction.
|
* continue to fetch head and perform other post-fetch operations.
|
||||||
*/
|
*/
|
||||||
ref_transaction_free(transaction);
|
if (retcode && atomic_fetch)
|
||||||
transaction = NULL;
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
|
||||||
|
|
||||||
if (!atomic_fetch) {
|
|
||||||
struct ref_rejection_data data = {
|
|
||||||
.retcode = &retcode,
|
|
||||||
.conflict_msg_shown = 0,
|
|
||||||
.remote_name = transport->remote->name,
|
|
||||||
};
|
|
||||||
|
|
||||||
ref_transaction_for_each_rejected_update(transaction,
|
|
||||||
ref_transaction_rejection_handler,
|
|
||||||
&data);
|
|
||||||
if (retcode) {
|
|
||||||
ref_transaction_free(transaction);
|
|
||||||
transaction = NULL;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commit_fetch_head(&fetch_head);
|
commit_fetch_head(&fetch_head);
|
||||||
|
|
||||||
@@ -1945,6 +1956,14 @@ static int do_fetch(struct transport *transport,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
/*
|
||||||
|
* When using batched updates, we want to commit the non-rejected
|
||||||
|
* updates and also handle the rejections.
|
||||||
|
*/
|
||||||
|
if (retcode && !atomic_fetch && transaction)
|
||||||
|
commit_ref_transaction(&transaction, false,
|
||||||
|
transport->remote->name, &err);
|
||||||
|
|
||||||
if (retcode) {
|
if (retcode) {
|
||||||
if (err.len) {
|
if (err.len) {
|
||||||
error("%s", err.buf);
|
error("%s", err.buf);
|
||||||
|
|||||||
150
t/t5510-fetch.sh
150
t/t5510-fetch.sh
@@ -1552,6 +1552,7 @@ test_expect_success CASE_INSENSITIVE_FS,REFFILES 'D/F conflict on case insensiti
|
|||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success REFFILES 'D/F conflict on case sensitive filesystem with lock' '
|
test_expect_success REFFILES 'D/F conflict on case sensitive filesystem with lock' '
|
||||||
|
test_when_finished rm -rf base repo &&
|
||||||
(
|
(
|
||||||
git init --ref-format=reftable base &&
|
git init --ref-format=reftable base &&
|
||||||
cd base &&
|
cd base &&
|
||||||
@@ -1577,6 +1578,155 @@ test_expect_success REFFILES 'D/F conflict on case sensitive filesystem with loc
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fetch --tags fetches existing tags' '
|
||||||
|
test_when_finished rm -rf base repo &&
|
||||||
|
|
||||||
|
git init base &&
|
||||||
|
git -C base commit --allow-empty -m "empty-commit" &&
|
||||||
|
|
||||||
|
git clone --bare base repo &&
|
||||||
|
|
||||||
|
git -C base tag tag-1 &&
|
||||||
|
git -C repo for-each-ref >out &&
|
||||||
|
test_grep ! "tag-1" out &&
|
||||||
|
git -C repo fetch --tags &&
|
||||||
|
git -C repo for-each-ref >out &&
|
||||||
|
test_grep "tag-1" out
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fetch --tags fetches non-conflicting tags' '
|
||||||
|
test_when_finished rm -rf base repo &&
|
||||||
|
|
||||||
|
git init base &&
|
||||||
|
git -C base commit --allow-empty -m "empty-commit" &&
|
||||||
|
git -C base tag tag-1 &&
|
||||||
|
|
||||||
|
git clone --bare base repo &&
|
||||||
|
|
||||||
|
git -C base tag tag-2 &&
|
||||||
|
git -C repo for-each-ref >out &&
|
||||||
|
test_grep ! "tag-2" out &&
|
||||||
|
|
||||||
|
git -C base commit --allow-empty -m "second empty-commit" &&
|
||||||
|
git -C base tag -f tag-1 &&
|
||||||
|
|
||||||
|
test_must_fail git -C repo fetch --tags 2>out &&
|
||||||
|
test_grep "tag-1 (would clobber existing tag)" out &&
|
||||||
|
git -C repo for-each-ref >out &&
|
||||||
|
test_grep "tag-2" out
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "backfill tags when providing a refspec" '
|
||||||
|
test_when_finished rm -rf source target &&
|
||||||
|
|
||||||
|
git init source &&
|
||||||
|
git -C source commit --allow-empty --message common &&
|
||||||
|
git clone file://"$(pwd)"/source target &&
|
||||||
|
(
|
||||||
|
cd source &&
|
||||||
|
test_commit history &&
|
||||||
|
test_commit fetch-me
|
||||||
|
) &&
|
||||||
|
|
||||||
|
# The "history" tag is backfilled even though we requested
|
||||||
|
# to only fetch HEAD
|
||||||
|
git -C target fetch origin HEAD:branch &&
|
||||||
|
git -C target tag -l >actual &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
fetch-me
|
||||||
|
history
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success REFFILES "FETCH_HEAD is updated even if ref updates fail" '
|
||||||
|
test_when_finished rm -rf base repo &&
|
||||||
|
|
||||||
|
git init base &&
|
||||||
|
(
|
||||||
|
cd base &&
|
||||||
|
test_commit "updated" &&
|
||||||
|
|
||||||
|
git update-ref refs/heads/foo @ &&
|
||||||
|
git update-ref refs/heads/branch @
|
||||||
|
) &&
|
||||||
|
|
||||||
|
git init --bare repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
rm -f FETCH_HEAD &&
|
||||||
|
git remote add origin ../base &&
|
||||||
|
>refs/heads/foo.lock &&
|
||||||
|
test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
|
||||||
|
test_grep "error: fetching ref refs/heads/foo failed: reference already exists" err &&
|
||||||
|
test_grep "branch ${SQ}branch${SQ} of ../base" FETCH_HEAD &&
|
||||||
|
test_grep "branch ${SQ}foo${SQ} of ../base" FETCH_HEAD
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "upstream tracking info is added with --set-upstream" '
|
||||||
|
test_when_finished rm -rf base repo &&
|
||||||
|
|
||||||
|
git init --initial-branch=main base &&
|
||||||
|
test_commit -C base "updated" &&
|
||||||
|
|
||||||
|
git init --bare --initial-branch=main repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
git remote add origin ../base &&
|
||||||
|
git fetch origin --set-upstream main &&
|
||||||
|
git config get branch.main.remote >actual &&
|
||||||
|
echo "origin" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success REFFILES "upstream tracking info is added even with conflicts" '
|
||||||
|
test_when_finished rm -rf base repo &&
|
||||||
|
|
||||||
|
git init --initial-branch=main base &&
|
||||||
|
test_commit -C base "updated" &&
|
||||||
|
|
||||||
|
git init --bare --initial-branch=main repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
git remote add origin ../base &&
|
||||||
|
test_must_fail git config get branch.main.remote &&
|
||||||
|
|
||||||
|
mkdir -p refs/remotes/origin &&
|
||||||
|
>refs/remotes/origin/main.lock &&
|
||||||
|
test_must_fail git fetch origin --set-upstream main &&
|
||||||
|
git config get branch.main.remote >actual &&
|
||||||
|
echo "origin" >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success REFFILES "HEAD is updated even with conflicts" '
|
||||||
|
test_when_finished rm -rf base repo &&
|
||||||
|
|
||||||
|
git init base &&
|
||||||
|
(
|
||||||
|
cd base &&
|
||||||
|
test_commit "updated" &&
|
||||||
|
|
||||||
|
git update-ref refs/heads/foo @ &&
|
||||||
|
git update-ref refs/heads/branch @
|
||||||
|
) &&
|
||||||
|
|
||||||
|
git init --bare repo &&
|
||||||
|
(
|
||||||
|
cd repo &&
|
||||||
|
git remote add origin ../base &&
|
||||||
|
|
||||||
|
test_path_is_missing refs/remotes/origin/HEAD &&
|
||||||
|
mkdir -p refs/remotes/origin &&
|
||||||
|
>refs/remotes/origin/branch.lock &&
|
||||||
|
test_must_fail git fetch origin &&
|
||||||
|
test -f refs/remotes/origin/HEAD
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
. "$TEST_DIRECTORY"/lib-httpd.sh
|
. "$TEST_DIRECTORY"/lib-httpd.sh
|
||||||
start_httpd
|
start_httpd
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user