mirror of
https://github.com/git/git.git
synced 2024-09-08 03:50:44 +02:00
Merge branch 'xx/bundie-uri-fixes'
When bundleURI interface fetches multiple bundles, Git failed to take full advantage of all bundles and ended up slurping duplicated objects. * xx/bundie-uri-fixes: unbundle: extend object verification for fetches fetch-pack: expose fsckObjects configuration logic bundle-uri: verify oid before writing refs
This commit is contained in:
commit
125e389470
@ -11,6 +11,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "pkt-line.h"
|
||||
#include "config.h"
|
||||
#include "fetch-pack.h"
|
||||
#include "remote.h"
|
||||
|
||||
static struct {
|
||||
@ -375,7 +376,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
|
||||
* the prerequisite commits.
|
||||
*/
|
||||
if ((result = unbundle(r, &header, bundle_fd, NULL,
|
||||
VERIFY_BUNDLE_QUIET)))
|
||||
VERIFY_BUNDLE_QUIET | (fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0))))
|
||||
return 1;
|
||||
|
||||
/*
|
||||
@ -402,8 +403,7 @@ static int unbundle_from_file(struct repository *r, const char *file)
|
||||
refs_update_ref(get_main_ref_store(the_repository),
|
||||
"fetched bundle", bundle_ref.buf, oid,
|
||||
has_old ? &old_oid : NULL,
|
||||
REF_SKIP_OID_VERIFICATION,
|
||||
UPDATE_REFS_MSG_ON_ERR);
|
||||
0, UPDATE_REFS_MSG_ON_ERR);
|
||||
}
|
||||
|
||||
bundle_header_release(&header);
|
||||
|
3
bundle.c
3
bundle.c
@ -636,6 +636,9 @@ int unbundle(struct repository *r, struct bundle_header *header,
|
||||
if (header->filter.choice)
|
||||
strvec_push(&ip.args, "--promisor=from-bundle");
|
||||
|
||||
if (flags & VERIFY_BUNDLE_FSCK)
|
||||
strvec_push(&ip.args, "--fsck-objects");
|
||||
|
||||
if (extra_index_pack_args) {
|
||||
strvec_pushv(&ip.args, extra_index_pack_args->v);
|
||||
strvec_clear(extra_index_pack_args);
|
||||
|
1
bundle.h
1
bundle.h
@ -33,6 +33,7 @@ int create_bundle(struct repository *r, const char *path,
|
||||
enum verify_bundle_flags {
|
||||
VERIFY_BUNDLE_VERBOSE = (1 << 0),
|
||||
VERIFY_BUNDLE_QUIET = (1 << 1),
|
||||
VERIFY_BUNDLE_FSCK = (1 << 2),
|
||||
};
|
||||
|
||||
int verify_bundle(struct repository *r, struct bundle_header *header,
|
||||
|
17
fetch-pack.c
17
fetch-pack.c
@ -956,12 +956,7 @@ static int get_pack(struct fetch_pack_args *args,
|
||||
strvec_push(&cmd.args, alternate_shallow_file);
|
||||
}
|
||||
|
||||
if (fetch_fsck_objects >= 0
|
||||
? fetch_fsck_objects
|
||||
: transfer_fsck_objects >= 0
|
||||
? transfer_fsck_objects
|
||||
: 0)
|
||||
fsck_objects = 1;
|
||||
fsck_objects = fetch_pack_fsck_objects();
|
||||
|
||||
if (do_keep || args->from_promisor || index_pack_args || fsck_objects) {
|
||||
if (pack_lockfiles || fsck_objects)
|
||||
@ -2050,6 +2045,16 @@ static const struct object_id *iterate_ref_map(void *cb_data)
|
||||
return &ref->old_oid;
|
||||
}
|
||||
|
||||
int fetch_pack_fsck_objects(void)
|
||||
{
|
||||
fetch_pack_setup();
|
||||
if (fetch_fsck_objects >= 0)
|
||||
return fetch_fsck_objects;
|
||||
if (transfer_fsck_objects >= 0)
|
||||
return transfer_fsck_objects;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||
int fd[],
|
||||
const struct ref *ref,
|
||||
|
@ -101,4 +101,9 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
|
||||
*/
|
||||
int report_unmatched_refs(struct ref **sought, int nr_sought);
|
||||
|
||||
/*
|
||||
* Return true if checks for broken objects in received pack are required.
|
||||
*/
|
||||
int fetch_pack_fsck_objects(void);
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@
|
||||
test_description='test fetching bundles with --bundle-uri'
|
||||
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY"/lib-bundle.sh
|
||||
|
||||
test_expect_success 'fail to clone from non-existent file' '
|
||||
test_when_finished rm -rf test &&
|
||||
@ -19,10 +20,39 @@ test_expect_success 'fail to clone from non-bundle file' '
|
||||
|
||||
test_expect_success 'create bundle' '
|
||||
git init clone-from &&
|
||||
git -C clone-from checkout -b topic &&
|
||||
test_commit -C clone-from A &&
|
||||
test_commit -C clone-from B &&
|
||||
git -C clone-from bundle create B.bundle topic
|
||||
(
|
||||
cd clone-from &&
|
||||
git checkout -b topic &&
|
||||
|
||||
test_commit A &&
|
||||
git bundle create A.bundle topic &&
|
||||
|
||||
test_commit B &&
|
||||
git bundle create B.bundle topic &&
|
||||
|
||||
# Create a bundle with reference pointing to non-existent object.
|
||||
commit_a=$(git rev-parse A) &&
|
||||
commit_b=$(git rev-parse B) &&
|
||||
sed -e "/^$/q" -e "s/$commit_a /$commit_b /" \
|
||||
<A.bundle >bad-header.bundle &&
|
||||
convert_bundle_to_pack \
|
||||
<A.bundle >>bad-header.bundle &&
|
||||
|
||||
tree_b=$(git rev-parse B^{tree}) &&
|
||||
cat >data <<-EOF &&
|
||||
tree $tree_b
|
||||
parent $commit_b
|
||||
author A U Thor
|
||||
committer A U Thor
|
||||
|
||||
commit: this is a commit with bad emails
|
||||
|
||||
EOF
|
||||
bad_commit=$(git hash-object --literally -t commit -w --stdin <data) &&
|
||||
git branch bad $bad_commit &&
|
||||
git bundle create bad-object.bundle bad &&
|
||||
git update-ref -d refs/heads/bad
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'clone with path bundle' '
|
||||
@ -33,6 +63,33 @@ test_expect_success 'clone with path bundle' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'clone with bundle that has bad header' '
|
||||
# Write bundle ref fails, but clone can still proceed.
|
||||
git clone --bundle-uri="clone-from/bad-header.bundle" \
|
||||
clone-from clone-bad-header 2>err &&
|
||||
commit_b=$(git -C clone-from rev-parse B) &&
|
||||
test_grep "trying to write ref '\''refs/bundles/topic'\'' with nonexistent object $commit_b" err &&
|
||||
git -C clone-bad-header for-each-ref --format="%(refname)" >refs &&
|
||||
test_grep ! "refs/bundles/" refs
|
||||
'
|
||||
|
||||
test_expect_success 'clone with bundle that has bad object' '
|
||||
# Unbundle succeeds if no fsckObjects configured.
|
||||
git clone --bundle-uri="clone-from/bad-object.bundle" \
|
||||
clone-from clone-bad-object-no-fsck &&
|
||||
git -C clone-bad-object-no-fsck for-each-ref --format="%(refname)" >refs &&
|
||||
grep "refs/bundles/" refs >actual &&
|
||||
test_write_lines refs/bundles/bad >expect &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
# Unbundle fails with fsckObjects set true, but clone can still proceed.
|
||||
git -c fetch.fsckObjects=true clone --bundle-uri="clone-from/bad-object.bundle" \
|
||||
clone-from clone-bad-object-fsck 2>err &&
|
||||
test_grep "missingEmail" err &&
|
||||
git -C clone-bad-object-fsck for-each-ref --format="%(refname)" >refs &&
|
||||
test_grep ! "refs/bundles/" refs
|
||||
'
|
||||
|
||||
test_expect_success 'clone with path bundle and non-default hash' '
|
||||
test_when_finished "rm -rf clone-path-non-default-hash" &&
|
||||
GIT_DEFAULT_HASH=sha256 git clone --bundle-uri="clone-from/B.bundle" \
|
||||
@ -259,6 +316,128 @@ test_expect_success 'clone bundle list (file, any mode, all failures)' '
|
||||
! grep "refs/bundles/" refs
|
||||
'
|
||||
|
||||
test_expect_success 'negotiation: bundle with part of wanted commits' '
|
||||
test_when_finished "rm -f trace*.txt" &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
|
||||
git clone --no-local --bundle-uri="clone-from/A.bundle" \
|
||||
clone-from nego-bundle-part &&
|
||||
git -C nego-bundle-part for-each-ref --format="%(refname)" >refs &&
|
||||
grep "refs/bundles/" refs >actual &&
|
||||
test_write_lines refs/bundles/topic >expect &&
|
||||
test_cmp expect actual &&
|
||||
# Ensure that refs/bundles/topic are sent as "have".
|
||||
tip=$(git -C clone-from rev-parse A) &&
|
||||
test_grep "clone> have $tip" trace-packet.txt
|
||||
'
|
||||
|
||||
test_expect_success 'negotiation: bundle with all wanted commits' '
|
||||
test_when_finished "rm -f trace*.txt" &&
|
||||
GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
|
||||
git clone --no-local --single-branch --branch=topic --no-tags \
|
||||
--bundle-uri="clone-from/B.bundle" \
|
||||
clone-from nego-bundle-all &&
|
||||
git -C nego-bundle-all for-each-ref --format="%(refname)" >refs &&
|
||||
grep "refs/bundles/" refs >actual &&
|
||||
test_write_lines refs/bundles/topic >expect &&
|
||||
test_cmp expect actual &&
|
||||
# We already have all needed commits so no "want" needed.
|
||||
test_grep ! "clone> want " trace-packet.txt
|
||||
'
|
||||
|
||||
test_expect_success 'negotiation: bundle list (no heuristic)' '
|
||||
test_when_finished "rm -f trace*.txt" &&
|
||||
cat >bundle-list <<-EOF &&
|
||||
[bundle]
|
||||
version = 1
|
||||
mode = all
|
||||
|
||||
[bundle "bundle-1"]
|
||||
uri = file://$(pwd)/clone-from/bundle-1.bundle
|
||||
|
||||
[bundle "bundle-2"]
|
||||
uri = file://$(pwd)/clone-from/bundle-2.bundle
|
||||
EOF
|
||||
|
||||
GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
|
||||
git clone --no-local --bundle-uri="file://$(pwd)/bundle-list" \
|
||||
clone-from nego-bundle-list-no-heuristic &&
|
||||
|
||||
git -C nego-bundle-list-no-heuristic for-each-ref --format="%(refname)" >refs &&
|
||||
grep "refs/bundles/" refs >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
refs/bundles/base
|
||||
refs/bundles/left
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
tip=$(git -C nego-bundle-list-no-heuristic rev-parse refs/bundles/left) &&
|
||||
test_grep "clone> have $tip" trace-packet.txt
|
||||
'
|
||||
|
||||
test_expect_success 'negotiation: bundle list (creationToken)' '
|
||||
test_when_finished "rm -f trace*.txt" &&
|
||||
cat >bundle-list <<-EOF &&
|
||||
[bundle]
|
||||
version = 1
|
||||
mode = all
|
||||
heuristic = creationToken
|
||||
|
||||
[bundle "bundle-1"]
|
||||
uri = file://$(pwd)/clone-from/bundle-1.bundle
|
||||
creationToken = 1
|
||||
|
||||
[bundle "bundle-2"]
|
||||
uri = file://$(pwd)/clone-from/bundle-2.bundle
|
||||
creationToken = 2
|
||||
EOF
|
||||
|
||||
GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
|
||||
git clone --no-local --bundle-uri="file://$(pwd)/bundle-list" \
|
||||
clone-from nego-bundle-list-heuristic &&
|
||||
|
||||
git -C nego-bundle-list-heuristic for-each-ref --format="%(refname)" >refs &&
|
||||
grep "refs/bundles/" refs >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
refs/bundles/base
|
||||
refs/bundles/left
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
tip=$(git -C nego-bundle-list-heuristic rev-parse refs/bundles/left) &&
|
||||
test_grep "clone> have $tip" trace-packet.txt
|
||||
'
|
||||
|
||||
test_expect_success 'negotiation: bundle list with all wanted commits' '
|
||||
test_when_finished "rm -f trace*.txt" &&
|
||||
cat >bundle-list <<-EOF &&
|
||||
[bundle]
|
||||
version = 1
|
||||
mode = all
|
||||
heuristic = creationToken
|
||||
|
||||
[bundle "bundle-1"]
|
||||
uri = file://$(pwd)/clone-from/bundle-1.bundle
|
||||
creationToken = 1
|
||||
|
||||
[bundle "bundle-2"]
|
||||
uri = file://$(pwd)/clone-from/bundle-2.bundle
|
||||
creationToken = 2
|
||||
EOF
|
||||
|
||||
GIT_TRACE_PACKET="$(pwd)/trace-packet.txt" \
|
||||
git clone --no-local --single-branch --branch=left --no-tags \
|
||||
--bundle-uri="file://$(pwd)/bundle-list" \
|
||||
clone-from nego-bundle-list-all &&
|
||||
|
||||
git -C nego-bundle-list-all for-each-ref --format="%(refname)" >refs &&
|
||||
grep "refs/bundles/" refs >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
refs/bundles/base
|
||||
refs/bundles/left
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
# We already have all needed commits so no "want" needed.
|
||||
test_grep ! "clone> want " trace-packet.txt
|
||||
'
|
||||
|
||||
#########################################################################
|
||||
# HTTP tests begin here
|
||||
|
||||
|
@ -139,6 +139,41 @@ test_expect_success 'fetch SHA-1 from bundle' '
|
||||
git fetch --no-tags foo/tip.bundle "$(cat hash)"
|
||||
'
|
||||
|
||||
test_expect_success 'clone bundle with different fsckObjects configurations' '
|
||||
test_create_repo bundle-fsck &&
|
||||
(
|
||||
cd bundle-fsck &&
|
||||
test_commit A &&
|
||||
commit_a=$(git rev-parse A) &&
|
||||
tree_a=$(git rev-parse A^{tree}) &&
|
||||
cat >data <<-EOF &&
|
||||
tree $tree_a
|
||||
parent $commit_a
|
||||
author A U Thor
|
||||
committer A U Thor
|
||||
|
||||
commit: this is a commit with bad emails
|
||||
|
||||
EOF
|
||||
bad_commit=$(git hash-object --literally -t commit -w --stdin <data) &&
|
||||
git branch bad $bad_commit &&
|
||||
git bundle create bad.bundle bad
|
||||
) &&
|
||||
|
||||
git clone bundle-fsck/bad.bundle bundle-no-fsck &&
|
||||
|
||||
git -c fetch.fsckObjects=false -c transfer.fsckObjects=true \
|
||||
clone bundle-fsck/bad.bundle bundle-fetch-no-fsck &&
|
||||
|
||||
test_must_fail git -c fetch.fsckObjects=true \
|
||||
clone bundle-fsck/bad.bundle bundle-fetch-fsck 2>err &&
|
||||
test_grep "missingEmail" err &&
|
||||
|
||||
test_must_fail git -c transfer.fsckObjects=true \
|
||||
clone bundle-fsck/bad.bundle bundle-transfer-fsck 2>err &&
|
||||
test_grep "missingEmail" err
|
||||
'
|
||||
|
||||
test_expect_success 'git bundle uses expected default format' '
|
||||
git bundle create bundle HEAD^.. &&
|
||||
cat >expect <<-EOF &&
|
||||
|
@ -186,7 +186,8 @@ static int fetch_refs_from_bundle(struct transport *transport,
|
||||
if (!data->get_refs_from_bundle_called)
|
||||
get_refs_from_bundle_inner(transport);
|
||||
ret = unbundle(the_repository, &data->header, data->fd,
|
||||
&extra_index_pack_args, 0);
|
||||
&extra_index_pack_args,
|
||||
fetch_pack_fsck_objects() ? VERIFY_BUNDLE_FSCK : 0);
|
||||
transport->hash_algo = data->header.hash_algo;
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user