]> git.ipfire.org Git - thirdparty/git.git/commitdiff
fetch: fetch from an external bundle URI
authorDerrick Stolee <derrickstolee@github.com>
Tue, 31 Jan 2023 13:29:17 +0000 (13:29 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 31 Jan 2023 16:57:48 +0000 (08:57 -0800)
When a user specifies a URI via 'git clone --bundle-uri', that URI may
be a bundle list that advertises a 'bundle.heuristic' value. In that
case, the Git client stores a 'fetch.bundleURI' config value storing
that URI.

Teach 'git fetch' to check for this config value and download bundles
from that URI before fetching from the Git remote(s). Likely, the bundle
provider has configured a heuristic (such as "creationToken") that will
allow the Git client to download only a portion of the bundles before
continuing the fetch.

Since this URI is completely independent of the remote server, we want
to be sure that we connect to the bundle URI before creating a
connection to the Git remote. We do not want to hold a stateful
connection for too long if we can avoid it.

To test that this works correctly, extend the previous tests that set
'fetch.bundleURI' to do follow-up fetches. The bundle list is updated
incrementally at each phase to demonstrate that the heuristic avoids
downloading older bundles. This includes the middle fetch downloading
the objects in bundle-3.bundle from the Git remote, and therefore not
needing that bundle in the third fetch.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/fetch.c
t/t5558-clone-bundle-uri.sh

index 7378cafeec9fd6ea3edc521646ab7d8f66bcc02a..0477c3793695c9db71cdc6ff03dd3eaf96fc3a94 100644 (file)
@@ -29,6 +29,7 @@
 #include "commit-graph.h"
 #include "shallow.h"
 #include "worktree.h"
+#include "bundle-uri.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
 
@@ -2109,6 +2110,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
+       const char *bundle_uri;
        struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote = NULL;
        int result = 0;
@@ -2194,6 +2196,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (dry_run)
                write_fetch_head = 0;
 
+       if (!git_config_get_string_tmp("fetch.bundleuri", &bundle_uri) &&
+           fetch_bundle_uri(the_repository, bundle_uri, NULL))
+               warning(_("failed to fetch bundles from '%s'"), bundle_uri);
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
index b2d15e141ca0f7096c795a26947fb23b83063308..7deeb4b8ad1fc7060322173ae5b2fd348d843509 100755 (executable)
@@ -440,7 +440,55 @@ test_expect_success 'clone incomplete bundle list (http, creationToken)' '
        EOF
 
        test_remote_https_urls <trace-clone.txt >actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+
+       # We now have only one bundle ref.
+       git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       EOF
+       test_cmp expect refs &&
+
+       # Add remaining bundles, exercising the "deepening" strategy
+       # for downloading via the creationToken heurisitc.
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+               git -C clone-token-http fetch origin --no-tags \
+               refs/heads/merge:refs/heads/merge &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       $HTTPD_URL/bundle-3.bundle
+       $HTTPD_URL/bundle-2.bundle
+       EOF
+
+       test_remote_https_urls <trace1.txt >actual &&
+       test_cmp expect actual &&
+
+       # We now have all bundle refs.
+       git -C clone-token-http for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       refs/bundles/right
+       EOF
+       test_cmp expect refs
 '
 
 test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
@@ -477,6 +525,69 @@ test_expect_success 'http clone with bundle.heuristic creates fetch.bundleURI' '
        cat >expect <<-\EOF &&
        refs/bundles/base
        EOF
+       test_cmp expect refs &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-2"]
+               uri = bundle-2.bundle
+               creationToken = 2
+       EOF
+
+       # Fetch the objects for bundle-2 _and_ bundle-3.
+       GIT_TRACE2_EVENT="$(pwd)/trace1.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/left:refs/heads/left \
+               refs/heads/right:refs/heads/right &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-2.bundle
+       EOF
+
+       test_remote_https_urls <trace1.txt >actual &&
+       test_cmp expect actual &&
+
+       # received left from bundle-2
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       EOF
+       test_cmp expect refs &&
+
+       cat >>"$HTTPD_DOCUMENT_ROOT_PATH/bundle-list" <<-EOF &&
+       [bundle "bundle-3"]
+               uri = bundle-3.bundle
+               creationToken = 3
+
+       [bundle "bundle-4"]
+               uri = bundle-4.bundle
+               creationToken = 4
+       EOF
+
+       # This fetch should skip bundle-3.bundle, since its objects are
+       # already local (we have the requisite commits for bundle-4.bundle).
+       GIT_TRACE2_EVENT="$(pwd)/trace2.txt" \
+               git -C fetch-http-4 fetch origin --no-tags \
+               refs/heads/merge:refs/heads/merge &&
+
+       cat >expect <<-EOF &&
+       $HTTPD_URL/bundle-list
+       $HTTPD_URL/bundle-4.bundle
+       EOF
+
+       test_remote_https_urls <trace2.txt >actual &&
+       test_cmp expect actual &&
+
+       # received merge ref from bundle-4, but right is missing
+       # because we did not download bundle-3.
+       git -C fetch-http-4 for-each-ref --format="%(refname)" "refs/bundles/*" >refs &&
+
+       cat >expect <<-\EOF &&
+       refs/bundles/base
+       refs/bundles/left
+       refs/bundles/merge
+       EOF
        test_cmp expect refs
 '