Skip to content

Commit cff38a5

Browse files
peffgitster
authored andcommitted
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 over. 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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 902bb36 commit cff38a5

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

builtin/receive-pack.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "remote.h"
1111
#include "transport.h"
1212
#include "string-list.h"
13+
#include "sha1-array.h"
1314

1415
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
1516

@@ -731,14 +732,23 @@ static int delete_only(struct command *commands)
731732
return 1;
732733
}
733734

734-
static void add_one_alternate_ref(const struct ref *ref, void *unused)
735+
static void add_one_alternate_sha1(const unsigned char sha1[20], void *unused)
735736
{
736-
add_extra_ref(".have", ref->old_sha1, 0);
737+
add_extra_ref(".have", sha1, 0);
738+
}
739+
740+
static void collect_one_alternate_ref(const struct ref *ref, void *data)
741+
{
742+
struct sha1_array *sa = data;
743+
sha1_array_append(sa, ref->old_sha1);
737744
}
738745

739746
static void add_alternate_refs(void)
740747
{
741-
for_each_alternate_ref(add_one_alternate_ref, NULL);
748+
struct sha1_array sa = SHA1_ARRAY_INIT;
749+
for_each_alternate_ref(collect_one_alternate_ref, &sa);
750+
sha1_array_for_each_unique(&sa, add_one_alternate_sha1, NULL);
751+
sha1_array_clear(&sa);
742752
}
743753

744754
int cmd_receive_pack(int argc, const char **argv, const char *prefix)

sha1-array.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,19 @@ void sha1_array_clear(struct sha1_array *array)
4141
array->alloc = 0;
4242
array->sorted = 0;
4343
}
44+
45+
void sha1_array_for_each_unique(struct sha1_array *array,
46+
for_each_sha1_fn fn,
47+
void *data)
48+
{
49+
int i;
50+
51+
if (!array->sorted)
52+
sha1_array_sort(array);
53+
54+
for (i = 0; i < array->nr; i++) {
55+
if (i > 0 && !hashcmp(array->sha1[i], array->sha1[i-1]))
56+
continue;
57+
fn(array->sha1[i], data);
58+
}
59+
}

sha1-array.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@ void sha1_array_sort(struct sha1_array *array);
1515
int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1);
1616
void sha1_array_clear(struct sha1_array *array);
1717

18+
typedef void (*for_each_sha1_fn)(const unsigned char sha1[20],
19+
void *data);
20+
void sha1_array_for_each_unique(struct sha1_array *array,
21+
for_each_sha1_fn fn,
22+
void *data);
23+
1824
#endif /* SHA1_ARRAY_H */

0 commit comments

Comments
 (0)