about summary refs log tree commit
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2018-04-10 08:25:43 +0900
committerJunio C Hamano <gitster@pobox.com>2018-04-10 08:25:43 +0900
commit5d806b74d5223d0c19afd023fea3b321cc5bc717 (patch)
tree6976e1d8d39c17aa9102600e72e3ea40e07e1803
parente4bb62fa1eeee689744b413e29a50b4d1dae6886 (diff)
parent024aa4696c788eb1b07be53331f770605696ffba (diff)
downloadgit-5d806b74d5223d0c19afd023fea3b321cc5bc717.tar.gz
A "git fetch" from a repository with insane number of refs into a
repository that is already up-to-date still wasted too many cycles
making many lstat(2) calls to see if these objects at the tips
exist as loose objects locally.  These lstat(2) calls are optimized
away by enumerating all loose objects beforehand.

It is unknown if the new strategy negatively affects existing use
cases, fetching into a repository with many loose objects from a
repository with small number of refs.

* ti/fetch-everything-local-optim:
  fetch-pack.c: use oidset to check existence of loose object
-rw-r--r--cache.h2
-rw-r--r--fetch-pack.c45
-rw-r--r--sha1_file.c3
3 files changed, 47 insertions, 3 deletions
diff --git a/cache.h b/cache.h
index a61b2d3f0d..09f78084da 100644
--- a/cache.h
+++ b/cache.h
@@ -1777,6 +1777,8 @@ struct object_info {
 #define OBJECT_INFO_SKIP_CACHED 4
 /* Do not retry packed storage after checking packed and loose storage */
 #define OBJECT_INFO_QUICK 8
+/* Do not check loose object */
+#define OBJECT_INFO_IGNORE_LOOSE 16
 extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 
 /*
diff --git a/fetch-pack.c b/fetch-pack.c
index 1d6117565c..52932b37f8 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -711,6 +711,28 @@ static void mark_alternate_complete(struct object *obj)
         mark_complete(&obj->oid);
 }
 
+struct loose_object_iter {
+        struct oidset *loose_object_set;
+        struct ref *refs;
+};
+
+/*
+ *  If the number of refs is not larger than the number of loose objects,
+ *  this function stops inserting.
+ */
+static int add_loose_objects_to_set(const struct object_id *oid,
+                                    const char *path,
+                                    void *data)
+{
+        struct loose_object_iter *iter = data;
+        oidset_insert(iter->loose_object_set, oid);
+        if (iter->refs == NULL)
+                return 1;
+
+        iter->refs = iter->refs->next;
+        return 0;
+}
+
 static int everything_local(struct fetch_pack_args *args,
                             struct ref **refs,
                             struct ref **sought, int nr_sought)
@@ -719,16 +741,31 @@ static int everything_local(struct fetch_pack_args *args,
         int retval;
         int old_save_commit_buffer = save_commit_buffer;
         timestamp_t cutoff = 0;
+        struct oidset loose_oid_set = OIDSET_INIT;
+        int use_oidset = 0;
+        struct loose_object_iter iter = {&loose_oid_set, *refs};
+
+        /* Enumerate all loose objects or know refs are not so many. */
+        use_oidset = !for_each_loose_object(add_loose_objects_to_set,
+                                            &iter, 0);
 
         save_commit_buffer = 0;
 
         for (ref = *refs; ref; ref = ref->next) {
                 struct object *o;
+                unsigned int flags = OBJECT_INFO_QUICK;
 
-                if (!has_object_file_with_flags(&ref->old_oid,
-                                                OBJECT_INFO_QUICK))
-                        continue;
+                if (use_oidset &&
+                    !oidset_contains(&loose_oid_set, &ref->old_oid)) {
+                        /*
+                         * I know this does not exist in the loose form,
+                         * so check if it exists in a non-loose form.
+                         */
+                        flags |= OBJECT_INFO_IGNORE_LOOSE;
+                }
 
+                if (!has_object_file_with_flags(&ref->old_oid, flags))
+                        continue;
                 o = parse_object(&ref->old_oid);
                 if (!o)
                         continue;
@@ -744,6 +781,8 @@ static int everything_local(struct fetch_pack_args *args,
                 }
         }
 
+        oidset_clear(&loose_oid_set);
+
         if (!args->no_dependents) {
                 if (!args->deepen) {
                         for_each_ref(mark_complete_oid, NULL);
diff --git a/sha1_file.c b/sha1_file.c
index cc0f43ea84..ad77549565 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1262,6 +1262,9 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
                 if (find_pack_entry(real, &e))
                         break;
 
+                if (flags & OBJECT_INFO_IGNORE_LOOSE)
+                        return -1;
+
                 /* Most likely it's a loose object. */
                 if (!sha1_loose_object_info(real, oi, flags))
                         return 0;