OSDN Git Service

write_entry: fix leak when retrying delayed filter
[git-core/git.git] / fetch-pack.c
index b42d01f..cd86865 100644 (file)
@@ -15,6 +15,7 @@
 #include "version.h"
 #include "prio-queue.h"
 #include "sha1-array.h"
+#include "oidset.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -78,7 +79,7 @@ static void cache_one_alternate(const char *refname,
                                void *vcache)
 {
        struct alternate_object_cache *cache = vcache;
-       struct object *obj = parse_object(oid->hash);
+       struct object *obj = parse_object(oid);
 
        if (!obj || (obj->flags & ALTERNATE))
                return;
@@ -120,7 +121,7 @@ static void rev_list_push(struct commit *commit, int mark)
 
 static int rev_list_insert_ref(const char *refname, const struct object_id *oid)
 {
-       struct object *o = deref_tag(parse_object(oid->hash), refname, 0);
+       struct object *o = deref_tag(parse_object(oid), refname, 0);
 
        if (o && o->type == OBJ_COMMIT)
                rev_list_push((struct commit *)o, SEEN);
@@ -137,7 +138,7 @@ static int rev_list_insert_ref_oid(const char *refname, const struct object_id *
 static int clear_marks(const char *refname, const struct object_id *oid,
                       int flag, void *cb_data)
 {
-       struct object *o = deref_tag(parse_object(oid->hash), refname, 0);
+       struct object *o = deref_tag(parse_object(oid), refname, 0);
 
        if (o && o->type == OBJ_COMMIT)
                clear_commit_marks((struct commit *)o,
@@ -394,8 +395,8 @@ static int find_common(struct fetch_pack_args *args,
        if (args->depth > 0)
                packet_buf_write(&req_buf, "deepen %d", args->depth);
        if (args->deepen_since) {
-               unsigned long max_age = approxidate(args->deepen_since);
-               packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+               timestamp_t max_age = approxidate(args->deepen_since);
+               packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
        }
        if (args->deepen_not) {
                int i;
@@ -417,7 +418,7 @@ static int find_common(struct fetch_pack_args *args,
                        if (skip_prefix(line, "shallow ", &arg)) {
                                if (get_oid_hex(arg, &oid))
                                        die(_("invalid shallow line: %s"), line);
-                               register_shallow(oid.hash);
+                               register_shallow(&oid);
                                continue;
                        }
                        if (skip_prefix(line, "unshallow ", &arg)) {
@@ -426,9 +427,9 @@ static int find_common(struct fetch_pack_args *args,
                                if (!lookup_object(oid.hash))
                                        die(_("object not found: %s"), line);
                                /* make sure that it is parsed as shallow */
-                               if (!parse_object(oid.hash))
+                               if (!parse_object(&oid))
                                        die(_("error in object: %s"), line);
-                               if (unregister_shallow(oid.hash))
+                               if (unregister_shallow(&oid))
                                        die(_("no shallow found: %s"), line);
                                continue;
                        }
@@ -483,7 +484,7 @@ static int find_common(struct fetch_pack_args *args,
                                case ACK_ready:
                                case ACK_continue: {
                                        struct commit *commit =
-                                               lookup_commit(result_oid->hash);
+                                               lookup_commit(result_oid);
                                        if (!commit)
                                                die(_("invalid commit %s"), oid_to_hex(result_oid));
                                        if (args->stateless_rpc
@@ -557,14 +558,14 @@ static struct commit_list *complete;
 
 static int mark_complete(const struct object_id *oid)
 {
-       struct object *o = parse_object(oid->hash);
+       struct object *o = parse_object(oid);
 
        while (o && o->type == OBJ_TAG) {
                struct tag *t = (struct tag *) o;
                if (!t->tagged)
                        break; /* broken repository */
                o->flags |= COMPLETE;
-               o = parse_object(t->tagged->oid.hash);
+               o = parse_object(&t->tagged->oid);
        }
        if (o && o->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)o;
@@ -583,7 +584,7 @@ static int mark_complete_oid(const char *refname, const struct object_id *oid,
 }
 
 static void mark_recent_complete_commits(struct fetch_pack_args *args,
-                                        unsigned long cutoff)
+                                        timestamp_t cutoff)
 {
        while (complete && cutoff <= complete->item->date) {
                print_verbose(args, _("Marking %s as complete"),
@@ -592,13 +593,38 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,
        }
 }
 
+static void add_refs_to_oidset(struct oidset *oids, struct ref *refs)
+{
+       for (; refs; refs = refs->next)
+               oidset_insert(oids, &refs->old_oid);
+}
+
+static int tip_oids_contain(struct oidset *tip_oids,
+                           struct ref *unmatched, struct ref *newlist,
+                           const struct object_id *id)
+{
+       /*
+        * Note that this only looks at the ref lists the first time it's
+        * called. This works out in filter_refs() because even though it may
+        * add to "newlist" between calls, the additions will always be for
+        * oids that are already in the set.
+        */
+       if (!tip_oids->map.tablesize) {
+               add_refs_to_oidset(tip_oids, unmatched);
+               add_refs_to_oidset(tip_oids, newlist);
+       }
+       return oidset_contains(tip_oids, id);
+}
+
 static void filter_refs(struct fetch_pack_args *args,
                        struct ref **refs,
                        struct ref **sought, int nr_sought)
 {
        struct ref *newlist = NULL;
        struct ref **newtail = &newlist;
+       struct ref *unmatched = NULL;
        struct ref *ref, *next;
+       struct oidset tip_oids = OIDSET_INIT;
        int i;
 
        i = 0;
@@ -631,7 +657,8 @@ static void filter_refs(struct fetch_pack_args *args,
                        ref->next = NULL;
                        newtail = &ref->next;
                } else {
-                       free(ref);
+                       ref->next = unmatched;
+                       unmatched = ref;
                }
        }
 
@@ -649,7 +676,9 @@ static void filter_refs(struct fetch_pack_args *args,
                        continue;
 
                if ((allow_unadvertised_object_request &
-                   (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
+                    (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)) ||
+                   tip_oids_contain(&tip_oids, unmatched, newlist,
+                                    &ref->old_oid)) {
                        ref->match_status = REF_MATCHED;
                        *newtail = copy_ref(ref);
                        newtail = &(*newtail)->next;
@@ -657,6 +686,13 @@ static void filter_refs(struct fetch_pack_args *args,
                        ref->match_status = REF_UNADVERTISED_NOT_ALLOWED;
                }
        }
+
+       oidset_clear(&tip_oids);
+       for (ref = unmatched; ref; ref = next) {
+               next = ref->next;
+               free(ref);
+       }
+
        *refs = newlist;
 }
 
@@ -671,7 +707,7 @@ static int everything_local(struct fetch_pack_args *args,
 {
        struct ref *ref;
        int retval;
-       unsigned long cutoff = 0;
+       timestamp_t cutoff = 0;
 
        save_commit_buffer = 0;
 
@@ -681,7 +717,7 @@ static int everything_local(struct fetch_pack_args *args,
                if (!has_object_file(&ref->old_oid))
                        continue;
 
-               o = parse_object(ref->old_oid.hash);
+               o = parse_object(&ref->old_oid);
                if (!o)
                        continue;