@@ -1951,6 +1951,16 @@ static char *apply_dir_rename(struct dir_rename_entry *entry,
1951
1951
return NULL ;
1952
1952
1953
1953
oldlen = strlen (entry -> dir );
1954
+ if (entry -> new_dir .len == 0 )
1955
+ /*
1956
+ * If someone renamed/merged a subdirectory into the root
1957
+ * directory (e.g. 'some/subdir' -> ''), then we want to
1958
+ * avoid returning
1959
+ * '' + '/filename'
1960
+ * as the rename; we need to make old_path + oldlen advance
1961
+ * past the '/' character.
1962
+ */
1963
+ oldlen ++ ;
1954
1964
newlen = entry -> new_dir .len + (strlen (old_path ) - oldlen ) + 1 ;
1955
1965
strbuf_grow (& new_path , newlen );
1956
1966
strbuf_addbuf (& new_path , & entry -> new_dir );
@@ -1963,8 +1973,8 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
1963
1973
char * * old_dir , char * * new_dir )
1964
1974
{
1965
1975
char * end_of_old , * end_of_new ;
1966
- int old_len , new_len ;
1967
1976
1977
+ /* Default return values: NULL, meaning no rename */
1968
1978
* old_dir = NULL ;
1969
1979
* new_dir = NULL ;
1970
1980
@@ -1975,43 +1985,91 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
1975
1985
* "a/b/c/d" was renamed to "a/b/some/thing/else"
1976
1986
* so, for this example, this function returns "a/b/c/d" in
1977
1987
* *old_dir and "a/b/some/thing/else" in *new_dir.
1978
- *
1979
- * Also, if the basename of the file changed, we don't care. We
1980
- * want to know which portion of the directory, if any, changed.
1988
+ */
1989
+
1990
+ /*
1991
+ * If the basename of the file changed, we don't care. We want
1992
+ * to know which portion of the directory, if any, changed.
1981
1993
*/
1982
1994
end_of_old = strrchr (old_path , '/' );
1983
1995
end_of_new = strrchr (new_path , '/' );
1984
1996
1985
- if (end_of_old == NULL || end_of_new == NULL )
1997
+ /*
1998
+ * If end_of_old is NULL, old_path wasn't in a directory, so there
1999
+ * could not be a directory rename (our rule elsewhere that a
2000
+ * directory which still exists is not considered to have been
2001
+ * renamed means the root directory can never be renamed -- because
2002
+ * the root directory always exists).
2003
+ */
2004
+ if (end_of_old == NULL )
2005
+ return ; /* Note: *old_dir and *new_dir are still NULL */
2006
+
2007
+ /*
2008
+ * If new_path contains no directory (end_of_new is NULL), then we
2009
+ * have a rename of old_path's directory to the root directory.
2010
+ */
2011
+ if (end_of_new == NULL ) {
2012
+ * old_dir = xstrndup (old_path , end_of_old - old_path );
2013
+ * new_dir = xstrdup ("" );
1986
2014
return ;
2015
+ }
2016
+
2017
+ /* Find the first non-matching character traversing backwards */
1987
2018
while (* -- end_of_new == * -- end_of_old &&
1988
2019
end_of_old != old_path &&
1989
2020
end_of_new != new_path )
1990
2021
; /* Do nothing; all in the while loop */
2022
+
1991
2023
/*
1992
- * We've found the first non-matching character in the directory
1993
- * paths. That means the current directory we were comparing
1994
- * represents the rename. Move end_of_old and end_of_new back
1995
- * to the full directory name.
2024
+ * If both got back to the beginning of their strings, then the
2025
+ * directory didn't change at all, only the basename did.
1996
2026
*/
1997
- if (* end_of_old == '/' )
1998
- end_of_old ++ ;
1999
- if (* end_of_old != '/' )
2000
- end_of_new ++ ;
2001
- end_of_old = strchr (end_of_old , '/' );
2002
- end_of_new = strchr (end_of_new , '/' );
2027
+ if (end_of_old == old_path && end_of_new == new_path &&
2028
+ * end_of_old == * end_of_new )
2029
+ return ; /* Note: *old_dir and *new_dir are still NULL */
2003
2030
2004
2031
/*
2005
- * It may have been the case that old_path and new_path were the same
2006
- * directory all along. Don't claim a rename if they're the same.
2032
+ * If end_of_new got back to the beginning of its string, and
2033
+ * end_of_old got back to the beginning of some subdirectory, then
2034
+ * we have a rename/merge of a subdirectory into the root, which
2035
+ * needs slightly special handling.
2036
+ *
2037
+ * Note: There is no need to consider the opposite case, with a
2038
+ * rename/merge of the root directory into some subdirectory
2039
+ * because as noted above the root directory always exists so it
2040
+ * cannot be considered to be renamed.
2007
2041
*/
2008
- old_len = end_of_old - old_path ;
2009
- new_len = end_of_new - new_path ;
2010
-
2011
- if (old_len != new_len || strncmp (old_path , new_path , old_len )) {
2012
- * old_dir = xstrndup (old_path , old_len );
2013
- * new_dir = xstrndup (new_path , new_len );
2042
+ if (end_of_new == new_path &&
2043
+ end_of_old != old_path && end_of_old [-1 ] == '/' ) {
2044
+ * old_dir = xstrndup (old_path , -- end_of_old - old_path );
2045
+ * new_dir = xstrdup ("" );
2046
+ return ;
2014
2047
}
2048
+
2049
+ /*
2050
+ * We've found the first non-matching character in the directory
2051
+ * paths. That means the current characters we were looking at
2052
+ * were part of the first non-matching subdir name going back from
2053
+ * the end of the strings. Get the whole name by advancing both
2054
+ * end_of_old and end_of_new to the NEXT '/' character. That will
2055
+ * represent the entire directory rename.
2056
+ *
2057
+ * The reason for the increment is cases like
2058
+ * a/b/star/foo/whatever.c -> a/b/tar/foo/random.c
2059
+ * After dropping the basename and going back to the first
2060
+ * non-matching character, we're now comparing:
2061
+ * a/b/s and a/b/
2062
+ * and we want to be comparing:
2063
+ * a/b/star/ and a/b/tar/
2064
+ * but without the pre-increment, the one on the right would stay
2065
+ * a/b/.
2066
+ */
2067
+ end_of_old = strchr (++ end_of_old , '/' );
2068
+ end_of_new = strchr (++ end_of_new , '/' );
2069
+
2070
+ /* Copy the old and new directories into *old_dir and *new_dir. */
2071
+ * old_dir = xstrndup (old_path , end_of_old - old_path );
2072
+ * new_dir = xstrndup (new_path , end_of_new - new_path );
2015
2073
}
2016
2074
2017
2075
static void remove_hashmap_entries (struct hashmap * dir_renames ,
0 commit comments