@matheusbsbs / matheus-bagliano / commits / d3eebaad5e
Fork 0

merge-recursive: clean up get_renamed_dir_portion()

Dscho noted a few things making this function hard to follow. Restructure it a bit and add comments to make it easier to follow. The restructurings include: * There was a special case if-check at the end of the function checking whether someone just renamed a file within its original directory, meaning that there could be no directory rename involved. That check was slightly convoluted; it could be done in a more straightforward fashion earlier in the function, and can be done more cheaply too (no call to strncmp). * The conditions for advancing end_of_old and end_of_new before calling strchr were both confusing and unnecessary. If either points at a '/', then they need to be advanced in order to find the next '/'. If either doesn't point at a '/', then advancing them one char before calling strchr() doesn't hurt. So, just rip out the if conditions and advance both before calling strchr(). Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Oct 22, 2019 at 21:22 UTC d3eebaad5ef02284c86d6c4b80199eac58c6729b
1 file changed +36 -24
merge-recursive.c
+36 -24
@@ -1943,8 +1943,8 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
1943 char **old_dir, char **new_dir)
1944 {
1945 char *end_of_old, *end_of_new;
1946 - int old_len, new_len;
1946
1947 + /* Default return values: NULL, meaning no rename */
1948 *old_dir = NULL;
1949 *new_dir = NULL;
1950
@@ -1955,43 +1955,55 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
1955 * "a/b/c/d" was renamed to "a/b/some/thing/else"
1956 * so, for this example, this function returns "a/b/c/d" in
1957 * *old_dir and "a/b/some/thing/else" in *new_dir.
1958 - *
1959 - * Also, if the basename of the file changed, we don't care. We
1960 - * want to know which portion of the directory, if any, changed.
1958 + */
1959 +
1960 + /*
1961 + * If the basename of the file changed, we don't care. We want
1962 + * to know which portion of the directory, if any, changed.
1963 */
1964 end_of_old = strrchr(old_path, '/');
1965 end_of_new = strrchr(new_path, '/');
1964 -
1966 if (end_of_old == NULL || end_of_new == NULL)
1966 - return;
1967 + return; /* We haven't modified *old_dir or *new_dir yet. */
1968 +
1969 + /* Find the first non-matching character traversing backwards */
1970 while (*--end_of_new == *--end_of_old &&
1971 end_of_old != old_path &&
1972 end_of_new != new_path)
1973 ; /* Do nothing; all in the while loop */
1974 +
1975 /*
1972 - * We've found the first non-matching character in the directory
1973 - * paths. That means the current directory we were comparing
1974 - * represents the rename. Move end_of_old and end_of_new back
1975 - * to the full directory name.
1976 + * If both got back to the beginning of their strings, then the
1977 + * directory didn't change at all, only the basename did.
1978 */
1977 - if (*end_of_old == '/')
1978 - end_of_old++;
1979 - if (*end_of_old != '/')
1980 - end_of_new++;
1981 - end_of_old = strchr(end_of_old, '/');
1982 - end_of_new = strchr(end_of_new, '/');
1979 + if (end_of_old == old_path && end_of_new == new_path &&
1980 + *end_of_old == *end_of_new)
1981 + return; /* We haven't modified *old_dir or *new_dir yet. */
1982
1983 /*
1985 - * It may have been the case that old_path and new_path were the same
1986 - * directory all along. Don't claim a rename if they're the same.
1984 + * We've found the first non-matching character in the directory
1985 + * paths. That means the current characters we were looking at
1986 + * were part of the first non-matching subdir name going back from
1987 + * the end of the strings. Get the whole name by advancing both
1988 + * end_of_old and end_of_new to the NEXT '/' character. That will
1989 + * represent the entire directory rename.
1990 + *
1991 + * The reason for the increment is cases like
1992 + * a/b/star/foo/whatever.c -> a/b/tar/foo/random.c
1993 + * After dropping the basename and going back to the first
1994 + * non-matching character, we're now comparing:
1995 + * a/b/s and a/b/
1996 + * and we want to be comparing:
1997 + * a/b/star/ and a/b/tar/
1998 + * but without the pre-increment, the one on the right would stay
1999 + * a/b/.
2000 */
1988 - old_len = end_of_old - old_path;
1989 - new_len = end_of_new - new_path;
2001 + end_of_old = strchr(++end_of_old, '/');
2002 + end_of_new = strchr(++end_of_new, '/');
2003
1991 - if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
1992 - *old_dir = xstrndup(old_path, old_len);
1993 - *new_dir = xstrndup(new_path, new_len);
1994 - }
2004 + /* Copy the old and new directories into *old_dir and *new_dir. */
2005 + *old_dir = xstrndup(old_path, end_of_old - old_path);
2006 + *new_dir = xstrndup(new_path, end_of_new - new_path);
2007 }
2008
2009 static void remove_hashmap_entries(struct hashmap *dir_renames,