sparse-checkout: optimize string_list construction and add tests to verify deduplication.

Improve O(n^2) complexity to O(n log n) while building a sorted 'string_list' by constructing it unsorted then sorting it followed by removing duplicates. sparse-checkout deduplicates repeated cone-mode patterns, but this behaviour was previously untested, add tests that verify that sparse-checkout file contain each cone pattern only once and sparse-checkout list reports each pattern only once. Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com> Acked-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Amisha Chhajed committed Jan 21, 2026 at 18:30 UTC 49223593fd743998d13fcd27fceaf1e0095bb08e
2 files changed +52 -3
builtin/sparse-checkout.c
+4 -3
@@ -91,10 +91,11 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix,
91
92 hashmap_for_each_entry(&pl.recursive_hashmap, &iter, pe, ent) {
93 /* pe->pattern starts with "/", skip it */
94 - string_list_insert(&sl, pe->pattern + 1);
94 + string_list_append(&sl, pe->pattern + 1);
95 }
96
97 string_list_sort(&sl);
98 + string_list_remove_duplicates(&sl, 0);
99
100 for (i = 0; i < sl.nr; i++) {
101 quote_c_style(sl.items[i].string, NULL, stdout, 0);
@@ -289,7 +290,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
290 if (!hashmap_contains_parent(&pl->recursive_hashmap,
291 pe->pattern,
292 &parent_pattern))
292 - string_list_insert(&sl, pe->pattern);
293 + string_list_append(&sl, pe->pattern);
294 }
295
296 string_list_sort(&sl);
@@ -311,7 +312,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
312 if (!hashmap_contains_parent(&pl->recursive_hashmap,
313 pe->pattern,
314 &parent_pattern))
314 - string_list_insert(&sl, pe->pattern);
315 + string_list_append(&sl, pe->pattern);
316 }
317
318 strbuf_release(&parent_pattern);
t/t1091-sparse-checkout-builtin.sh
+48
@@ -817,6 +817,54 @@ test_expect_success 'cone mode clears ignored subdirectories' '
817 test_cmp expect out
818 '
819
820 +test_expect_success 'sparse-checkout deduplicates repeated cone patterns' '
821 + rm -f repo/.git/info/sparse-checkout &&
822 + git -C repo sparse-checkout init --cone &&
823 + git -C repo sparse-checkout add --stdin <<-\EOF &&
824 + foo/bar/baz
825 + a/b/c
826 + foo/bar/baz
827 + a/b
828 + EOF
829 + cat >expect <<-\EOF &&
830 + /*
831 + !/*/
832 + /a/
833 + !/a/*/
834 + /foo/
835 + !/foo/*/
836 + /foo/bar/
837 + !/foo/bar/*/
838 + /a/b/
839 + /foo/bar/baz/
840 + EOF
841 + test_cmp expect repo/.git/info/sparse-checkout
842 +'
843 +
844 +test_expect_success 'sparse-checkout list deduplicates repeated cone patterns' '
845 + rm -f repo/.git/info/sparse-checkout &&
846 + git -C repo sparse-checkout init --cone &&
847 + cat <<-\EOF >repo/.git/info/sparse-checkout &&
848 + /*
849 + !/*/
850 + /a/
851 + !/a/*/
852 + /foo/
853 + !/foo/*/
854 + /foo/bar/
855 + !/foo/bar/*/
856 + /a/b/
857 + /foo/bar/baz/
858 + /foo/bar/baz/
859 + EOF
860 + git -C repo sparse-checkout list >actual &&
861 + cat <<-\EOF >expect &&
862 + a/b
863 + foo/bar/baz
864 + EOF
865 + test_cmp expect actual
866 +'
867 +
868 test_expect_success 'malformed cone-mode patterns' '
869 git -C repo sparse-checkout init --cone &&
870 mkdir -p repo/foo/bar &&