merge-ort: copy get_renamed_dir_portion() from merge-recursive.c

Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Jan 19, 2021 at 19:53 UTC 9fe37e7bb9e251a3419d93f0090c17df05fafb4e
1 file changed +104
merge-ort.c
+104
@@ -721,6 +721,110 @@ static int handle_content_merge(struct merge_options *opt,
721
722 /*** Function Grouping: functions related to directory rename detection ***/
723
724 +MAYBE_UNUSED
725 +static void get_renamed_dir_portion(const char *old_path, const char *new_path,
726 + char **old_dir, char **new_dir)
727 +{
728 + char *end_of_old, *end_of_new;
729 +
730 + /* Default return values: NULL, meaning no rename */
731 + *old_dir = NULL;
732 + *new_dir = NULL;
733 +
734 + /*
735 + * For
736 + * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
737 + * the "e/foo.c" part is the same, we just want to know that
738 + * "a/b/c/d" was renamed to "a/b/some/thing/else"
739 + * so, for this example, this function returns "a/b/c/d" in
740 + * *old_dir and "a/b/some/thing/else" in *new_dir.
741 + */
742 +
743 + /*
744 + * If the basename of the file changed, we don't care. We want
745 + * to know which portion of the directory, if any, changed.
746 + */
747 + end_of_old = strrchr(old_path, '/');
748 + end_of_new = strrchr(new_path, '/');
749 +
750 + /*
751 + * If end_of_old is NULL, old_path wasn't in a directory, so there
752 + * could not be a directory rename (our rule elsewhere that a
753 + * directory which still exists is not considered to have been
754 + * renamed means the root directory can never be renamed -- because
755 + * the root directory always exists).
756 + */
757 + if (end_of_old == NULL)
758 + return; /* Note: *old_dir and *new_dir are still NULL */
759 +
760 + /*
761 + * If new_path contains no directory (end_of_new is NULL), then we
762 + * have a rename of old_path's directory to the root directory.
763 + */
764 + if (end_of_new == NULL) {
765 + *old_dir = xstrndup(old_path, end_of_old - old_path);
766 + *new_dir = xstrdup("");
767 + return;
768 + }
769 +
770 + /* Find the first non-matching character traversing backwards */
771 + while (*--end_of_new == *--end_of_old &&
772 + end_of_old != old_path &&
773 + end_of_new != new_path)
774 + ; /* Do nothing; all in the while loop */
775 +
776 + /*
777 + * If both got back to the beginning of their strings, then the
778 + * directory didn't change at all, only the basename did.
779 + */
780 + if (end_of_old == old_path && end_of_new == new_path &&
781 + *end_of_old == *end_of_new)
782 + return; /* Note: *old_dir and *new_dir are still NULL */
783 +
784 + /*
785 + * If end_of_new got back to the beginning of its string, and
786 + * end_of_old got back to the beginning of some subdirectory, then
787 + * we have a rename/merge of a subdirectory into the root, which
788 + * needs slightly special handling.
789 + *
790 + * Note: There is no need to consider the opposite case, with a
791 + * rename/merge of the root directory into some subdirectory
792 + * because as noted above the root directory always exists so it
793 + * cannot be considered to be renamed.
794 + */
795 + if (end_of_new == new_path &&
796 + end_of_old != old_path && end_of_old[-1] == '/') {
797 + *old_dir = xstrndup(old_path, --end_of_old - old_path);
798 + *new_dir = xstrdup("");
799 + return;
800 + }
801 +
802 + /*
803 + * We've found the first non-matching character in the directory
804 + * paths. That means the current characters we were looking at
805 + * were part of the first non-matching subdir name going back from
806 + * the end of the strings. Get the whole name by advancing both
807 + * end_of_old and end_of_new to the NEXT '/' character. That will
808 + * represent the entire directory rename.
809 + *
810 + * The reason for the increment is cases like
811 + * a/b/star/foo/whatever.c -> a/b/tar/foo/random.c
812 + * After dropping the basename and going back to the first
813 + * non-matching character, we're now comparing:
814 + * a/b/s and a/b/
815 + * and we want to be comparing:
816 + * a/b/star/ and a/b/tar/
817 + * but without the pre-increment, the one on the right would stay
818 + * a/b/.
819 + */
820 + end_of_old = strchr(++end_of_old, '/');
821 + end_of_new = strchr(++end_of_new, '/');
822 +
823 + /* Copy the old and new directories into *old_dir and *new_dir. */
824 + *old_dir = xstrndup(old_path, end_of_old - old_path);
825 + *new_dir = xstrndup(new_path, end_of_new - new_path);
826 +}
827 +
828 static void compute_rename_counts(struct diff_queue_struct *pairs,
829 struct strmap *dir_rename_count,
830 struct strset *dirs_removed)