string-list: add string_list_sort_u() that mimics "sort -u"

Many callsites of string_list_remove_duplicates() call it immdediately after calling string_list_sort(), understandably as the former requires string-list to be sorted, it is clear that these places are sorting only to remove duplicates and for no other reason. Introduce a helper function string_list_sort_u that combines these two calls that often appear together, to simplify these callsites. Replace the current calls of those methods with string_list_sort_u(). Signed-off-by: Amisha Chhajed <amishhhaaaa@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Amisha Chhajed committed Jan 29, 2026 at 17:42 UTC 2e711acfbdfc0fedf631688d78cde153ba835c93
9 files changed +54 -16
builtin/clone.c
+1 -2
@@ -1136,8 +1136,7 @@ int cmd_clone(int argc,
1136 int val;
1137
1138 /* remove duplicates */
1139 - string_list_sort(&option_recurse_submodules);
1140 - string_list_remove_duplicates(&option_recurse_submodules, 0);
1139 + string_list_sort_u(&option_recurse_submodules, 0);
1140
1141 /*
1142 * NEEDSWORK: In a multi-working-tree world, this needs to be
builtin/fast-export.c
+1 -2
@@ -1118,8 +1118,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
1118 free(full_name);
1119 }
1120
1121 - string_list_sort(&extra_refs);
1122 - string_list_remove_duplicates(&extra_refs, 0);
1121 + string_list_sort_u(&extra_refs, 0);
1122 }
1123
1124 static void handle_tags_and_duplicates(struct string_list *extras)
builtin/pack-objects.c
+2 -4
@@ -3855,10 +3855,8 @@ static void read_packs_list_from_stdin(struct rev_info *revs)
3855 strbuf_reset(&buf);
3856 }
3857
3858 - string_list_sort(&include_packs);
3859 - string_list_remove_duplicates(&include_packs, 0);
3860 - string_list_sort(&exclude_packs);
3861 - string_list_remove_duplicates(&exclude_packs, 0);
3858 + string_list_sort_u(&include_packs, 0);
3859 + string_list_sort_u(&exclude_packs, 0);
3860
3861 repo_for_each_pack(the_repository, p) {
3862 const char *pack_name = pack_basename(p);
builtin/sparse-checkout.c
+2 -4
@@ -292,8 +292,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
292 string_list_insert(&sl, pe->pattern);
293 }
294
295 - string_list_sort(&sl);
296 - string_list_remove_duplicates(&sl, 0);
295 + string_list_sort_u(&sl, 0);
296
297 fprintf(fp, "/*\n!/*/\n");
298
@@ -316,8 +315,7 @@ static void write_cone_to_file(FILE *fp, struct pattern_list *pl)
315
316 strbuf_release(&parent_pattern);
317
319 - string_list_sort(&sl);
320 - string_list_remove_duplicates(&sl, 0);
318 + string_list_sort_u(&sl, 0);
319
320 for (i = 0; i < sl.nr; i++) {
321 char *pattern = escaped_pattern(sl.items[i].string);
help.c
+1 -2
@@ -420,8 +420,7 @@ void list_cmds_by_config(struct string_list *list)
420 if (repo_config_get_string_tmp(the_repository, "completion.commands", &cmd_list))
421 return;
422
423 - string_list_sort(list);
424 - string_list_remove_duplicates(list, 0);
423 + string_list_sort_u(list, 0);
424
425 while (*cmd_list) {
426 struct strbuf sb = STRBUF_INIT;
notes.c
+1 -2
@@ -921,8 +921,7 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid,
921 if (string_list_add_note_lines(&sort_uniq_list, new_oid))
922 goto out;
923 string_list_remove_empty_items(&sort_uniq_list, 0);
924 - string_list_sort(&sort_uniq_list);
925 - string_list_remove_duplicates(&sort_uniq_list, 0);
924 + string_list_sort_u(&sort_uniq_list, 0);
925
926 /* create a new blob object from sort_uniq_list */
927 if (for_each_string_list(&sort_uniq_list,
string-list.c
+6
@@ -247,6 +247,12 @@ void string_list_sort(struct string_list *list)
247 QSORT_S(list->items, list->nr, cmp_items, &sort_ctx);
248 }
249
250 +void string_list_sort_u(struct string_list *list, int free_util)
251 +{
252 + string_list_sort(list);
253 + string_list_remove_duplicates(list, free_util);
254 +}
255 +
256 struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
257 const char *string)
258 {
string-list.h
+6
@@ -239,6 +239,12 @@ struct string_list_item *string_list_append_nodup(struct string_list *list, char
239 */
240 void string_list_sort(struct string_list *list);
241
242 +/**
243 + * Sort the list and then remove duplicate entries. If free_util is true,
244 + * call free() on the util members of any items that have to be deleted.
245 + */
246 +void string_list_sort_u(struct string_list *list, int free_util);
247 +
248 /**
249 * Like `string_list_has_string()` but for unsorted lists. Linear in
250 * size of the list.
t/unit-tests/u-string-list.c
+34
@@ -437,6 +437,40 @@ void test_string_list__remove_duplicates(void)
437 t_string_list_clear(&list, 0);
438 }
439
440 +static void t_string_list_sort_u(struct string_list *list, ...)
441 +{
442 + struct string_list expected_strings = STRING_LIST_INIT_DUP;
443 + va_list ap;
444 +
445 + va_start(ap, list);
446 + t_vcreate_string_list_dup(&expected_strings, 0, ap);
447 + va_end(ap);
448 +
449 + string_list_sort_u(list, 0);
450 + t_string_list_equal(list, &expected_strings);
451 +
452 + string_list_clear(&expected_strings, 0);
453 +}
454 +
455 +void test_string_list__sort_u(void)
456 +{
457 + struct string_list list = STRING_LIST_INIT_DUP;
458 +
459 + t_create_string_list_dup(&list, 0, NULL);
460 + t_string_list_sort_u(&list, NULL);
461 +
462 + t_create_string_list_dup(&list, 0, "", "", "", "", NULL);
463 + t_string_list_sort_u(&list, "", NULL);
464 +
465 + t_create_string_list_dup(&list, 0, "b", "a", "a", "", NULL);
466 + t_string_list_sort_u(&list, "", "a", "b", NULL);
467 +
468 + t_create_string_list_dup(&list, 0, "b", "a", "a", "d", "c", "c", NULL);
469 + t_string_list_sort_u(&list, "a", "b", "c", "d", NULL);
470 +
471 + t_string_list_clear(&list, 0);
472 +}
473 +
474 static void t_string_list_remove_empty_items(
475 struct string_list *expected_strings,
476 struct string_list *list)