{"thread":{"id":"64854","subject":"[RFC PATCH 0/2] Adding string_list_sort_u to replace combined calls of string_list_sort and string_list_remove_duplicates calls.","startedAt":"2026-01-22T17:15:34Z","lastAt":"2026-01-30T21:45:57Z","messageCount":24,"participants":["Amisha Chhajed","Junio C Hamano","Kristoffer Haugsbakk"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"534472","messageId":"20260122171523.94234-1-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":null,"subject":"[RFC PATCH 0/2] Adding string_list_sort_u to replace combined calls of string_list_sort and string_list_remove_duplicates calls.","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-22T17:15:21Z","receivedAt":"2026-01-22T17:15:34Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Following up with Junio's suggestion in \nhttps://lore.kernel.org/git/xmqqqzrp74q3.fsf@gitster.g/, calls to \nstring_list_remove_duplicates are almost always preceeded by string_list_sort\nwhich can be coupled into a single method.\n\nThere are places, for example the call of string_list_remove_duplicates\nin pack-objects.c where removal of that line causes no test failures,\nhowever removal of string_list_sort_u version did because removing sort\nis a more catchable behaviour by the current tests, this might improve\nthe case where we were unsure why removing string_list_remove_duplicates\ncaused no test failures.\n\nI was unable to write unit tests for this new method in this RFC PATCH\nsince I was quite unsure why no unit tests were present for string_list_sort\nand string_list_insert.\n\nAmisha Chhajed (2):\n  Adding string_list_sort_u which sorts a list then deduplicates it.\n  Replacing calls of string_list_sort and string_list_remove_duplicates\n    with the combined variant string_list_u.\n\n builtin/clone.c           | 3 +--\n builtin/fast-export.c     | 3 +--\n builtin/pack-objects.c    | 6 ++----\n builtin/sparse-checkout.c | 6 ++----\n help.c                    | 3 +--\n notes.c                   | 3 +--\n string-list.c             | 6 ++++++\n string-list.h             | 6 ++++++\n 8 files changed, 20 insertions(+), 16 deletions(-)\n\n-- \n2.51.0\n\n"},{"id":"534473","messageId":"20260122171523.94234-2-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260122171523.94234-1-amishhhaaaa@gmail.com","subject":"[RFC PATCH 1/2] Adding string_list_sort_u which sorts a list then deduplicates it.","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-22T17:15:22Z","receivedAt":"2026-01-22T17:15:45Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"string_list_remove_duplicates is almost always preceeded by\nstring_list_sort, hence adding string_list_sort_u which dedupliactes\npost sorting.\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n string-list.c | 6 ++++++\n string-list.h | 6 ++++++\n 2 files changed, 12 insertions(+)\n\ndiff --git a/string-list.c b/string-list.c\nindex 08dc00984c..020ed8fef7 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -247,6 +247,12 @@ void string_list_sort(struct string_list *list)\n \tQSORT_S(list->items, list->nr, cmp_items, &sort_ctx);\n }\n \n+void string_list_sort_u(struct string_list *list, int free_util)\n+{\n+\tstring_list_sort(list);\n+\tstring_list_remove_duplicates(list, free_util);\n+}\n+\n struct string_list_item *unsorted_string_list_lookup(struct string_list *list,\n \t\t\t\t\t\t     const char *string)\n {\ndiff --git a/string-list.h b/string-list.h\nindex fa6ba07853..3ad862a187 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -239,6 +239,12 @@ struct string_list_item *string_list_append_nodup(struct string_list *list, char\n  */\n void string_list_sort(struct string_list *list);\n \n+/**\n+ * Sort the list and then remove duplicate entries.  If free_util is true,\n+ * call free() on the util members of any items that have to be deleted.\n+ */\n+void string_list_sort_u(struct string_list *list, int free_util);\n+\n /**\n  * Like `string_list_has_string()` but for unsorted lists. Linear in\n  * size of the list.\n-- \n2.51.0\n\n"},{"id":"534474","messageId":"20260122171523.94234-3-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260122171523.94234-1-amishhhaaaa@gmail.com","subject":"[RFC PATCH 2/2] Replacing calls of string_list_sort and string_list_remove_duplicates with the combined variant string_list_u.","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-22T17:15:23Z","receivedAt":"2026-01-22T17:15:51Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n builtin/clone.c           | 3 +--\n builtin/fast-export.c     | 3 +--\n builtin/pack-objects.c    | 6 ++----\n builtin/sparse-checkout.c | 6 ++----\n help.c                    | 3 +--\n notes.c                   | 3 +--\n 6 files changed, 8 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..f05364c268 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1136,8 +1136,7 @@ int cmd_clone(int argc,\n \t\tint val;\n \n \t\t/* remove duplicates */\n-\t\tstring_list_sort(&option_recurse_submodules);\n-\t\tstring_list_remove_duplicates(&option_recurse_submodules, 0);\n+\t\tstring_list_sort_u(&option_recurse_submodules, 0);\n \n \t\t/*\n \t\t * NEEDSWORK: In a multi-working-tree world, this needs to be\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex b90da5e616..0c5d2386d8 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1118,8 +1118,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \t\t\tfree(full_name);\n \t}\n \n-\tstring_list_sort(&extra_refs);\n-\tstring_list_remove_duplicates(&extra_refs, 0);\n+\tstring_list_sort_u(&extra_refs, 0);\n }\n \n static void handle_tags_and_duplicates(struct string_list *extras)\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ca44b7894f..649dab4ed0 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3849,10 +3849,8 @@ static void read_packs_list_from_stdin(struct rev_info *revs)\n \t\tstrbuf_reset(&buf);\n \t}\n \n-\tstring_list_sort(&include_packs);\n-\tstring_list_remove_duplicates(&include_packs, 0);\n-\tstring_list_sort(&exclude_packs);\n-\tstring_list_remove_duplicates(&exclude_packs, 0);\n+\tstring_list_sort_u(&include_packs, 0);\n+\tstring_list_sort_u(&exclude_packs, 0);\n \n \trepo_for_each_pack(the_repository, p) {\n \t\tconst char *pack_name = pack_basename(p);\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..25de7692c9 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -292,8 +292,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \t\t\tstring_list_insert(&sl, pe->pattern);\n \t}\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfprintf(fp, \"/*\\n!/*/\\n\");\n \n@@ -316,8 +315,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \n \tstrbuf_release(&parent_pattern);\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfor (i = 0; i < sl.nr; i++) {\n \t\tchar *pattern = escaped_pattern(sl.items[i].string);\ndiff --git a/help.c b/help.c\nindex 20e114432d..2070095b6f 100644\n--- a/help.c\n+++ b/help.c\n@@ -420,8 +420,7 @@ void list_cmds_by_config(struct string_list *list)\n \tif (repo_config_get_string_tmp(the_repository, \"completion.commands\", &cmd_list))\n \t\treturn;\n \n-\tstring_list_sort(list);\n-\tstring_list_remove_duplicates(list, 0);\n+\tstring_list_sort_u(list, 0);\n \n \twhile (*cmd_list) {\n \t\tstruct strbuf sb = STRBUF_INIT;\ndiff --git a/notes.c b/notes.c\nindex 8e00fd8c47..090c48bbd5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -921,8 +921,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,\n \tif (string_list_add_note_lines(&sort_uniq_list, new_oid))\n \t\tgoto out;\n \tstring_list_remove_empty_items(&sort_uniq_list, 0);\n-\tstring_list_sort(&sort_uniq_list);\n-\tstring_list_remove_duplicates(&sort_uniq_list, 0);\n+\tstring_list_sort_u(&sort_uniq_list, 0);\n \n \t/* create a new blob object from sort_uniq_list */\n \tif (for_each_string_list(&sort_uniq_list,\n-- \n2.51.0\n\n"},{"id":"534506","messageId":"xmqqcy31l2s2.fsf@gitster.g","threadId":"64854","inReplyTo":"20260122171523.94234-2-amishhhaaaa@gmail.com","subject":"Re: [RFC PATCH 1/2] Adding string_list_sort_u which sorts a list then deduplicates it.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-22T22:07:41Z","receivedAt":"2026-01-22T22:07:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n\n> string_list_remove_duplicates is almost always preceeded by\n> string_list_sort, hence adding string_list_sort_u which dedupliactes\n> post sorting.\n\nThe usual way to compose a log message of this project is to\n\n - Give an observation on how the current system works in the\n   present tense (so no need to say \"Currently X is Y\", or\n   \"Previously X was Y\" to describe the state before your change;\n   just \"X is Y\" is enough), and discuss what you perceive as a\n   problem in it.\n\n - Propose a solution (optional---often, problem description\n   trivially leads to an obvious solution in reader's minds).\n\n - Give commands to somebody editing the codebase to \"make it so\",\n   instead of saying \"This commit does X\".\n\nin this order.\n\nTo those who have been intimately following the discussion, it often\nis understandable without some of the above, but we are not writing\nfor those who review the patches.  We are primarily writing for future\nreaders of \"git log\" who are not aware of the review discussion we\nhave on list, so we should give something to prepare them by setting\nthe stage and stating the objective first, before going into how the\npatch solved it.\n\nWith that in mind, perhaps something along this line ...\n\n\n    Subject: string-list: add string_list_sort_u() that mimics \"sort -u\"\n\n    Many callsites of string_list_remove_duplicates() call it\n    immediately after calling string_list_sort().  It is\n    understandable because the former requires the string-list to be\n    sorted, but at the same time, it is clear that these places are\n    sorting only to remove duplicates and for no other reason.\n\n    Introduce a helper function string_list_sort_u() that combines\n    these two calls that often appear together, to help simplify\n    these callsites.\n\n... probably?\n\nThe same comment applies to the way the other patch is explained.\n\nThanks.\n\n"},{"id":"534507","messageId":"xmqq8qdpl2op.fsf@gitster.g","threadId":"64854","inReplyTo":"20260122171523.94234-1-amishhhaaaa@gmail.com","subject":"Re: [RFC PATCH 0/2] Adding string_list_sort_u to replace combined calls of string_list_sort and string_list_remove_duplicates calls.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-22T22:09:42Z","receivedAt":"2026-01-22T22:09:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n\n> I was unable to write unit tests for this new method in this RFC PATCH\n> since I was quite unsure why no unit tests were present for string_list_sort\n> and string_list_insert.\n\nt/unit-tests/u-string-list.c has tests for remove_duplicates,\nthough.  If one discovers test coverage is lacking, it is perfectly\nfine to enhance the coverage, especially the low-level unit tests\nare ralatively cheap to run.\n\nThanks.\n"},{"id":"534509","messageId":"xmqq4iodl27p.fsf@gitster.g","threadId":"64854","inReplyTo":"20260122171523.94234-3-amishhhaaaa@gmail.com","subject":"Re: [RFC PATCH 2/2] Replacing calls of string_list_sort and string_list_remove_duplicates with the combined variant string_list_u.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-22T22:19:54Z","receivedAt":"2026-01-22T22:19:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n\n> Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n> ---\n>  builtin/clone.c           | 3 +--\n>  builtin/fast-export.c     | 3 +--\n>  builtin/pack-objects.c    | 6 ++----\n>  builtin/sparse-checkout.c | 6 ++----\n>  help.c                    | 3 +--\n>  notes.c                   | 3 +--\n>  6 files changed, 8 insertions(+), 16 deletions(-)\n\nNice to see many calls to remove_duplicates() are hidden away, so\nthat we can check the remaining ones.\n"},{"id":"534619","messageId":"20260125201500.63904-1-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260122171523.94234-1-amishhhaaaa@gmail.com","subject":"[PATCH 1/2] u-string-list: add unit tests for string-list methods","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-25T20:14:59Z","receivedAt":"2026-01-25T20:15:14Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Unit tests in u-string-list.c does not cover several methods\nin string-list, this gap in coverage makes it difficult to\nensure no regressions are introduced in future changes.\n\nAdd unit tests for the following methods to enhance coverage:\nstring_list_remove_empty_items()\nunsorted_string_list_has_string()\nunsorted_string_list_delete_item()\nstring_list_has_string()\nstring_list_insert()\nstring_list_sort()\nstring_list_remove()\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n t/unit-tests/u-string-list.c | 197 +++++++++++++++++++++++++++++++++++\n 1 file changed, 197 insertions(+)\n\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex a2457d7b1e..6b4b858330 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -243,6 +243,133 @@ void test_string_list__filter(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_has_string(struct string_list *list, const char *string, int expected)\n+{\n+\tint has_string = string_list_has_string(list, string);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_has_string(&list, \"\", 0);\n+\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_has_string(&list, \"c\", 1);\n+\tt_string_list_has_string(&list, \"d\", 0);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_insert(struct string_list *expected_strings, ...)\n+{\n+\tstruct string_list strings_to_insert = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, expected_strings);\n+\tt_vcreate_string_list_dup(&strings_to_insert, 0, ap);\n+\tva_end(ap);\n+\n+\tfor (int i = 0; i < strings_to_insert.nr; i++) {\n+\t\tstring_list_insert(&list, strings_to_insert.items[i].string);\n+\t}\n+\n+\tt_string_list_equal(&list, expected_strings);\n+\n+\tstring_list_clear(&strings_to_insert, 0);\n+\tstring_list_clear(&list, 0);\n+}\n+\n+void test_string_list__insert(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_string_list_insert(&expected_strings, NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_string_list_insert(&expected_strings, \"b\", \"a\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_insert(&expected_strings, \"c\", \"b\", \"a\", \"c\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"\", \"a\", NULL);\n+\tt_string_list_insert(&expected_strings, \"a\", \"a\", \"a\", \"\", NULL);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+}\n+\n+static void t_string_list_sort(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort(list);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"c\", \"a\", \"b\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"a\", \"a\", \"b\", \"c\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_remove(struct string_list *expected_strings, struct string_list *list, char const *str)\n+{\n+\tstring_list_remove(list, str, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"a\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"a\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"b\", \"c\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"e\");\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_duplicates(struct string_list *list, ...)\n {\n \tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n@@ -304,3 +431,73 @@ void test_string_list__remove_duplicates(void)\n \n \tt_string_list_clear(&list, 0);\n }\n+\n+static void t_string_list_remove_empty_items(struct string_list *expected_strings, struct string_list *list)\n+{\n+\tstring_list_remove_empty_items(list, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove_empty_items(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"\", \"b\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_has_string(struct string_list *list, const char *str, int expected)\n+{\n+\tint has_string = unsorted_string_list_has_string(list, str);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__unsorted_string_list_has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"d\", \"a\", NULL);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"c\", 0);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"d\", 1);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_delete_item(struct string_list *expected_list, struct string_list *list, int i)\n+{\n+\tunsorted_string_list_delete_item(list, i, 0);\n+\n+\tt_string_list_equal(list, expected_list);\n+}\n+\n+void test_string_list__unsorted_string_list_delete_item(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"d\", \"b\", \"c\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 1);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 0);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"d\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0,  \"a\", \"d\", \"c\", \"b\", \"d\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 4);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n\\ No newline at end of file\n-- \n2.51.0\n\n"},{"id":"534620","messageId":"20260125201500.63904-2-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260125201500.63904-1-amishhhaaaa@gmail.com","subject":"[PATCH 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-25T20:15:00Z","receivedAt":"2026-01-25T20:15:17Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Many callsites of string_list_remove_duplicates() call it\nimmdediately after calling string_list_sort(), understandably\nas the former requires string-list to be sorted, it is clear\nthat these places are sorting only to remove duplicates and\nfor no other reason.\n\nIntroduce a helper function string_list_sort_u that combines\nthese two calls that often appear together, to simplify\nthese callsites. Replace the current calls of those methods with\nstring_list_sort_u().\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n builtin/clone.c              |  3 +--\n builtin/fast-export.c        |  3 +--\n builtin/pack-objects.c       |  6 ++----\n builtin/sparse-checkout.c    |  6 ++----\n help.c                       |  3 +--\n notes.c                      |  3 +--\n string-list.c                |  6 ++++++\n string-list.h                |  6 ++++++\n t/unit-tests/u-string-list.c | 34 ++++++++++++++++++++++++++++++++++\n 9 files changed, 54 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..f05364c268 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1136,8 +1136,7 @@ int cmd_clone(int argc,\n \t\tint val;\n \n \t\t/* remove duplicates */\n-\t\tstring_list_sort(&option_recurse_submodules);\n-\t\tstring_list_remove_duplicates(&option_recurse_submodules, 0);\n+\t\tstring_list_sort_u(&option_recurse_submodules, 0);\n \n \t\t/*\n \t\t * NEEDSWORK: In a multi-working-tree world, this needs to be\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex b90da5e616..0c5d2386d8 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1118,8 +1118,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \t\t\tfree(full_name);\n \t}\n \n-\tstring_list_sort(&extra_refs);\n-\tstring_list_remove_duplicates(&extra_refs, 0);\n+\tstring_list_sort_u(&extra_refs, 0);\n }\n \n static void handle_tags_and_duplicates(struct string_list *extras)\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ca44b7894f..649dab4ed0 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3849,10 +3849,8 @@ static void read_packs_list_from_stdin(struct rev_info *revs)\n \t\tstrbuf_reset(&buf);\n \t}\n \n-\tstring_list_sort(&include_packs);\n-\tstring_list_remove_duplicates(&include_packs, 0);\n-\tstring_list_sort(&exclude_packs);\n-\tstring_list_remove_duplicates(&exclude_packs, 0);\n+\tstring_list_sort_u(&include_packs, 0);\n+\tstring_list_sort_u(&exclude_packs, 0);\n \n \trepo_for_each_pack(the_repository, p) {\n \t\tconst char *pack_name = pack_basename(p);\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..25de7692c9 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -292,8 +292,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \t\t\tstring_list_insert(&sl, pe->pattern);\n \t}\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfprintf(fp, \"/*\\n!/*/\\n\");\n \n@@ -316,8 +315,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \n \tstrbuf_release(&parent_pattern);\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfor (i = 0; i < sl.nr; i++) {\n \t\tchar *pattern = escaped_pattern(sl.items[i].string);\ndiff --git a/help.c b/help.c\nindex 20e114432d..2070095b6f 100644\n--- a/help.c\n+++ b/help.c\n@@ -420,8 +420,7 @@ void list_cmds_by_config(struct string_list *list)\n \tif (repo_config_get_string_tmp(the_repository, \"completion.commands\", &cmd_list))\n \t\treturn;\n \n-\tstring_list_sort(list);\n-\tstring_list_remove_duplicates(list, 0);\n+\tstring_list_sort_u(list, 0);\n \n \twhile (*cmd_list) {\n \t\tstruct strbuf sb = STRBUF_INIT;\ndiff --git a/notes.c b/notes.c\nindex 8e00fd8c47..090c48bbd5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -921,8 +921,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,\n \tif (string_list_add_note_lines(&sort_uniq_list, new_oid))\n \t\tgoto out;\n \tstring_list_remove_empty_items(&sort_uniq_list, 0);\n-\tstring_list_sort(&sort_uniq_list);\n-\tstring_list_remove_duplicates(&sort_uniq_list, 0);\n+\tstring_list_sort_u(&sort_uniq_list, 0);\n \n \t/* create a new blob object from sort_uniq_list */\n \tif (for_each_string_list(&sort_uniq_list,\ndiff --git a/string-list.c b/string-list.c\nindex 08dc00984c..020ed8fef7 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -247,6 +247,12 @@ void string_list_sort(struct string_list *list)\n \tQSORT_S(list->items, list->nr, cmp_items, &sort_ctx);\n }\n \n+void string_list_sort_u(struct string_list *list, int free_util)\n+{\n+\tstring_list_sort(list);\n+\tstring_list_remove_duplicates(list, free_util);\n+}\n+\n struct string_list_item *unsorted_string_list_lookup(struct string_list *list,\n \t\t\t\t\t\t     const char *string)\n {\ndiff --git a/string-list.h b/string-list.h\nindex fa6ba07853..3ad862a187 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -239,6 +239,12 @@ struct string_list_item *string_list_append_nodup(struct string_list *list, char\n  */\n void string_list_sort(struct string_list *list);\n \n+/**\n+ * Sort the list and then remove duplicate entries.  If free_util is true,\n+ * call free() on the util members of any items that have to be deleted.\n+ */\n+void string_list_sort_u(struct string_list *list, int free_util);\n+\n /**\n  * Like `string_list_has_string()` but for unsorted lists. Linear in\n  * size of the list.\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex 6b4b858330..f91bb60e09 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -432,6 +432,40 @@ void test_string_list__remove_duplicates(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_sort_u(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort_u(list, 0);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort_u(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort_u(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"d\", \"c\", \"c\", NULL);\n+\tt_string_list_sort_u(&list, \"a\", \"b\", \"c\", \"d\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_empty_items(struct string_list *expected_strings, struct string_list *list)\n {\n \tstring_list_remove_empty_items(list, 0);\n-- \n2.51.0\n\n"},{"id":"534621","messageId":"20260125201743.64317-1-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260122171523.94234-1-amishhhaaaa@gmail.com","subject":"[PATCH 1/2] u-string-list: add unit tests for string-list methods","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-25T20:17:42Z","receivedAt":"2026-01-25T20:17:53Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Unit tests in u-string-list.c does not cover several methods\nin string-list, this gap in coverage makes it difficult to\nensure no regressions are introduced in future changes.\n\nAdd unit tests for the following methods to enhance coverage:\nstring_list_remove_empty_items()\nunsorted_string_list_has_string()\nunsorted_string_list_delete_item()\nstring_list_has_string()\nstring_list_insert()\nstring_list_sort()\nstring_list_remove()\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n t/unit-tests/u-string-list.c | 197 +++++++++++++++++++++++++++++++++++\n 1 file changed, 197 insertions(+)\n\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex a2457d7b1e..6b4b858330 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -243,6 +243,133 @@ void test_string_list__filter(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_has_string(struct string_list *list, const char *string, int expected)\n+{\n+\tint has_string = string_list_has_string(list, string);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_has_string(&list, \"\", 0);\n+\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_has_string(&list, \"c\", 1);\n+\tt_string_list_has_string(&list, \"d\", 0);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_insert(struct string_list *expected_strings, ...)\n+{\n+\tstruct string_list strings_to_insert = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, expected_strings);\n+\tt_vcreate_string_list_dup(&strings_to_insert, 0, ap);\n+\tva_end(ap);\n+\n+\tfor (int i = 0; i < strings_to_insert.nr; i++) {\n+\t\tstring_list_insert(&list, strings_to_insert.items[i].string);\n+\t}\n+\n+\tt_string_list_equal(&list, expected_strings);\n+\n+\tstring_list_clear(&strings_to_insert, 0);\n+\tstring_list_clear(&list, 0);\n+}\n+\n+void test_string_list__insert(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_string_list_insert(&expected_strings, NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_string_list_insert(&expected_strings, \"b\", \"a\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_insert(&expected_strings, \"c\", \"b\", \"a\", \"c\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"\", \"a\", NULL);\n+\tt_string_list_insert(&expected_strings, \"a\", \"a\", \"a\", \"\", NULL);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+}\n+\n+static void t_string_list_sort(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort(list);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"c\", \"a\", \"b\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"a\", \"a\", \"b\", \"c\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_remove(struct string_list *expected_strings, struct string_list *list, char const *str)\n+{\n+\tstring_list_remove(list, str, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"a\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"a\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"b\", \"c\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"e\");\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_duplicates(struct string_list *list, ...)\n {\n \tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n@@ -304,3 +431,73 @@ void test_string_list__remove_duplicates(void)\n \n \tt_string_list_clear(&list, 0);\n }\n+\n+static void t_string_list_remove_empty_items(struct string_list *expected_strings, struct string_list *list)\n+{\n+\tstring_list_remove_empty_items(list, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove_empty_items(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"\", \"b\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_has_string(struct string_list *list, const char *str, int expected)\n+{\n+\tint has_string = unsorted_string_list_has_string(list, str);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__unsorted_string_list_has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"d\", \"a\", NULL);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"c\", 0);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"d\", 1);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_delete_item(struct string_list *expected_list, struct string_list *list, int i)\n+{\n+\tunsorted_string_list_delete_item(list, i, 0);\n+\n+\tt_string_list_equal(list, expected_list);\n+}\n+\n+void test_string_list__unsorted_string_list_delete_item(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"d\", \"b\", \"c\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 1);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 0);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"d\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0,  \"a\", \"d\", \"c\", \"b\", \"d\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 4);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n\\ No newline at end of file\n-- \n2.51.0\n\n"},{"id":"534622","messageId":"20260125201743.64317-2-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260125201743.64317-1-amishhhaaaa@gmail.com","subject":"[PATCH 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-25T20:17:43Z","receivedAt":"2026-01-25T20:18:04Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Many callsites of string_list_remove_duplicates() call it\nimmdediately after calling string_list_sort(), understandably\nas the former requires string-list to be sorted, it is clear\nthat these places are sorting only to remove duplicates and\nfor no other reason.\n\nIntroduce a helper function string_list_sort_u that combines\nthese two calls that often appear together, to simplify\nthese callsites. Replace the current calls of those methods with\nstring_list_sort_u().\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n builtin/clone.c              |  3 +--\n builtin/fast-export.c        |  3 +--\n builtin/pack-objects.c       |  6 ++----\n builtin/sparse-checkout.c    |  6 ++----\n help.c                       |  3 +--\n notes.c                      |  3 +--\n string-list.c                |  6 ++++++\n string-list.h                |  6 ++++++\n t/unit-tests/u-string-list.c | 34 ++++++++++++++++++++++++++++++++++\n 9 files changed, 54 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..f05364c268 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1136,8 +1136,7 @@ int cmd_clone(int argc,\n \t\tint val;\n \n \t\t/* remove duplicates */\n-\t\tstring_list_sort(&option_recurse_submodules);\n-\t\tstring_list_remove_duplicates(&option_recurse_submodules, 0);\n+\t\tstring_list_sort_u(&option_recurse_submodules, 0);\n \n \t\t/*\n \t\t * NEEDSWORK: In a multi-working-tree world, this needs to be\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex b90da5e616..0c5d2386d8 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1118,8 +1118,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \t\t\tfree(full_name);\n \t}\n \n-\tstring_list_sort(&extra_refs);\n-\tstring_list_remove_duplicates(&extra_refs, 0);\n+\tstring_list_sort_u(&extra_refs, 0);\n }\n \n static void handle_tags_and_duplicates(struct string_list *extras)\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ca44b7894f..649dab4ed0 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3849,10 +3849,8 @@ static void read_packs_list_from_stdin(struct rev_info *revs)\n \t\tstrbuf_reset(&buf);\n \t}\n \n-\tstring_list_sort(&include_packs);\n-\tstring_list_remove_duplicates(&include_packs, 0);\n-\tstring_list_sort(&exclude_packs);\n-\tstring_list_remove_duplicates(&exclude_packs, 0);\n+\tstring_list_sort_u(&include_packs, 0);\n+\tstring_list_sort_u(&exclude_packs, 0);\n \n \trepo_for_each_pack(the_repository, p) {\n \t\tconst char *pack_name = pack_basename(p);\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..25de7692c9 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -292,8 +292,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \t\t\tstring_list_insert(&sl, pe->pattern);\n \t}\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfprintf(fp, \"/*\\n!/*/\\n\");\n \n@@ -316,8 +315,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \n \tstrbuf_release(&parent_pattern);\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfor (i = 0; i < sl.nr; i++) {\n \t\tchar *pattern = escaped_pattern(sl.items[i].string);\ndiff --git a/help.c b/help.c\nindex 20e114432d..2070095b6f 100644\n--- a/help.c\n+++ b/help.c\n@@ -420,8 +420,7 @@ void list_cmds_by_config(struct string_list *list)\n \tif (repo_config_get_string_tmp(the_repository, \"completion.commands\", &cmd_list))\n \t\treturn;\n \n-\tstring_list_sort(list);\n-\tstring_list_remove_duplicates(list, 0);\n+\tstring_list_sort_u(list, 0);\n \n \twhile (*cmd_list) {\n \t\tstruct strbuf sb = STRBUF_INIT;\ndiff --git a/notes.c b/notes.c\nindex 8e00fd8c47..090c48bbd5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -921,8 +921,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,\n \tif (string_list_add_note_lines(&sort_uniq_list, new_oid))\n \t\tgoto out;\n \tstring_list_remove_empty_items(&sort_uniq_list, 0);\n-\tstring_list_sort(&sort_uniq_list);\n-\tstring_list_remove_duplicates(&sort_uniq_list, 0);\n+\tstring_list_sort_u(&sort_uniq_list, 0);\n \n \t/* create a new blob object from sort_uniq_list */\n \tif (for_each_string_list(&sort_uniq_list,\ndiff --git a/string-list.c b/string-list.c\nindex 08dc00984c..020ed8fef7 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -247,6 +247,12 @@ void string_list_sort(struct string_list *list)\n \tQSORT_S(list->items, list->nr, cmp_items, &sort_ctx);\n }\n \n+void string_list_sort_u(struct string_list *list, int free_util)\n+{\n+\tstring_list_sort(list);\n+\tstring_list_remove_duplicates(list, free_util);\n+}\n+\n struct string_list_item *unsorted_string_list_lookup(struct string_list *list,\n \t\t\t\t\t\t     const char *string)\n {\ndiff --git a/string-list.h b/string-list.h\nindex fa6ba07853..3ad862a187 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -239,6 +239,12 @@ struct string_list_item *string_list_append_nodup(struct string_list *list, char\n  */\n void string_list_sort(struct string_list *list);\n \n+/**\n+ * Sort the list and then remove duplicate entries.  If free_util is true,\n+ * call free() on the util members of any items that have to be deleted.\n+ */\n+void string_list_sort_u(struct string_list *list, int free_util);\n+\n /**\n  * Like `string_list_has_string()` but for unsorted lists. Linear in\n  * size of the list.\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex 6b4b858330..f91bb60e09 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -432,6 +432,40 @@ void test_string_list__remove_duplicates(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_sort_u(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort_u(list, 0);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort_u(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort_u(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"d\", \"c\", \"c\", NULL);\n+\tt_string_list_sort_u(&list, \"a\", \"b\", \"c\", \"d\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_empty_items(struct string_list *expected_strings, struct string_list *list)\n {\n \tstring_list_remove_empty_items(list, 0);\n-- \n2.51.0\n\n"},{"id":"534623","messageId":"CAPvEtreqaocdrHf0R+kMxhWXJQnDqV+4jKWd8UHqZ3zzfZBE+Q@mail.gmail.com","threadId":"64854","inReplyTo":"xmqqcy31l2s2.fsf@gitster.g","subject":"Re: [RFC PATCH 1/2] Adding string_list_sort_u which sorts a list then deduplicates it.","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-25T20:23:08Z","receivedAt":"2026-01-25T20:23:20Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"On Fri, 23 Jan 2026 at 03:37, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n>\n> > string_list_remove_duplicates is almost always preceeded by\n> > string_list_sort, hence adding string_list_sort_u which dedupliactes\n> > post sorting.\n>\n> The usual way to compose a log message of this project is to\n>\n>  - Give an observation on how the current system works in the\n>    present tense (so no need to say \"Currently X is Y\", or\n>    \"Previously X was Y\" to describe the state before your change;\n>    just \"X is Y\" is enough), and discuss what you perceive as a\n>    problem in it.\n>\n>  - Propose a solution (optional---often, problem description\n>    trivially leads to an obvious solution in reader's minds).\n>\n>  - Give commands to somebody editing the codebase to \"make it so\",\n>    instead of saying \"This commit does X\".\n>\n> in this order.\n>\n> To those who have been intimately following the discussion, it often\n> is understandable without some of the above, but we are not writing\n> for those who review the patches.  We are primarily writing for future\n> readers of \"git log\" who are not aware of the review discussion we\n> have on list, so we should give something to prepare them by setting\n> the stage and stating the objective first, before going into how the\n> patch solved it.\n>\n> With that in mind, perhaps something along this line ...\n>\n>\n>     Subject: string-list: add string_list_sort_u() that mimics \"sort -u\"\n>\n>     Many callsites of string_list_remove_duplicates() call it\n>     immediately after calling string_list_sort().  It is\n>     understandable because the former requires the string-list to be\n>     sorted, but at the same time, it is clear that these places are\n>     sorting only to remove duplicates and for no other reason.\n>\n>     Introduce a helper function string_list_sort_u() that combines\n>     these two calls that often appear together, to help simplify\n>     these callsites.\n>\n> ... probably?\n>\n> The same comment applies to the way the other patch is explained.\n>\n> Thanks.\n>\n\nVery helpful, thank you so much, i will keep in mind.\n"},{"id":"534635","messageId":"xmqqpl6wkftw.fsf@gitster.g","threadId":"64854","inReplyTo":"20260125201500.63904-1-amishhhaaaa@gmail.com","subject":"Re: [PATCH 1/2] u-string-list: add unit tests for string-list methods","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-26T07:12:27Z","receivedAt":"2026-01-26T07:12:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n\n> Unit tests in u-string-list.c does not cover several methods\n> in string-list, this gap in coverage makes it difficult to\n> ensure no regressions are introduced in future changes.\n>\n> Add unit tests for the following methods to enhance coverage:\n> string_list_remove_empty_items()\n> unsorted_string_list_has_string()\n> unsorted_string_list_delete_item()\n> string_list_has_string()\n> string_list_insert()\n> string_list_sort()\n> string_list_remove()\n>\n> Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n> ---\n>  t/unit-tests/u-string-list.c | 197 +++++++++++++++++++++++++++++++++++\n>  1 file changed, 197 insertions(+)\n>\n> diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\n> index a2457d7b1e..6b4b858330 100644\n> --- a/t/unit-tests/u-string-list.c\n> +++ b/t/unit-tests/u-string-list.c\n> @@ -243,6 +243,133 @@ void test_string_list__filter(void)\n> ...\n> +static void t_string_list_insert(struct string_list *expected_strings, ...)\n> +{\n> +\tstruct string_list strings_to_insert = STRING_LIST_INIT_DUP;\n> +\tstruct string_list list = STRING_LIST_INIT_DUP;\n> +\tva_list ap;\n> +\n> +\tva_start(ap, expected_strings);\n> +\tt_vcreate_string_list_dup(&strings_to_insert, 0, ap);\n> +\tva_end(ap);\n> +\n> +\tfor (int i = 0; i < strings_to_insert.nr; i++) {\n\nAs strings_to_insert.nr is of type size_t, even though int is plenty\nlarge enough in practice, the compiler will complain.\n\n> +\t\tstring_list_insert(&list, strings_to_insert.items[i].string);\n> +\t}\n\nAlso, lose the {} around a single statement block.\n"},{"id":"534682","messageId":"20260126185604.90089-1-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260122171523.94234-1-amishhhaaaa@gmail.com","subject":"[PATCH v2 1/2] u-string-list: add unit tests for string-list methods","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-26T18:56:03Z","receivedAt":"2026-01-26T18:56:19Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Unit tests in u-string-list.c does not cover several methods\nin string-list, this gap in coverage makes it difficult to\nensure no regressions are introduced in future changes.\n\nAdd unit tests for the following methods to enhance coverage:\nstring_list_remove_empty_items()\nunsorted_string_list_has_string()\nunsorted_string_list_delete_item()\nstring_list_has_string()\nstring_list_insert()\nstring_list_sort()\nstring_list_remove()\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n t/unit-tests/u-string-list.c | 196 +++++++++++++++++++++++++++++++++++\n 1 file changed, 196 insertions(+)\n\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex a2457d7b1e..6b2b16671c 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -243,6 +243,132 @@ void test_string_list__filter(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_has_string(struct string_list *list, const char *string, int expected)\n+{\n+\tint has_string = string_list_has_string(list, string);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_has_string(&list, \"\", 0);\n+\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_has_string(&list, \"c\", 1);\n+\tt_string_list_has_string(&list, \"d\", 0);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_insert(struct string_list *expected_strings, ...)\n+{\n+\tstruct string_list strings_to_insert = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, expected_strings);\n+\tt_vcreate_string_list_dup(&strings_to_insert, 0, ap);\n+\tva_end(ap);\n+\n+\tfor (size_t i = 0; i < strings_to_insert.nr; i++)\n+\t\tstring_list_insert(&list, strings_to_insert.items[i].string);\n+\n+\tt_string_list_equal(&list, expected_strings);\n+\n+\tstring_list_clear(&strings_to_insert, 0);\n+\tstring_list_clear(&list, 0);\n+}\n+\n+void test_string_list__insert(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_string_list_insert(&expected_strings, NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_string_list_insert(&expected_strings, \"b\", \"a\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_insert(&expected_strings, \"c\", \"b\", \"a\", \"c\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"\", \"a\", NULL);\n+\tt_string_list_insert(&expected_strings, \"a\", \"a\", \"a\", \"\", NULL);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+}\n+\n+static void t_string_list_sort(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort(list);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"c\", \"a\", \"b\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"a\", \"a\", \"b\", \"c\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_remove(struct string_list *expected_strings, struct string_list *list, char const *str)\n+{\n+\tstring_list_remove(list, str, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"a\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"a\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"b\", \"c\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"e\");\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_duplicates(struct string_list *list, ...)\n {\n \tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n@@ -304,3 +430,73 @@ void test_string_list__remove_duplicates(void)\n \n \tt_string_list_clear(&list, 0);\n }\n+\n+static void t_string_list_remove_empty_items(struct string_list *expected_strings, struct string_list *list)\n+{\n+\tstring_list_remove_empty_items(list, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove_empty_items(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"\", \"b\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_has_string(struct string_list *list, const char *str, int expected)\n+{\n+\tint has_string = unsorted_string_list_has_string(list, str);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__unsorted_string_list_has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"d\", \"a\", NULL);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"c\", 0);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"d\", 1);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_delete_item(struct string_list *expected_list, struct string_list *list, int i)\n+{\n+\tunsorted_string_list_delete_item(list, i, 0);\n+\n+\tt_string_list_equal(list, expected_list);\n+}\n+\n+void test_string_list__unsorted_string_list_delete_item(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"d\", \"b\", \"c\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 1);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 0);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"d\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0,  \"a\", \"d\", \"c\", \"b\", \"d\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 4);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n\\ No newline at end of file\n-- \n2.51.0\n\n"},{"id":"534683","messageId":"20260126185604.90089-2-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260126185604.90089-1-amishhhaaaa@gmail.com","subject":"[PATCH v2 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-26T18:56:04Z","receivedAt":"2026-01-26T18:56:28Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Many callsites of string_list_remove_duplicates() call it\nimmdediately after calling string_list_sort(), understandably\nas the former requires string-list to be sorted, it is clear\nthat these places are sorting only to remove duplicates and\nfor no other reason.\n\nIntroduce a helper function string_list_sort_u that combines\nthese two calls that often appear together, to simplify\nthese callsites. Replace the current calls of those methods with\nstring_list_sort_u().\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n builtin/clone.c              |  3 +--\n builtin/fast-export.c        |  3 +--\n builtin/pack-objects.c       |  6 ++----\n builtin/sparse-checkout.c    |  6 ++----\n help.c                       |  3 +--\n notes.c                      |  3 +--\n string-list.c                |  6 ++++++\n string-list.h                |  6 ++++++\n t/unit-tests/u-string-list.c | 34 ++++++++++++++++++++++++++++++++++\n 9 files changed, 54 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..f05364c268 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1136,8 +1136,7 @@ int cmd_clone(int argc,\n \t\tint val;\n \n \t\t/* remove duplicates */\n-\t\tstring_list_sort(&option_recurse_submodules);\n-\t\tstring_list_remove_duplicates(&option_recurse_submodules, 0);\n+\t\tstring_list_sort_u(&option_recurse_submodules, 0);\n \n \t\t/*\n \t\t * NEEDSWORK: In a multi-working-tree world, this needs to be\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex b90da5e616..0c5d2386d8 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1118,8 +1118,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \t\t\tfree(full_name);\n \t}\n \n-\tstring_list_sort(&extra_refs);\n-\tstring_list_remove_duplicates(&extra_refs, 0);\n+\tstring_list_sort_u(&extra_refs, 0);\n }\n \n static void handle_tags_and_duplicates(struct string_list *extras)\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ca44b7894f..649dab4ed0 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3849,10 +3849,8 @@ static void read_packs_list_from_stdin(struct rev_info *revs)\n \t\tstrbuf_reset(&buf);\n \t}\n \n-\tstring_list_sort(&include_packs);\n-\tstring_list_remove_duplicates(&include_packs, 0);\n-\tstring_list_sort(&exclude_packs);\n-\tstring_list_remove_duplicates(&exclude_packs, 0);\n+\tstring_list_sort_u(&include_packs, 0);\n+\tstring_list_sort_u(&exclude_packs, 0);\n \n \trepo_for_each_pack(the_repository, p) {\n \t\tconst char *pack_name = pack_basename(p);\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..25de7692c9 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -292,8 +292,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \t\t\tstring_list_insert(&sl, pe->pattern);\n \t}\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfprintf(fp, \"/*\\n!/*/\\n\");\n \n@@ -316,8 +315,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \n \tstrbuf_release(&parent_pattern);\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfor (i = 0; i < sl.nr; i++) {\n \t\tchar *pattern = escaped_pattern(sl.items[i].string);\ndiff --git a/help.c b/help.c\nindex 20e114432d..2070095b6f 100644\n--- a/help.c\n+++ b/help.c\n@@ -420,8 +420,7 @@ void list_cmds_by_config(struct string_list *list)\n \tif (repo_config_get_string_tmp(the_repository, \"completion.commands\", &cmd_list))\n \t\treturn;\n \n-\tstring_list_sort(list);\n-\tstring_list_remove_duplicates(list, 0);\n+\tstring_list_sort_u(list, 0);\n \n \twhile (*cmd_list) {\n \t\tstruct strbuf sb = STRBUF_INIT;\ndiff --git a/notes.c b/notes.c\nindex 8e00fd8c47..090c48bbd5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -921,8 +921,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,\n \tif (string_list_add_note_lines(&sort_uniq_list, new_oid))\n \t\tgoto out;\n \tstring_list_remove_empty_items(&sort_uniq_list, 0);\n-\tstring_list_sort(&sort_uniq_list);\n-\tstring_list_remove_duplicates(&sort_uniq_list, 0);\n+\tstring_list_sort_u(&sort_uniq_list, 0);\n \n \t/* create a new blob object from sort_uniq_list */\n \tif (for_each_string_list(&sort_uniq_list,\ndiff --git a/string-list.c b/string-list.c\nindex 08dc00984c..020ed8fef7 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -247,6 +247,12 @@ void string_list_sort(struct string_list *list)\n \tQSORT_S(list->items, list->nr, cmp_items, &sort_ctx);\n }\n \n+void string_list_sort_u(struct string_list *list, int free_util)\n+{\n+\tstring_list_sort(list);\n+\tstring_list_remove_duplicates(list, free_util);\n+}\n+\n struct string_list_item *unsorted_string_list_lookup(struct string_list *list,\n \t\t\t\t\t\t     const char *string)\n {\ndiff --git a/string-list.h b/string-list.h\nindex fa6ba07853..3ad862a187 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -239,6 +239,12 @@ struct string_list_item *string_list_append_nodup(struct string_list *list, char\n  */\n void string_list_sort(struct string_list *list);\n \n+/**\n+ * Sort the list and then remove duplicate entries.  If free_util is true,\n+ * call free() on the util members of any items that have to be deleted.\n+ */\n+void string_list_sort_u(struct string_list *list, int free_util);\n+\n /**\n  * Like `string_list_has_string()` but for unsorted lists. Linear in\n  * size of the list.\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex 6b2b16671c..9d11a2f3fb 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -431,6 +431,40 @@ void test_string_list__remove_duplicates(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_sort_u(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort_u(list, 0);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort_u(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort_u(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"d\", \"c\", \"c\", NULL);\n+\tt_string_list_sort_u(&list, \"a\", \"b\", \"c\", \"d\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_empty_items(struct string_list *expected_strings, struct string_list *list)\n {\n \tstring_list_remove_empty_items(list, 0);\n-- \n2.51.0\n\n"},{"id":"534684","messageId":"CAPvEtrcLrrRhOi2OCaQzD6xNSHbyv38cr5-WLRPg_HGW5UKzAg@mail.gmail.com","threadId":"64854","inReplyTo":"xmqqpl6wkftw.fsf@gitster.g","subject":"Re: [PATCH 1/2] u-string-list: add unit tests for string-list methods","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-26T18:57:33Z","receivedAt":"2026-01-26T18:57:45Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"On Mon, 26 Jan 2026 at 12:42, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n>\n> > Unit tests in u-string-list.c does not cover several methods\n> > in string-list, this gap in coverage makes it difficult to\n> > ensure no regressions are introduced in future changes.\n> >\n> > Add unit tests for the following methods to enhance coverage:\n> > string_list_remove_empty_items()\n> > unsorted_string_list_has_string()\n> > unsorted_string_list_delete_item()\n> > string_list_has_string()\n> > string_list_insert()\n> > string_list_sort()\n> > string_list_remove()\n> >\n> > Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n> > ---\n> >  t/unit-tests/u-string-list.c | 197 +++++++++++++++++++++++++++++++++++\n> >  1 file changed, 197 insertions(+)\n> >\n> > diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\n> > index a2457d7b1e..6b4b858330 100644\n> > --- a/t/unit-tests/u-string-list.c\n> > +++ b/t/unit-tests/u-string-list.c\n> > @@ -243,6 +243,133 @@ void test_string_list__filter(void)\n> > ...\n> > +static void t_string_list_insert(struct string_list *expected_strings, ...)\n> > +{\n> > +     struct string_list strings_to_insert = STRING_LIST_INIT_DUP;\n> > +     struct string_list list = STRING_LIST_INIT_DUP;\n> > +     va_list ap;\n> > +\n> > +     va_start(ap, expected_strings);\n> > +     t_vcreate_string_list_dup(&strings_to_insert, 0, ap);\n> > +     va_end(ap);\n> > +\n> > +     for (int i = 0; i < strings_to_insert.nr; i++) {\n>\n> As strings_to_insert.nr is of type size_t, even though int is plenty\n> large enough in practice, the compiler will complain.\n>\n> > +             string_list_insert(&list, strings_to_insert.items[i].string);\n> > +     }\n>\n> Also, lose the {} around a single statement block.\n\nDone, Thanks for the review.\n"},{"id":"534685","messageId":"xmqqldhki3xp.fsf@gitster.g","threadId":"64854","inReplyTo":"CAPvEtrcLrrRhOi2OCaQzD6xNSHbyv38cr5-WLRPg_HGW5UKzAg@mail.gmail.com","subject":"Re: [PATCH 1/2] u-string-list: add unit tests for string-list methods","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-26T19:12:18Z","receivedAt":"2026-01-26T19:17:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n\n>> > +     for (int i = 0; i < strings_to_insert.nr; i++) {\n>>\n>> As strings_to_insert.nr is of type size_t, even though int is plenty\n>> large enough in practice, the compiler will complain.\n>>\n>> > +             string_list_insert(&list, strings_to_insert.items[i].string);\n>> > +     }\n>>\n>> Also, lose the {} around a single statement block.\n>\n> Done, Thanks for the review.\n\nHeh, that wasn't even a review but a knee-jerk reaction to compiler\nwarnings.\n\n"},{"id":"534686","messageId":"xmqqa4y0i25m.fsf@gitster.g","threadId":"64854","inReplyTo":"20260126185604.90089-1-amishhhaaaa@gmail.com","subject":"Re: [PATCH v2 1/2] u-string-list: add unit tests for string-list methods","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-26T19:50:45Z","receivedAt":"2026-01-26T19:50:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n\n> +void test_string_list__remove(void)\n> +{\n> +\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +\tstruct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +\tt_create_string_list_dup(&expected_strings, 0, NULL);\n> +\tt_create_string_list_dup(&list, 0, NULL);\n> +\tt_string_list_remove(&expected_strings, &list, \"\");\n> +\n> +\tt_create_string_list_dup(&expected_strings, 0, \"a\", NULL);\n> +\tt_create_string_list_dup(&list, 0, \"a\", \"a\", NULL);\n> +\tt_string_list_remove(&expected_strings, &list, \"a\");\n\nNot a complaint, not a suggestion to change anything, but just an\nobservation.  While \"remove\" requires the string-list to be sorted,\nits implementation does not seem to care if you by mistake fed an\nunsorted string list.\n\nAfter seeing this particular test that feeds a list with two \"a\"\nand expects in the resulting list a single \"a\", I naturally wondered\nwhich one of these two \"a\" survives and which one is dropped.\n\n\"remove\" removes only one matching element that is picked at random\namong the duplicates, but because the input is expected to be\nsorted, these duplicate elements sit next to each other forming a\nsingle strand of identical pearls.  The end result of picking one of\nthese pearls out would not be different no matter which one of them\nyou pick.  So the answer to my \"which one of these 'a'?\" question\nturns out to be \"you cannot tell, but it does not matter\" ;-)\n\n> +static void t_string_list_remove_empty_items(struct string_list *expected_strings, struct string_list *list)\n> +{\n> +\tstring_list_remove_empty_items(list, 0);\n> +\tt_string_list_equal(list, expected_strings);\n> +}\n> +\n> +void test_string_list__remove_empty_items(void)\n> +{\n> +\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +\tstruct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +\tt_create_string_list_dup(&expected_strings, 0, NULL);\n> +\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", NULL);\n> +\tt_string_list_remove_empty_items(&expected_strings, &list);\n\nAgain, not a complaint, not a suggestion to change anything, but\njust an observation.  As we saw earlier, \"remove\" is \"remove just\none of many\", but \"remove_empty\" is \"remove all empties\".  Simply\nmakes me wonder if the API looked more sane if we had \"remove_all\"\nwhose signature is the same as string_list_remove().\n\n> +static void t_string_list_unsorted_string_list_delete_item(struct string_list *expected_list, struct string_list *list, int i)\n\nThis is a way overlong line.\n\n> +{\n> +\tunsorted_string_list_delete_item(list, i, 0);\n> +\n> +\tt_string_list_equal(list, expected_list);\n> +}\n> +\n> +void test_string_list__unsorted_string_list_delete_item(void)\n> +{\n> +\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +\tstruct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"c\", \"b\", NULL);\n> +\tt_create_string_list_dup(&list, 0, \"a\", \"d\", \"b\", \"c\", NULL);\n> +\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 1);\n\nThis demonstrates one peculiar aspect of this \"delete item from\nunsorted list\" API function very well.  If one is expected to name\nan element to delete by specifying its position in the list, it is\nnatural to expect that the elements in the list to be ordered in\nsome way that is meaningful to the application [*], and the API is\nnot expected to shuffle the resulting list in such a way that makes\nfurther use of the list cumbersome.  Yet, the function does exactly\nthat by moving the element at the end of the list to the place the\nlocation of the deleted element.\n\n\tSide note: [*] The \"unsorted\" in the name of the function is\n\ta reference to the fact that the elements are not sorted by\n\tthe natural order string-list API uses to allow it to binary\n\tsearch; it does not mean the elements are entirely randomly\n\tthrown in and it shouldn't mean that the application cannot\n\trely on\n\nThe only caller of this function is git.c::list_cmds() that is asked\nto remove the helper binaries (i.e., those whose name contains\n\"--\"), so even though git.c::commands[] list is in sorted order, and\nthe list_builtins() function slurps them into a working list with\nstring_list_append(), processing \"nohelpers\" will splinkle command\nnames from near the tail of the list into random places in the\nmiddle of the list.\n\n> +\tt_string_list_clear(&expected_strings, 0);\n> +\tt_string_list_clear(&list, 0);\n> +}\n> \\ No newline at end of file\n\nDon't.  Always end a text file with a complete line, please.\n\n"},{"id":"534687","messageId":"xmqq1pjci16l.fsf@gitster.g","threadId":"64854","inReplyTo":"20260126185604.90089-2-amishhhaaaa@gmail.com","subject":"Re: [PATCH v2 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-26T20:11:46Z","receivedAt":"2026-01-26T20:11:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Amisha Chhajed <amishhhaaaa@gmail.com> writes:\n\n> Many callsites of string_list_remove_duplicates() call it\n> immdediately after calling string_list_sort(), understandably\n> as the former requires string-list to be sorted, it is clear\n> that these places are sorting only to remove duplicates and\n> for no other reason.\n>\n> Introduce a helper function string_list_sort_u that combines\n> these two calls that often appear together, to simplify\n> these callsites. Replace the current calls of those methods with\n> string_list_sort_u().\n\nAfter this, only two callers of string_list_remove_duplicates()\nremain in the codebase.\n\nThe one in builtin/fetch.c::cmd_fetch() smells somewhat fishy.  It\nprepares a string_list \"list\", populates it with for_each_remote()\nby appending remotes found in the configuration when asked to do\n\"--all\", or append named ones with \"--multiple\", and then calls\n\"remove duplicates\" without sorting the resulting list first.\n\n - A test should be able to demonstrate that the call to\n   string_list_remove_duplicates() is not operating on a sorted\n   string list.\n\n - Once a breakage is demonstrated, we need to devise a fix.\n   Sorting the string list before removing would certainly fix the\n   duplicates removal, but it will change the order in which the\n   remotes are consulted.  I think it is currently \"whatever order\n   these remotes appear in your configuration file(s)\", but that\n   does not mean it is a random order.  It is very likely that they\n   are in the order the user has learned to expect the remotes are\n   to be consulted, so \"sort and then dedup\" might appear as a\n   regression in behaviour.  I dunno.\n\nThe one in builtin/help.c::list_config_help() is somewhat fishy as\nwell.  I didn't read it too carefully, but it walks over keys which\nis in sorted string_list, and sometimes pushes the key intact to\nkeys_uniq, and some other times munges the key and pushes the result\nto keys_uniq.  I do not know if presence of these these munged keys\nin the keys_uniq string list breaks the sortedness of keys_uniq.\nIf keys_uniq is *not* sorted, then running \"remove duplicates\" would\nbe broken, of course.  Again, a test should be able to demonstrate\nif this is the case, and we should fix it as well if it is broken.\n\nThanks.\n\n"},{"id":"534699","messageId":"CAPvEtrfEZXHxcDf=z60ODfUA8cS81rhF1y7KEZApEBby7aCa1A@mail.gmail.com","threadId":"64854","inReplyTo":"xmqq1pjci16l.fsf@gitster.g","subject":"Re: [PATCH v2 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-27T01:27:16Z","receivedAt":"2026-01-27T01:27:28Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"> The one in builtin/fetch.c::cmd_fetch() smells somewhat fishy.  It\n> prepares a string_list \"list\", populates it with for_each_remote()\n> by appending remotes found in the configuration when asked to do\n> \"--all\", or append named ones with \"--multiple\", and then calls\n> \"remove duplicates\" without sorting the resulting list first.\n>\n>  - A test should be able to demonstrate that the call to\n>    string_list_remove_duplicates() is not operating on a sorted\n>    string list.\n>\n>  - Once a breakage is demonstrated, we need to devise a fix.\n>    Sorting the string list before removing would certainly fix the\n>    duplicates removal, but it will change the order in which the\n>    remotes are consulted.  I think it is currently \"whatever order\n>    these remotes appear in your configuration file(s)\", but that\n>    does not mean it is a random order.  It is very likely that they\n>    are in the order the user has learned to expect the remotes are\n>    to be consulted, so \"sort and then dedup\" might appear as a\n>    regression in behaviour.  I dunno.\n\nwas able to make the behavior fail on test,\nfrom file t5506-remote-groups.sh,\n\ntest_expect_success 'group with non-adjacent duplicate remotes causes\nduplicate fetches (expected-to-fail)' '\nmark fetch-dup &&\nupdate_repos &&\ngit config --add remotes.dup one &&\ngit config --add remotes.dup two &&\ngit config --add remotes.dup one &&\nrm -f .git/FETCH_HEAD &&\ngit -c fetch.parallel=3 remote update dup &&\nrepo_fetched two\n'\ngave log with command,\nmake -C t T=t5506-remote-groups.sh GIT_TEST_OPTS=\"-v\"\nfrom git/,\n\nFetching one\nFetching two\nFetching one\nFrom one\n   d1132c8..cceba7b  main       -> one/main\nFrom two\n   72c2514..57c13f4  main       -> two/main\nFrom one\n   d1132c8..cceba7b  main       -> one/main\n\nerror: fetching ref refs/remotes/one/main failed: incorrect old value provided\ncould not fetch 'one' (exit code: 1)\n\nDid not see any test failures post replacing this call with string_list_sort_u()\nbut from the code, order of the list matters, and i think the expected\nbehaviour is\nif any duplicate the first call of the item should only remain but i\nam unsure if it is\nachievable with the current sort and remove duplicate methods this is more of a,\nif current_item in seen_set:\n   pass\nelse:\n   add(list, current_item)\n   add(seen_set, current_item)\nthis preserves the order and maintains the time complexity and only\nkeeps the first occurence.\n\n\n> The one in builtin/help.c::list_config_help() is somewhat fishy as\n> well.  I didn't read it too carefully, but it walks over keys which\n> is in sorted string_list, and sometimes pushes the key intact to\n> keys_uniq, and some other times munges the key and pushes the result\n> to keys_uniq.  I do not know if presence of these these munged keys\n> in the keys_uniq string list breaks the sortedness of keys_uniq.\n> If keys_uniq is *not* sorted, then running \"remove duplicates\" would\n> be broken, of course.  Again, a test should be able to demonstrate\n> if this is the case, and we should fix it as well if it is broken.\n\nThis one is a bit more complex,\nthere is a very specific case that would result in un-sorted behaviour\nof the string-list\nthat is of form,\nsorted: [aa*., aa.b]\npost processing in &keys_uniq: [aa*, aa] (this is unsorted)\nbecause ASCII value of * < . and the precedence in our code is . < * [1]\nbut this still works as remove_duplicates does not depend strictly on the sorted\nproperty as we remove adjacent similar elements which would also work if similar\nitems are somehow grouped together, which they are under the conditions\nof our code.\n\nbut sorted(x) != sorted(f(x)), and the processing we are doing also\ndoes not depend\non the order of the elements so maybe shifting the sort below with\nremove_duplicates\nmight work as a solution and would make the behaviour deterministic,\nsaw no failures\npost shifting the sort down with it.\n\n[1] Snippet from fetch.c\nif (dot)\n   cut = dot;\nelse if (wildcard && !tag)\n   cut = wildcard;\nelse if (!wildcard && tag)\n   cut = tag;\nelse\n   cut = wildcard < tag ? wildcard : tag;\n"},{"id":"534809","messageId":"20260129121220.69267-1-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260125201500.63904-2-amishhhaaaa@gmail.com","subject":"[PATCH v3 1/2] u-string-list: add unit tests for string-list methods","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-29T12:12:19Z","receivedAt":"2026-01-29T12:12:30Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Unit tests in u-string-list.c does not cover several methods\nin string-list, this gap in coverage makes it difficult to\nensure no regressions are introduced in future changes.\n\nAdd unit tests for the following methods to enhance coverage:\nstring_list_remove_empty_items()\nunsorted_string_list_has_string()\nunsorted_string_list_delete_item()\nstring_list_has_string()\nstring_list_insert()\nstring_list_sort()\nstring_list_remove()\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n t/unit-tests/u-string-list.c | 209 +++++++++++++++++++++++++++++++++++\n 1 file changed, 209 insertions(+)\n\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex a2457d7b1e..d469a06eca 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -243,6 +243,138 @@ void test_string_list__filter(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_has_string(\n+\tstruct string_list *list,\n+\tconst char *string,\n+\tint expected)\n+{\n+\tint has_string = string_list_has_string(list, string);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_has_string(&list, \"\", 0);\n+\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_has_string(&list, \"c\", 1);\n+\tt_string_list_has_string(&list, \"d\", 0);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_insert(struct string_list *expected_strings, ...)\n+{\n+\tstruct string_list strings_to_insert = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, expected_strings);\n+\tt_vcreate_string_list_dup(&strings_to_insert, 0, ap);\n+\tva_end(ap);\n+\n+\tfor (size_t i = 0; i < strings_to_insert.nr; i++)\n+\t\tstring_list_insert(&list, strings_to_insert.items[i].string);\n+\n+\tt_string_list_equal(&list, expected_strings);\n+\n+\tstring_list_clear(&strings_to_insert, 0);\n+\tstring_list_clear(&list, 0);\n+}\n+\n+void test_string_list__insert(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_string_list_insert(&expected_strings, NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_string_list_insert(&expected_strings, \"b\", \"a\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", NULL);\n+\tt_string_list_insert(&expected_strings, \"c\", \"b\", \"a\", \"c\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"\", \"a\", NULL);\n+\tt_string_list_insert(&expected_strings, \"a\", \"a\", \"a\", \"\", NULL);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+}\n+\n+static void t_string_list_sort(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort(list);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"c\", \"a\", \"b\", \"a\", NULL);\n+\tt_string_list_sort(&list, \"a\", \"a\", \"b\", \"c\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_remove(\n+\tstruct string_list *expected_strings,\n+\tstruct string_list *list,\n+\tchar const *str)\n+{\n+\tstring_list_remove(list, str, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"a\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"a\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"b\", \"c\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"c\");\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n+\tt_string_list_remove(&expected_strings, &list, \"e\");\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_duplicates(struct string_list *list, ...)\n {\n \tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n@@ -304,3 +436,80 @@ void test_string_list__remove_duplicates(void)\n \n \tt_string_list_clear(&list, 0);\n }\n+\n+static void t_string_list_remove_empty_items(\n+\tstruct string_list *expected_strings,\n+\tstruct string_list *list)\n+{\n+\tstring_list_remove_empty_items(list, 0);\n+\tt_string_list_equal(list, expected_strings);\n+}\n+\n+void test_string_list__remove_empty_items(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"\", \"b\", \"\", NULL);\n+\tt_string_list_remove_empty_items(&expected_strings, &list);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_has_string(\n+\tstruct string_list *list,\n+\tconst char *str, int expected)\n+{\n+\tint has_string = unsorted_string_list_has_string(list, str);\n+\tcl_assert_equal_i(has_string, expected);\n+}\n+\n+void test_string_list__unsorted_string_list_has_string(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"d\", \"a\", NULL);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"a\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"b\", 1);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"c\", 0);\n+\tt_string_list_unsorted_string_list_has_string(&list, \"d\", 1);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n+static void t_string_list_unsorted_string_list_delete_item(\n+\tstruct string_list *expected_list,\n+\tstruct string_list *list,\n+\tint i)\n+{\n+\tunsorted_string_list_delete_item(list, i, 0);\n+\n+\tt_string_list_equal(list, expected_list);\n+}\n+\n+void test_string_list__unsorted_string_list_delete_item(void)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0, \"a\", \"d\", \"b\", \"c\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 1);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, NULL);\n+\tt_create_string_list_dup(&list, 0, \"\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 0);\n+\n+\tt_create_string_list_dup(&expected_strings, 0, \"a\", \"d\", \"c\", \"b\", NULL);\n+\tt_create_string_list_dup(&list, 0,  \"a\", \"d\", \"c\", \"b\", \"d\", NULL);\n+\tt_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 4);\n+\n+\tt_string_list_clear(&expected_strings, 0);\n+\tt_string_list_clear(&list, 0);\n+}\n-- \n2.51.0\n\n"},{"id":"534810","messageId":"20260129121220.69267-2-amishhhaaaa@gmail.com","threadId":"64854","inReplyTo":"20260129121220.69267-1-amishhhaaaa@gmail.com","subject":"[PATCH v3 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-29T12:12:20Z","receivedAt":"2026-01-29T12:12:35Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"Many callsites of string_list_remove_duplicates() call it\nimmdediately after calling string_list_sort(), understandably\nas the former requires string-list to be sorted, it is clear\nthat these places are sorting only to remove duplicates and\nfor no other reason.\n\nIntroduce a helper function string_list_sort_u that combines\nthese two calls that often appear together, to simplify\nthese callsites. Replace the current calls of those methods with\nstring_list_sort_u().\n\nSigned-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n---\n builtin/clone.c              |  3 +--\n builtin/fast-export.c        |  3 +--\n builtin/pack-objects.c       |  6 ++----\n builtin/sparse-checkout.c    |  6 ++----\n help.c                       |  3 +--\n notes.c                      |  3 +--\n string-list.c                |  6 ++++++\n string-list.h                |  6 ++++++\n t/unit-tests/u-string-list.c | 34 ++++++++++++++++++++++++++++++++++\n 9 files changed, 54 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex b19b302b06..f05364c268 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1136,8 +1136,7 @@ int cmd_clone(int argc,\n \t\tint val;\n \n \t\t/* remove duplicates */\n-\t\tstring_list_sort(&option_recurse_submodules);\n-\t\tstring_list_remove_duplicates(&option_recurse_submodules, 0);\n+\t\tstring_list_sort_u(&option_recurse_submodules, 0);\n \n \t\t/*\n \t\t * NEEDSWORK: In a multi-working-tree world, this needs to be\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex b90da5e616..0c5d2386d8 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1118,8 +1118,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \t\t\tfree(full_name);\n \t}\n \n-\tstring_list_sort(&extra_refs);\n-\tstring_list_remove_duplicates(&extra_refs, 0);\n+\tstring_list_sort_u(&extra_refs, 0);\n }\n \n static void handle_tags_and_duplicates(struct string_list *extras)\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ca44b7894f..649dab4ed0 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3849,10 +3849,8 @@ static void read_packs_list_from_stdin(struct rev_info *revs)\n \t\tstrbuf_reset(&buf);\n \t}\n \n-\tstring_list_sort(&include_packs);\n-\tstring_list_remove_duplicates(&include_packs, 0);\n-\tstring_list_sort(&exclude_packs);\n-\tstring_list_remove_duplicates(&exclude_packs, 0);\n+\tstring_list_sort_u(&include_packs, 0);\n+\tstring_list_sort_u(&exclude_packs, 0);\n \n \trepo_for_each_pack(the_repository, p) {\n \t\tconst char *pack_name = pack_basename(p);\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 15d51e60a8..25de7692c9 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -292,8 +292,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \t\t\tstring_list_insert(&sl, pe->pattern);\n \t}\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfprintf(fp, \"/*\\n!/*/\\n\");\n \n@@ -316,8 +315,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)\n \n \tstrbuf_release(&parent_pattern);\n \n-\tstring_list_sort(&sl);\n-\tstring_list_remove_duplicates(&sl, 0);\n+\tstring_list_sort_u(&sl, 0);\n \n \tfor (i = 0; i < sl.nr; i++) {\n \t\tchar *pattern = escaped_pattern(sl.items[i].string);\ndiff --git a/help.c b/help.c\nindex 20e114432d..2070095b6f 100644\n--- a/help.c\n+++ b/help.c\n@@ -420,8 +420,7 @@ void list_cmds_by_config(struct string_list *list)\n \tif (repo_config_get_string_tmp(the_repository, \"completion.commands\", &cmd_list))\n \t\treturn;\n \n-\tstring_list_sort(list);\n-\tstring_list_remove_duplicates(list, 0);\n+\tstring_list_sort_u(list, 0);\n \n \twhile (*cmd_list) {\n \t\tstruct strbuf sb = STRBUF_INIT;\ndiff --git a/notes.c b/notes.c\nindex 8e00fd8c47..090c48bbd5 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -921,8 +921,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,\n \tif (string_list_add_note_lines(&sort_uniq_list, new_oid))\n \t\tgoto out;\n \tstring_list_remove_empty_items(&sort_uniq_list, 0);\n-\tstring_list_sort(&sort_uniq_list);\n-\tstring_list_remove_duplicates(&sort_uniq_list, 0);\n+\tstring_list_sort_u(&sort_uniq_list, 0);\n \n \t/* create a new blob object from sort_uniq_list */\n \tif (for_each_string_list(&sort_uniq_list,\ndiff --git a/string-list.c b/string-list.c\nindex 08dc00984c..020ed8fef7 100644\n--- a/string-list.c\n+++ b/string-list.c\n@@ -247,6 +247,12 @@ void string_list_sort(struct string_list *list)\n \tQSORT_S(list->items, list->nr, cmp_items, &sort_ctx);\n }\n \n+void string_list_sort_u(struct string_list *list, int free_util)\n+{\n+\tstring_list_sort(list);\n+\tstring_list_remove_duplicates(list, free_util);\n+}\n+\n struct string_list_item *unsorted_string_list_lookup(struct string_list *list,\n \t\t\t\t\t\t     const char *string)\n {\ndiff --git a/string-list.h b/string-list.h\nindex fa6ba07853..3ad862a187 100644\n--- a/string-list.h\n+++ b/string-list.h\n@@ -239,6 +239,12 @@ struct string_list_item *string_list_append_nodup(struct string_list *list, char\n  */\n void string_list_sort(struct string_list *list);\n \n+/**\n+ * Sort the list and then remove duplicate entries.  If free_util is true,\n+ * call free() on the util members of any items that have to be deleted.\n+ */\n+void string_list_sort_u(struct string_list *list, int free_util);\n+\n /**\n  * Like `string_list_has_string()` but for unsorted lists. Linear in\n  * size of the list.\ndiff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\nindex d469a06eca..7ad84cc1cd 100644\n--- a/t/unit-tests/u-string-list.c\n+++ b/t/unit-tests/u-string-list.c\n@@ -437,6 +437,40 @@ void test_string_list__remove_duplicates(void)\n \tt_string_list_clear(&list, 0);\n }\n \n+static void t_string_list_sort_u(struct string_list *list, ...)\n+{\n+\tstruct string_list expected_strings = STRING_LIST_INIT_DUP;\n+\tva_list ap;\n+\n+\tva_start(ap, list);\n+\tt_vcreate_string_list_dup(&expected_strings, 0, ap);\n+\tva_end(ap);\n+\n+\tstring_list_sort_u(list, 0);\n+\tt_string_list_equal(list, &expected_strings);\n+\n+\tstring_list_clear(&expected_strings, 0);\n+}\n+\n+void test_string_list__sort_u(void)\n+{\n+\tstruct string_list list = STRING_LIST_INIT_DUP;\n+\n+\tt_create_string_list_dup(&list, 0, NULL);\n+\tt_string_list_sort_u(&list, NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"\", \"\", \"\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"\", NULL);\n+\tt_string_list_sort_u(&list, \"\", \"a\", \"b\", NULL);\n+\n+\tt_create_string_list_dup(&list, 0, \"b\", \"a\", \"a\", \"d\", \"c\", \"c\", NULL);\n+\tt_string_list_sort_u(&list, \"a\", \"b\", \"c\", \"d\", NULL);\n+\n+\tt_string_list_clear(&list, 0);\n+}\n+\n static void t_string_list_remove_empty_items(\n \tstruct string_list *expected_strings,\n \tstruct string_list *list)\n-- \n2.51.0\n\n"},{"id":"534811","messageId":"CAPvEtrcHVaz_aROXhzomCkmgn6OyoSHnhnrc-tAZD+V79oOsTA@mail.gmail.com","threadId":"64854","inReplyTo":"20260129121220.69267-1-amishhhaaaa@gmail.com","subject":"Re: [PATCH v3 1/2] u-string-list: add unit tests for string-list methods","fromName":"Amisha Chhajed","fromEmail":"amishhhaaaa@gmail.com","sentAt":"2026-01-29T12:14:14Z","receivedAt":"2026-01-29T12:14:26Z","isPatch":true,"sender":{"key":"amishhhaaaa@gmail.com","avatar":"https://avatars.githubusercontent.com/u/136238836?v=4"},"body":"On Thu, 29 Jan 2026 at 17:42, Amisha Chhajed <amishhhaaaa@gmail.com> wrote:\n>\n> Unit tests in u-string-list.c does not cover several methods\n> in string-list, this gap in coverage makes it difficult to\n> ensure no regressions are introduced in future changes.\n>\n> Add unit tests for the following methods to enhance coverage:\n> string_list_remove_empty_items()\n> unsorted_string_list_has_string()\n> unsorted_string_list_delete_item()\n> string_list_has_string()\n> string_list_insert()\n> string_list_sort()\n> string_list_remove()\n>\n> Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n> ---\n>  t/unit-tests/u-string-list.c | 209 +++++++++++++++++++++++++++++++++++\n>  1 file changed, 209 insertions(+)\n>\n> diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c\n> index a2457d7b1e..d469a06eca 100644\n> --- a/t/unit-tests/u-string-list.c\n> +++ b/t/unit-tests/u-string-list.c\n> @@ -243,6 +243,138 @@ void test_string_list__filter(void)\n>         t_string_list_clear(&list, 0);\n>  }\n>\n> +static void t_string_list_has_string(\n> +       struct string_list *list,\n> +       const char *string,\n> +       int expected)\n> +{\n> +       int has_string = string_list_has_string(list, string);\n> +       cl_assert_equal_i(has_string, expected);\n> +}\n> +\n> +void test_string_list__has_string(void)\n> +{\n> +       struct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +       t_create_string_list_dup(&list, 0, NULL);\n> +       t_string_list_has_string(&list, \"\", 0);\n> +\n> +       t_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", NULL);\n> +       t_string_list_has_string(&list, \"a\", 1);\n> +       t_string_list_has_string(&list, \"b\", 1);\n> +       t_string_list_has_string(&list, \"c\", 1);\n> +       t_string_list_has_string(&list, \"d\", 0);\n> +\n> +       t_string_list_clear(&list, 0);\n> +}\n> +\n> +static void t_string_list_insert(struct string_list *expected_strings, ...)\n> +{\n> +       struct string_list strings_to_insert = STRING_LIST_INIT_DUP;\n> +       struct string_list list = STRING_LIST_INIT_DUP;\n> +       va_list ap;\n> +\n> +       va_start(ap, expected_strings);\n> +       t_vcreate_string_list_dup(&strings_to_insert, 0, ap);\n> +       va_end(ap);\n> +\n> +       for (size_t i = 0; i < strings_to_insert.nr; i++)\n> +               string_list_insert(&list, strings_to_insert.items[i].string);\n> +\n> +       t_string_list_equal(&list, expected_strings);\n> +\n> +       string_list_clear(&strings_to_insert, 0);\n> +       string_list_clear(&list, 0);\n> +}\n> +\n> +void test_string_list__insert(void)\n> +{\n> +       struct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, NULL);\n> +       t_string_list_insert(&expected_strings, NULL);\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n> +       t_string_list_insert(&expected_strings, \"b\", \"a\", \"a\", \"b\", NULL);\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", NULL);\n> +       t_string_list_insert(&expected_strings, \"c\", \"b\", \"a\", \"c\", \"b\", NULL);\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"\", \"a\", NULL);\n> +       t_string_list_insert(&expected_strings, \"a\", \"a\", \"a\", \"\", NULL);\n> +\n> +       t_string_list_clear(&expected_strings, 0);\n> +}\n> +\n> +static void t_string_list_sort(struct string_list *list, ...)\n> +{\n> +       struct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +       va_list ap;\n> +\n> +       va_start(ap, list);\n> +       t_vcreate_string_list_dup(&expected_strings, 0, ap);\n> +       va_end(ap);\n> +\n> +       string_list_sort(list);\n> +       t_string_list_equal(list, &expected_strings);\n> +\n> +       string_list_clear(&expected_strings, 0);\n> +}\n> +\n> +void test_string_list__sort(void)\n> +{\n> +       struct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +       t_create_string_list_dup(&list, 0, NULL);\n> +       t_string_list_sort(&list, NULL);\n> +\n> +       t_create_string_list_dup(&list, 0, \"b\", \"\", \"a\", NULL);\n> +       t_string_list_sort(&list, \"\", \"a\", \"b\", NULL);\n> +\n> +       t_create_string_list_dup(&list, 0, \"c\", \"a\", \"b\", \"a\", NULL);\n> +       t_string_list_sort(&list, \"a\", \"a\", \"b\", \"c\", NULL);\n> +\n> +       t_string_list_clear(&list, 0);\n> +}\n> +\n> +static void t_string_list_remove(\n> +       struct string_list *expected_strings,\n> +       struct string_list *list,\n> +       char const *str)\n> +{\n> +       string_list_remove(list, str, 0);\n> +       t_string_list_equal(list, expected_strings);\n> +}\n> +\n> +void test_string_list__remove(void)\n> +{\n> +       struct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +       struct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, NULL);\n> +       t_create_string_list_dup(&list, 0, NULL);\n> +       t_string_list_remove(&expected_strings, &list, \"\");\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", NULL);\n> +       t_create_string_list_dup(&list, 0, \"a\", \"a\", NULL);\n> +       t_string_list_remove(&expected_strings, &list, \"a\");\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"b\", NULL);\n> +       t_create_string_list_dup(&list, 0, \"a\", \"b\", \"b\", \"c\", NULL);\n> +       t_string_list_remove(&expected_strings, &list, \"c\");\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"d\", NULL);\n> +       t_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n> +       t_string_list_remove(&expected_strings, &list, \"c\");\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n> +       t_create_string_list_dup(&list, 0, \"a\", \"b\", \"c\", \"d\", NULL);\n> +       t_string_list_remove(&expected_strings, &list, \"e\");\n> +\n> +       t_string_list_clear(&expected_strings, 0);\n> +       t_string_list_clear(&list, 0);\n> +}\n> +\n>  static void t_string_list_remove_duplicates(struct string_list *list, ...)\n>  {\n>         struct string_list expected_strings = STRING_LIST_INIT_DUP;\n> @@ -304,3 +436,80 @@ void test_string_list__remove_duplicates(void)\n>\n>         t_string_list_clear(&list, 0);\n>  }\n> +\n> +static void t_string_list_remove_empty_items(\n> +       struct string_list *expected_strings,\n> +       struct string_list *list)\n> +{\n> +       string_list_remove_empty_items(list, 0);\n> +       t_string_list_equal(list, expected_strings);\n> +}\n> +\n> +void test_string_list__remove_empty_items(void)\n> +{\n> +       struct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +       struct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, NULL);\n> +       t_create_string_list_dup(&list, 0, \"\", \"\", \"\", NULL);\n> +       t_string_list_remove_empty_items(&expected_strings, &list);\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"b\", NULL);\n> +       t_create_string_list_dup(&list, 0, \"a\", \"\", \"b\", \"\", NULL);\n> +       t_string_list_remove_empty_items(&expected_strings, &list);\n> +\n> +       t_string_list_clear(&expected_strings, 0);\n> +       t_string_list_clear(&list, 0);\n> +}\n> +\n> +static void t_string_list_unsorted_string_list_has_string(\n> +       struct string_list *list,\n> +       const char *str, int expected)\n> +{\n> +       int has_string = unsorted_string_list_has_string(list, str);\n> +       cl_assert_equal_i(has_string, expected);\n> +}\n> +\n> +void test_string_list__unsorted_string_list_has_string(void)\n> +{\n> +       struct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +       t_create_string_list_dup(&list, 0, \"b\", \"d\", \"a\", NULL);\n> +       t_string_list_unsorted_string_list_has_string(&list, \"a\", 1);\n> +       t_string_list_unsorted_string_list_has_string(&list, \"b\", 1);\n> +       t_string_list_unsorted_string_list_has_string(&list, \"c\", 0);\n> +       t_string_list_unsorted_string_list_has_string(&list, \"d\", 1);\n> +\n> +       t_string_list_clear(&list, 0);\n> +}\n> +\n> +static void t_string_list_unsorted_string_list_delete_item(\n> +       struct string_list *expected_list,\n> +       struct string_list *list,\n> +       int i)\n> +{\n> +       unsorted_string_list_delete_item(list, i, 0);\n> +\n> +       t_string_list_equal(list, expected_list);\n> +}\n> +\n> +void test_string_list__unsorted_string_list_delete_item(void)\n> +{\n> +       struct string_list expected_strings = STRING_LIST_INIT_DUP;\n> +       struct string_list list = STRING_LIST_INIT_DUP;\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"c\", \"b\", NULL);\n> +       t_create_string_list_dup(&list, 0, \"a\", \"d\", \"b\", \"c\", NULL);\n> +       t_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 1);\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, NULL);\n> +       t_create_string_list_dup(&list, 0, \"\", NULL);\n> +       t_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 0);\n> +\n> +       t_create_string_list_dup(&expected_strings, 0, \"a\", \"d\", \"c\", \"b\", NULL);\n> +       t_create_string_list_dup(&list, 0,  \"a\", \"d\", \"c\", \"b\", \"d\", NULL);\n> +       t_string_list_unsorted_string_list_delete_item(&expected_strings, &list, 4);\n> +\n> +       t_string_list_clear(&expected_strings, 0);\n> +       t_string_list_clear(&list, 0);\n> +}\n> --\n> 2.51.0\n>\nFixed extra EOF lines and overlong lines in v3.\n"},{"id":"534903","messageId":"e01f307c-3d7e-41b2-b62a-f61b1dd17640@app.fastmail.com","threadId":"64854","inReplyTo":"20260125201500.63904-2-amishhhaaaa@gmail.com","subject":"Re: [PATCH 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-01-30T19:51:21Z","receivedAt":"2026-01-30T19:51:53Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Sun, Jan 25, 2026, at 21:15, Amisha Chhajed wrote:\n> Many callsites of string_list_remove_duplicates() call it\n> immdediately after calling string_list_sort(), understandably\n\nnit: s/immdediately/immediately\n\n> as the former requires string-list to be sorted, it is clear\n> that these places are sorting only to remove duplicates and\n> for no other reason.\n>\n> Introduce a helper function string_list_sort_u that combines\n> these two calls that often appear together, to simplify\n> these callsites. Replace the current calls of those methods with\n> string_list_sort_u().\n>\n> Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com>\n> ---\n>[snip]\n"},{"id":"534907","messageId":"xmqq1pj67p0t.fsf@gitster.g","threadId":"64854","inReplyTo":"e01f307c-3d7e-41b2-b62a-f61b1dd17640@app.fastmail.com","subject":"Re: [PATCH 2/2] string-list: add string_list_sort_u() that mimics \"sort -u\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-30T21:45:54Z","receivedAt":"2026-01-30T21:45:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kristoffer Haugsbakk\" <kristofferhaugsbakk@fastmail.com> writes:\n\n> On Sun, Jan 25, 2026, at 21:15, Amisha Chhajed wrote:\n>> Many callsites of string_list_remove_duplicates() call it\n>> immdediately after calling string_list_sort(), understandably\n>\n> nit: s/immdediately/immediately\n\nThanks for good eyes, but I've merged this version already to\n'next' X-<.\n"}]}