merge-ort: have process_entries operate in a defined order
We want to handle paths below a directory before needing to handle the directory itself. Also, we want to handle the directory immediately after the paths below it, so we can't use simple lexicographic ordering from strcmp (which would insert foo.txt between foo and foo/file.c). Copy string_list_df_name_compare() from merge-recursive.c, and set up a string list of paths sorted by that function so that we can iterate in the desired order. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Elijah Newren committed
Dec 13, 2020 at 08:04 UTC
8adffaa818d17595b23959e995ad395c8cd0b0be
1 file changed
+50
-3
merge-ort.c
+50
-3
@@ -492,6 +492,33 @@ static int detect_and_process_renames(struct merge_options *opt,
492
return clean;
493
}
494
495
+static int string_list_df_name_compare(const char *one, const char *two)
496
+{
497
+ int onelen = strlen(one);
498
+ int twolen = strlen(two);
499
+ /*
500
+ * Here we only care that entries for D/F conflicts are
501
+ * adjacent, in particular with the file of the D/F conflict
502
+ * appearing before files below the corresponding directory.
503
+ * The order of the rest of the list is irrelevant for us.
504
+ *
505
+ * To achieve this, we sort with df_name_compare and provide
506
+ * the mode S_IFDIR so that D/F conflicts will sort correctly.
507
+ * We use the mode S_IFDIR for everything else for simplicity,
508
+ * since in other cases any changes in their order due to
509
+ * sorting cause no problems for us.
510
+ */
511
+ int cmp = df_name_compare(one, onelen, S_IFDIR,
512
+ two, twolen, S_IFDIR);
513
+ /*
514
+ * Now that 'foo' and 'foo/bar' compare equal, we have to make sure
515
+ * that 'foo' comes before 'foo/bar'.
516
+ */
517
+ if (cmp)
518
+ return cmp;
519
+ return onelen - twolen;
520
+}
521
+
522
/* Per entry merge function */
523
static void process_entry(struct merge_options *opt,
524
const char *path,
@@ -578,24 +605,44 @@ static void process_entries(struct merge_options *opt,
605
{
606
struct hashmap_iter iter;
607
struct strmap_entry *e;
608
+ struct string_list plist = STRING_LIST_INIT_NODUP;
609
+ struct string_list_item *entry;
610
611
if (strmap_empty(&opt->priv->paths)) {
612
oidcpy(result_oid, opt->repo->hash_algo->empty_tree);
613
return;
614
}
615
616
+ /* Hack to pre-allocate plist to the desired size */
617
+ ALLOC_GROW(plist.items, strmap_get_size(&opt->priv->paths), plist.alloc);
618
+
619
+ /* Put every entry from paths into plist, then sort */
620
strmap_for_each_entry(&opt->priv->paths, &iter, e) {
621
+ string_list_append(&plist, e->key)->util = e->value;
622
+ }
623
+ plist.cmp = string_list_df_name_compare;
624
+ string_list_sort(&plist);
625
+
626
+ /*
627
+ * Iterate over the items in reverse order, so we can handle paths
628
+ * below a directory before needing to handle the directory itself.
629
+ */
630
+ for (entry = &plist.items[plist.nr-1]; entry >= plist.items; --entry) {
631
+ char *path = entry->string;
632
/*
633
* NOTE: mi may actually be a pointer to a conflict_info, but
634
* we have to check mi->clean first to see if it's safe to
635
* reassign to such a pointer type.
636
*/
593
- struct merged_info *mi = e->value;
637
+ struct merged_info *mi = entry->util;
638
595
- if (!mi->clean)
596
- process_entry(opt, e->key, e->value);
639
+ if (!mi->clean) {
640
+ struct conflict_info *ci = (struct conflict_info *)mi;
641
+ process_entry(opt, path, ci);
642
+ }
643
}
644
645
+ string_list_clear(&plist, 0);
646
die("Tree creation not yet implemented");
647
}
648