aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2025-04-23 13:58:50 -0700
committerJunio C Hamano <gitster@pobox.com>2025-04-23 13:58:50 -0700
commitbb74c0abbc31da35be52999569ea481ebd149d1d (patch)
treee10db152ee6486a1b9af0f7990a140730ebe0ce2
parent68cd0cfa7ef753cc5549aa7882790565d51b9133 (diff)
parenta52d459e72b890c192485002ec518bb9e01c19a6 (diff)
downloadgit-bb74c0abbc31da35be52999569ea481ebd149d1d.tar.gz
Merge branch 'kn/bundle-dedup-optim'
Optimize the code to dedup references recorded in a bundle file. * kn/bundle-dedup-optim: bundle: fix non-linear performance scaling with refs t6020: test for duplicate refnames in bundle creation
-rw-r--r--bundle.c8
-rw-r--r--object.c35
-rw-r--r--object.h6
-rwxr-xr-xt/t6020-bundle-misc.sh53
4 files changed, 61 insertions, 41 deletions
diff --git a/bundle.c b/bundle.c
index d7ad690843..0614426e20 100644
--- a/bundle.c
+++ b/bundle.c
@@ -384,6 +384,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
{
int i;
int ref_count = 0;
+ struct strset objects = STRSET_INIT;
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *e = revs->pending.objects + i;
@@ -401,6 +402,9 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
+ if (strset_contains(&objects, display_ref))
+ goto skip_write_ref;
+
if (e->item->type == OBJ_TAG &&
!is_tag_in_date_range(e->item, revs)) {
e->item->flags |= UNINTERESTING;
@@ -423,6 +427,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
}
ref_count++;
+ strset_add(&objects, display_ref);
write_or_die(bundle_fd, oid_to_hex(&e->item->oid), the_hash_algo->hexsz);
write_or_die(bundle_fd, " ", 1);
write_or_die(bundle_fd, display_ref, strlen(display_ref));
@@ -431,6 +436,8 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
free(ref);
}
+ strset_clear(&objects);
+
/* end header */
write_or_die(bundle_fd, "\n", 1);
return ref_count;
@@ -566,7 +573,6 @@ int create_bundle(struct repository *r, const char *path,
*/
revs.blob_objects = revs.tree_objects = 0;
traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
- object_array_remove_duplicates(&revs_copy.pending);
/* write bundle refs */
ref_count = write_bundle_refs(bundle_fd, &revs_copy);
diff --git a/object.c b/object.c
index 154525a497..d054e7f0d0 100644
--- a/object.c
+++ b/object.c
@@ -492,44 +492,11 @@ void object_array_clear(struct object_array *array)
array->nr = array->alloc = 0;
}
-/*
- * Return true if array already contains an entry.
- */
-static int contains_object(struct object_array *array,
- const struct object *item, const char *name)
-{
- unsigned nr = array->nr, i;
- struct object_array_entry *object = array->objects;
-
- for (i = 0; i < nr; i++, object++)
- if (item == object->item && !strcmp(object->name, name))
- return 1;
- return 0;
-}
-
-void object_array_remove_duplicates(struct object_array *array)
-{
- unsigned nr = array->nr, src;
- struct object_array_entry *objects = array->objects;
-
- array->nr = 0;
- for (src = 0; src < nr; src++) {
- if (!contains_object(array, objects[src].item,
- objects[src].name)) {
- if (src != array->nr)
- objects[array->nr] = objects[src];
- array->nr++;
- } else {
- object_array_release_entry(&objects[src]);
- }
- }
-}
-
void clear_object_flags(struct repository *repo, unsigned flags)
{
int i;
- for (i=0; i < repo->parsed_objects->obj_hash_size; i++) {
+ for (i = 0; i < repo->parsed_objects->obj_hash_size; i++) {
struct object *obj = repo->parsed_objects->obj_hash[i];
if (obj)
obj->flags &= ~flags;
diff --git a/object.h b/object.h
index a304093979..8c3c1c46e1 100644
--- a/object.h
+++ b/object.h
@@ -327,12 +327,6 @@ void object_array_filter(struct object_array *array,
object_array_each_func_t want, void *cb_data);
/*
- * Remove from array all but the first entry with a given name.
- * Warning: this function uses an O(N^2) algorithm.
- */
-void object_array_remove_duplicates(struct object_array *array);
-
-/*
* Remove any objects from the array, freeing all used memory; afterwards
* the array is ready to store more objects with add_object_array().
*/
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index b3807e8f35..500c81b8a1 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -673,6 +673,59 @@ test_expect_success 'bundle progress with --no-quiet' '
grep "%" err
'
+test_expect_success 'create bundle with duplicate refnames' '
+ git bundle create out.bdl "main" "main" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'create bundle with duplicate refnames and --all' '
+ git bundle create out.bdl --all "main" "main" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ <COMMIT-N> refs/heads/release
+ <COMMIT-D> refs/heads/topic/1
+ <COMMIT-H> refs/heads/topic/2
+ <COMMIT-D> refs/pull/1/head
+ <COMMIT-G> refs/pull/2/head
+ <TAG-1> refs/tags/v1
+ <TAG-2> refs/tags/v2
+ <TAG-3> refs/tags/v3
+ <COMMIT-P> HEAD
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'create bundle with duplicate exlusion refnames' '
+ git bundle create out.bdl "main" "main^!" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'create bundle with duplicate refname short-form' '
+ git bundle create out.bdl "main" "main" "refs/heads/main" "refs/heads/main" &&
+
+ git bundle list-heads out.bdl |
+ make_user_friendly_and_stable_output >actual &&
+ cat >expect <<-\EOF &&
+ <COMMIT-P> refs/heads/main
+ EOF
+ test_cmp expect actual
+'
+
test_expect_success 'read bundle over stdin' '
git bundle create some.bundle HEAD &&