fetch: in partial clone, check presence of targets
authorJonathan Tan <jonathantanmy@google.com>
Fri, 21 Sep 2018 18:22:38 +0000 (11:22 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 21 Sep 2018 20:20:49 +0000 (13:20 -0700)
When fetching an object that is known as a promisor object to the local
repository, the connectivity check in quickfetch() in builtin/fetch.c
succeeds, causing object transfer to be bypassed. However, this should
not happen if that object is merely promised and not actually present.

Because this happens, when a user invokes "git fetch origin <sha-1>" on
the command-line, the <sha-1> object may not actually be fetched even
though the command returns an exit code of 0. This is a similar issue
(but with a different cause) to the one fixed by a0c9016abd
("upload-pack: send refs' objects despite "filter"", 2018-07-09).

Therefore, update quickfetch() to also directly check for the presence
of all objects to be fetched. Its documentation and name are also
updated to better reflect what it does.

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/fetch.c
t/t5616-partial-clone.sh
index 0696abfc2a158baa3177fa9e71e2c16bc64655a4..6ce882ce824cd26273ffac73f96cf82b6bf591bb 100644 (file)
@@ -931,10 +931,11 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
  * everything we are going to fetch already exists and is connected
  * locally.
  */
-static int quickfetch(struct ref *ref_map)
+static int check_exist_and_connected(struct ref *ref_map)
 {
        struct ref *rm = ref_map;
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
+       struct ref *r;
 
        /*
         * If we are deepening a shallow clone we already have these
@@ -945,13 +946,23 @@ static int quickfetch(struct ref *ref_map)
         */
        if (deepen)
                return -1;
+
+       /*
+        * check_connected() allows objects to merely be promised, but
+        * we need all direct targets to exist.
+        */
+       for (r = rm; r; r = r->next) {
+               if (!has_object_file(&r->old_oid))
+                       return -1;
+       }
+
        opt.quiet = 1;
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
 {
-       int ret = quickfetch(ref_map);
+       int ret = check_exist_and_connected(ref_map);
        if (ret)
                ret = transport_fetch_refs(transport, ref_map);
        if (!ret)
index bbbe7537dfd315a567c6dc5583804325e6f10235..359d27d0292657fdf956147705cc2da4ee15c398 100755 (executable)
@@ -170,6 +170,23 @@ test_expect_success 'partial clone fetches blobs pointed to by refs even if norm
        git -C dst fsck
 '
 
+test_expect_success 'fetch what is specified on CLI even if already promised' '
+       rm -rf src dst.git &&
+       git init src &&
+       test_commit -C src foo &&
+       test_config -C src uploadpack.allowfilter 1 &&
+       test_config -C src uploadpack.allowanysha1inwant 1 &&
+
+       git hash-object --stdin <src/foo.t >blob &&
+
+       git clone --bare --filter=blob:none "file://$(pwd)/src" dst.git &&
+       git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_before &&
+       grep "?$(cat blob)" missing_before &&
+       git -C dst.git fetch origin $(cat blob) &&
+       git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_after &&
+       ! grep "?$(cat blob)" missing_after
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd