108
*/
109
struct strset relevant_sources[3];
110
111
+ /*
112
+ * dir_rename_mask:
113
+ * 0: optimization removing unmodified potential rename source okay
114
+ * 2 or 4: optimization okay, but must check for files added to dir
115
+ * 7: optimization forbidden; need rename source in case of dir rename
116
+ */
117
+ unsigned dir_rename_mask:3;
118
+
119
/*
120
* callback_data_*: supporting data structures for alternate traversal
121
*
427
strmap_clear(&opti->output, 0);
428
}
429
430
+ renames->dir_rename_mask = 0;
431
+
432
/* Clean out callback_data as well. */
433
FREE_AND_NULL(renames->callback_data);
434
renames->callback_data_nr = renames->callback_data_alloc = 0;
530
{
531
struct merge_options *opt = info->data;
532
struct rename_info *renames = &opt->priv->renames;
533
+ unsigned filemask = mask & ~dirmask;
534
535
assert(n==3);
536
537
if (!renames->callback_data_traverse_path)
538
renames->callback_data_traverse_path = xstrdup(info->traverse_path);
539
540
+ if (filemask && filemask == renames->dir_rename_mask)
541
+ renames->dir_rename_mask = 0x07;
542
+
543
ALLOC_GROW(renames->callback_data, renames->callback_data_nr + 1,
544
renames->callback_data_alloc);
545
renames->callback_data[renames->callback_data_nr].mask = mask;
558
* additional details before the "real" traversal
559
* - loop through the saved entries and call the original callback on them
560
*/
547
-MAYBE_UNUSED
561
static int traverse_trees_wrapper(struct index_state *istate,
562
int n,
563
struct tree_desc *t,
569
struct merge_options *opt = info->data;
570
struct rename_info *renames = &opt->priv->renames;
571
572
+ assert(renames->dir_rename_mask == 2 || renames->dir_rename_mask == 4);
573
+
574
old_callback_data_traverse_path = renames->callback_data_traverse_path;
575
old_fn = info->fn;
576
old_offset = renames->callback_data_nr;
662
const char *pathname,
663
unsigned side,
664
unsigned is_add /* if false, is_delete */,
650
- unsigned match_mask)
665
+ unsigned match_mask,
666
+ unsigned dir_rename_mask)
667
{
668
struct diff_filespec *one, *two;
669
struct rename_info *renames = &opt->priv->renames;
671
672
if (!is_add) {
673
unsigned content_relevant = (match_mask == 0);
658
- unsigned location_relevant = 1; /* FIXME: compute this */
674
+ unsigned location_relevant = (dir_rename_mask == 0x07);
675
676
if (content_relevant || location_relevant)
677
strset_add(&renames->relevant_sources[side], pathname);
695
struct rename_info *renames = &opt->priv->renames;
696
unsigned side;
697
698
+ /*
699
+ * Update dir_rename_mask (determines ignore-rename-source validity)
700
+ *
701
+ * dir_rename_mask helps us keep track of when directory rename
702
+ * detection may be relevant. Basically, whenver a directory is
703
+ * removed on one side of history, and a file is added to that
704
+ * directory on the other side of history, directory rename
705
+ * detection is relevant (meaning we have to detect renames for all
706
+ * files within that directory to deduce where the directory
707
+ * moved). Also, whenever a directory needs directory rename
708
+ * detection, due to the "majority rules" choice for where to move
709
+ * it (see t6423 testcase 1f), we also need to detect renames for
710
+ * all files within subdirectories of that directory as well.
711
+ *
712
+ * Here we haven't looked at files within the directory yet, we are
713
+ * just looking at the directory itself. So, if we aren't yet in
714
+ * a case where a parent directory needed directory rename detection
715
+ * (i.e. dir_rename_mask != 0x07), and if the directory was removed
716
+ * on one side of history, record the mask of the other side of
717
+ * history in dir_rename_mask.
718
+ */
719
+ if (renames->dir_rename_mask != 0x07 &&
720
+ (dirmask == 3 || dirmask == 5)) {
721
+ /* simple sanity check */
722
+ assert(renames->dir_rename_mask == 0 ||
723
+ renames->dir_rename_mask == (dirmask & ~1));
724
+ /* update dir_rename_mask; have it record mask of new side */
725
+ renames->dir_rename_mask = (dirmask & ~1);
726
+ }
727
+
728
/* Update dirs_removed, as needed */
729
if (dirmask == 1 || dirmask == 3 || dirmask == 5) {
730
/* absent_mask = 0x07 - dirmask; sides = absent_mask/2 */
744
/* Check for deletion on side */
745
if ((filemask & 1) && !(filemask & side_mask))
746
add_pair(opt, names, fullname, side, 0 /* delete */,
701
- match_mask & filemask);
747
+ match_mask & filemask,
748
+ renames->dir_rename_mask);
749
750
/* Check for addition on side */
751
if (!(filemask & 1) && (filemask & side_mask))
752
add_pair(opt, names, fullname, side, 1 /* add */,
706
- match_mask & filemask);
753
+ match_mask & filemask,
754
+ renames->dir_rename_mask);
755
}
756
}
757
769
*/
770
struct merge_options *opt = info->data;
771
struct merge_options_internal *opti = opt->priv;
772
+ struct rename_info *renames = &opt->priv->renames;
773
struct string_list_item pi; /* Path Info */
774
struct conflict_info *ci; /* typed alias to pi.util (which is void*) */
775
struct name_entry *p;
776
size_t len;
777
char *fullpath;
778
const char *dirname = opti->current_dir_name;
779
+ unsigned prev_dir_rename_mask = renames->dir_rename_mask;
780
unsigned filemask = mask & ~dirmask;
781
unsigned match_mask = 0; /* will be updated below */
782
unsigned mbase_null = !(mask & 1);
917
918
original_dir_name = opti->current_dir_name;
919
opti->current_dir_name = pi.string;
870
- ret = traverse_trees(NULL, 3, t, &newinfo);
920
+ if (renames->dir_rename_mask == 0 ||
921
+ renames->dir_rename_mask == 0x07)
922
+ ret = traverse_trees(NULL, 3, t, &newinfo);
923
+ else
924
+ ret = traverse_trees_wrapper(NULL, 3, t, &newinfo);
925
opti->current_dir_name = original_dir_name;
926
+ renames->dir_rename_mask = prev_dir_rename_mask;
927
928
for (i = MERGE_BASE; i <= MERGE_SIDE2; i++)
929
free(buf[i]);