Skip to content
Browse files

receive-pack: eliminate duplicate .have refs

When receiving a push, we advertise ref tips from any
alternate repositories, in case that helps the client send a
smaller pack. Since these refs don't actually exist in the
destination repository, we don't transmit the real ref
names, but instead use the pseudo-ref ".have".

If your alternate has a large number of duplicate refs (for
example, because it is aggregating objects from many related
repositories, some of which will have the same tags and
branch tips), then we will send each ".have $sha1" line
multiple times. This is a pointless waste of bandwidth, as
we are simply repeating the same fact to the client over and

This patch eliminates duplicate .have refs early on. It does
so efficiently by sorting the complete list and skipping
duplicates. This has the side effect of re-ordering the
.have lines by ascending sha1; this isn't a problem, though,
as the original order was meaningless.

There is a similar .have system in fetch-pack, but it
does not suffer from the same problem. For each alternate
ref we consider in fetch-pack, we actually open the object
and mark it with the SEEN flag, so duplicates are
automatically culled.

Signed-off-by: Jeff King <>
Signed-off-by: Junio C Hamano <>
  • Loading branch information...
1 parent 902bb36 commit cff38a5e11217dc997c1ffae8d0856023e05554a @peff peff committed with gitster May 19, 2011
Showing with 35 additions and 3 deletions.
  1. +13 −3 builtin/receive-pack.c
  2. +16 −0 sha1-array.c
  3. +6 −0 sha1-array.h
16 builtin/receive-pack.c
@@ -10,6 +10,7 @@
#include "remote.h"
#include "transport.h"
#include "string-list.h"
+#include "sha1-array.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -731,14 +732,23 @@ static int delete_only(struct command *commands)
return 1;
-static void add_one_alternate_ref(const struct ref *ref, void *unused)
+static void add_one_alternate_sha1(const unsigned char sha1[20], void *unused)
- add_extra_ref(".have", ref->old_sha1, 0);
+ add_extra_ref(".have", sha1, 0);
+static void collect_one_alternate_ref(const struct ref *ref, void *data)
+ struct sha1_array *sa = data;
+ sha1_array_append(sa, ref->old_sha1);
static void add_alternate_refs(void)
- for_each_alternate_ref(add_one_alternate_ref, NULL);
+ struct sha1_array sa = SHA1_ARRAY_INIT;
+ for_each_alternate_ref(collect_one_alternate_ref, &sa);
+ sha1_array_for_each_unique(&sa, add_one_alternate_sha1, NULL);
+ sha1_array_clear(&sa);
int cmd_receive_pack(int argc, const char **argv, const char *prefix)
16 sha1-array.c
@@ -41,3 +41,19 @@ void sha1_array_clear(struct sha1_array *array)
array->alloc = 0;
array->sorted = 0;
+void sha1_array_for_each_unique(struct sha1_array *array,
+ for_each_sha1_fn fn,
+ void *data)
+ int i;
+ if (!array->sorted)
+ sha1_array_sort(array);
+ for (i = 0; i < array->nr; i++) {
+ if (i > 0 && !hashcmp(array->sha1[i], array->sha1[i-1]))
+ continue;
+ fn(array->sha1[i], data);
+ }
6 sha1-array.h
@@ -15,4 +15,10 @@ void sha1_array_sort(struct sha1_array *array);
int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1);
void sha1_array_clear(struct sha1_array *array);
+typedef void (*for_each_sha1_fn)(const unsigned char sha1[20],
+ void *data);
+void sha1_array_for_each_unique(struct sha1_array *array,
+ for_each_sha1_fn fn,
+ void *data);
#endif /* SHA1_ARRAY_H */

0 comments on commit cff38a5

Please sign in to comment.
Something went wrong with that request. Please try again.