Merge branch 'en/ort-perf-batch-12'

More fix-ups and optimization to "merge -sort".

* en/ort-perf-batch-12:
  merge-ort: miscellaneous touch-ups
  Fix various issues found in comments
  diffcore-rename: avoid unnecessary strdup'ing in break_idx
  merge-ort: replace string_list_df_name_compare with faster alternative
maint
Junio C Hamano 2021-07-16 17:42:45 -07:00
commit 89efac81c7
3 changed files with 60 additions and 26 deletions

View File

@ -54,7 +54,7 @@ static void register_rename_src(struct diff_filepair *p)
if (p->broken_pair) { if (p->broken_pair) {
if (!break_idx) { if (!break_idx) {
break_idx = xmalloc(sizeof(*break_idx)); break_idx = xmalloc(sizeof(*break_idx));
strintmap_init(break_idx, -1); strintmap_init_with_options(break_idx, -1, NULL, 0);
} }
strintmap_set(break_idx, p->one->path, rename_dst_nr); strintmap_set(break_idx, p->one->path, rename_dst_nr);
} }
@ -1543,7 +1543,7 @@ void diffcore_rename_extended(struct diff_options *options,
/* all the usual ones need to be kept */ /* all the usual ones need to be kept */
diff_q(&outq, p); diff_q(&outq, p);
else else
/* no need to keep unmodified pairs; FIXME: remove earlier? */ /* no need to keep unmodified pairs */
pair_to_free = p; pair_to_free = p;


if (pair_to_free) if (pair_to_free)

View File

@ -765,6 +765,7 @@ static void add_pair(struct merge_options *opt,
int names_idx = is_add ? side : 0; int names_idx = is_add ? side : 0;


if (is_add) { if (is_add) {
assert(match_mask == 0 || match_mask == 6);
if (strset_contains(&renames->cached_target_names[side], if (strset_contains(&renames->cached_target_names[side],
pathname)) pathname))
return; return;
@ -772,6 +773,8 @@ static void add_pair(struct merge_options *opt,
unsigned content_relevant = (match_mask == 0); unsigned content_relevant = (match_mask == 0);
unsigned location_relevant = (dir_rename_mask == 0x07); unsigned location_relevant = (dir_rename_mask == 0x07);


assert(match_mask == 0 || match_mask == 3 || match_mask == 5);

/* /*
* If pathname is found in cached_irrelevant[side] due to * If pathname is found in cached_irrelevant[side] due to
* previous pick but for this commit content is relevant, * previous pick but for this commit content is relevant,
@ -2533,7 +2536,7 @@ static int compare_pairs(const void *a_, const void *b_)
return strcmp(a->one->path, b->one->path); return strcmp(a->one->path, b->one->path);
} }


/* Call diffcore_rename() to compute which files have changed on given side */ /* Call diffcore_rename() to update deleted/added pairs into rename pairs */
static void detect_regular_renames(struct merge_options *opt, static void detect_regular_renames(struct merge_options *opt,
unsigned side_index) unsigned side_index)
{ {
@ -2586,8 +2589,10 @@ static void detect_regular_renames(struct merge_options *opt,
} }


/* /*
* Get information of all renames which occurred in 'side_pairs', discarding * Get information of all renames which occurred in 'side_pairs', making use
* non-renames. * of any implicit directory renames in side_dir_renames (also making use of
* implicit directory renames rename_exclusions as needed by
* check_for_directory_rename()). Add all (updated) renames into result.
*/ */
static int collect_renames(struct merge_options *opt, static int collect_renames(struct merge_options *opt,
struct diff_queue_struct *result, struct diff_queue_struct *result,
@ -2746,31 +2751,58 @@ simple_cleanup:


/*** Function Grouping: functions related to process_entries() ***/ /*** Function Grouping: functions related to process_entries() ***/


static int string_list_df_name_compare(const char *one, const char *two) static int sort_dirs_next_to_their_children(const char *one, const char *two)
{ {
int onelen = strlen(one); unsigned char c1, c2;
int twolen = strlen(two);
/* /*
* Here we only care that entries for D/F conflicts are * Here we only care that entries for directories appear adjacent
* adjacent, in particular with the file of the D/F conflict * to and before files underneath the directory. We can achieve
* appearing before files below the corresponding directory. * that by pretending to add a trailing slash to every file and
* The order of the rest of the list is irrelevant for us. * then sorting. In other words, we do not want the natural
* sorting of
* foo
* foo.txt
* foo/bar
* Instead, we want "foo" to sort as though it were "foo/", so that
* we instead get
* foo.txt
* foo
* foo/bar
* To achieve this, we basically implement our own strcmp, except that
* if we get to the end of either string instead of comparing NUL to
* another character, we compare '/' to it.
* *
* To achieve this, we sort with df_name_compare and provide * If this unusual "sort as though '/' were appended" perplexes
* the mode S_IFDIR so that D/F conflicts will sort correctly. * you, perhaps it will help to note that this is not the final
* We use the mode S_IFDIR for everything else for simplicity, * sort. write_tree() will sort again without the trailing slash
* since in other cases any changes in their order due to * magic, but just on paths immediately under a given tree.
* sorting cause no problems for us. *
* The reason to not use df_name_compare directly was that it was
* just too expensive (we don't have the string lengths handy), so
* it was reimplemented.
*/ */
int cmp = df_name_compare(one, onelen, S_IFDIR,
two, twolen, S_IFDIR);
/* /*
* Now that 'foo' and 'foo/bar' compare equal, we have to make sure * NOTE: This function will never be called with two equal strings,
* that 'foo' comes before 'foo/bar'. * because it is used to sort the keys of a strmap, and strmaps have
* unique keys by construction. That simplifies our c1==c2 handling
* below.
*/ */
if (cmp)
return cmp; while (*one && (*one == *two)) {
return onelen - twolen; one++;
two++;
}

c1 = *one ? *one : '/';
c2 = *two ? *two : '/';

if (c1 == c2) {
/* Getting here means one is a leading directory of the other */
return (*one) ? 1 : -1;
} else
return c1 - c2;
} }


static int read_oid_strbuf(struct merge_options *opt, static int read_oid_strbuf(struct merge_options *opt,
@ -3457,6 +3489,8 @@ static void process_entry(struct merge_options *opt,
*/ */
if (!ci->merged.clean) if (!ci->merged.clean)
strmap_put(&opt->priv->conflicted, path, ci); strmap_put(&opt->priv->conflicted, path, ci);

/* Record metadata for ci->merged in dir_metadata */
record_entry_for_tree(dir_metadata, path, &ci->merged); record_entry_for_tree(dir_metadata, path, &ci->merged);
} }


@ -3490,7 +3524,7 @@ static void process_entries(struct merge_options *opt,
trace2_region_leave("merge", "plist copy", opt->repo); trace2_region_leave("merge", "plist copy", opt->repo);


trace2_region_enter("merge", "plist special sort", opt->repo); trace2_region_enter("merge", "plist special sort", opt->repo);
plist.cmp = string_list_df_name_compare; plist.cmp = sort_dirs_next_to_their_children;
string_list_sort(&plist); string_list_sort(&plist);
trace2_region_leave("merge", "plist special sort", opt->repo); trace2_region_leave("merge", "plist special sort", opt->repo);



View File

@ -454,7 +454,7 @@ test_expect_success '1f: Split a directory into two other directories' '
# the directory renamed, but the files within it. (see 1b) # the directory renamed, but the files within it. (see 1b)
# #
# If renames split a directory into two or more others, the directory # If renames split a directory into two or more others, the directory
# with the most renames, "wins" (see 1c). However, see the testcases # with the most renames, "wins" (see 1f). However, see the testcases
# in section 2, plus testcases 3a and 4a. # in section 2, plus testcases 3a and 4a.
########################################################################### ###########################################################################