Merge branch 'en/merge-recursive-directory-rename-fixes'
When all files from some subdirectory were renamed to the root directory, the directory rename heuristics would fail to detect that as a rename/merge of the subdirectory to the root directory, which has been corrected. * en/merge-recursive-directory-rename-fixes: t604[236]: do not run setup in separate tests merge-recursive: fix merging a subdirectory into the root directory merge-recursive: clean up get_renamed_dir_portion()
This commit is contained in:
@ -1951,6 +1951,16 @@ static char *apply_dir_rename(struct dir_rename_entry *entry,
|
||||
return NULL;
|
||||
|
||||
oldlen = strlen(entry->dir);
|
||||
if (entry->new_dir.len == 0)
|
||||
/*
|
||||
* If someone renamed/merged a subdirectory into the root
|
||||
* directory (e.g. 'some/subdir' -> ''), then we want to
|
||||
* avoid returning
|
||||
* '' + '/filename'
|
||||
* as the rename; we need to make old_path + oldlen advance
|
||||
* past the '/' character.
|
||||
*/
|
||||
oldlen++;
|
||||
newlen = entry->new_dir.len + (strlen(old_path) - oldlen) + 1;
|
||||
strbuf_grow(&new_path, newlen);
|
||||
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,
|
||||
char **old_dir, char **new_dir)
|
||||
{
|
||||
char *end_of_old, *end_of_new;
|
||||
int old_len, new_len;
|
||||
|
||||
/* Default return values: NULL, meaning no rename */
|
||||
*old_dir = NULL;
|
||||
*new_dir = NULL;
|
||||
|
||||
@ -1975,43 +1985,91 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path,
|
||||
* "a/b/c/d" was renamed to "a/b/some/thing/else"
|
||||
* so, for this example, this function returns "a/b/c/d" in
|
||||
* *old_dir and "a/b/some/thing/else" in *new_dir.
|
||||
*
|
||||
* Also, if the basename of the file changed, we don't care. We
|
||||
* want to know which portion of the directory, if any, changed.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If the basename of the file changed, we don't care. We want
|
||||
* to know which portion of the directory, if any, changed.
|
||||
*/
|
||||
end_of_old = strrchr(old_path, '/');
|
||||
end_of_new = strrchr(new_path, '/');
|
||||
|
||||
if (end_of_old == NULL || end_of_new == NULL)
|
||||
/*
|
||||
* If end_of_old is NULL, old_path wasn't in a directory, so there
|
||||
* could not be a directory rename (our rule elsewhere that a
|
||||
* directory which still exists is not considered to have been
|
||||
* renamed means the root directory can never be renamed -- because
|
||||
* the root directory always exists).
|
||||
*/
|
||||
if (end_of_old == NULL)
|
||||
return; /* Note: *old_dir and *new_dir are still NULL */
|
||||
|
||||
/*
|
||||
* If new_path contains no directory (end_of_new is NULL), then we
|
||||
* have a rename of old_path's directory to the root directory.
|
||||
*/
|
||||
if (end_of_new == NULL) {
|
||||
*old_dir = xstrndup(old_path, end_of_old - old_path);
|
||||
*new_dir = xstrdup("");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the first non-matching character traversing backwards */
|
||||
while (*--end_of_new == *--end_of_old &&
|
||||
end_of_old != old_path &&
|
||||
end_of_new != new_path)
|
||||
; /* Do nothing; all in the while loop */
|
||||
|
||||
/*
|
||||
* If both got back to the beginning of their strings, then the
|
||||
* directory didn't change at all, only the basename did.
|
||||
*/
|
||||
if (end_of_old == old_path && end_of_new == new_path &&
|
||||
*end_of_old == *end_of_new)
|
||||
return; /* Note: *old_dir and *new_dir are still NULL */
|
||||
|
||||
/*
|
||||
* If end_of_new got back to the beginning of its string, and
|
||||
* end_of_old got back to the beginning of some subdirectory, then
|
||||
* we have a rename/merge of a subdirectory into the root, which
|
||||
* needs slightly special handling.
|
||||
*
|
||||
* Note: There is no need to consider the opposite case, with a
|
||||
* rename/merge of the root directory into some subdirectory
|
||||
* because as noted above the root directory always exists so it
|
||||
* cannot be considered to be renamed.
|
||||
*/
|
||||
if (end_of_new == new_path &&
|
||||
end_of_old != old_path && end_of_old[-1] == '/') {
|
||||
*old_dir = xstrndup(old_path, --end_of_old - old_path);
|
||||
*new_dir = xstrdup("");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've found the first non-matching character in the directory
|
||||
* paths. That means the current directory we were comparing
|
||||
* represents the rename. Move end_of_old and end_of_new back
|
||||
* to the full directory name.
|
||||
* paths. That means the current characters we were looking at
|
||||
* were part of the first non-matching subdir name going back from
|
||||
* the end of the strings. Get the whole name by advancing both
|
||||
* end_of_old and end_of_new to the NEXT '/' character. That will
|
||||
* represent the entire directory rename.
|
||||
*
|
||||
* The reason for the increment is cases like
|
||||
* a/b/star/foo/whatever.c -> a/b/tar/foo/random.c
|
||||
* After dropping the basename and going back to the first
|
||||
* non-matching character, we're now comparing:
|
||||
* a/b/s and a/b/
|
||||
* and we want to be comparing:
|
||||
* a/b/star/ and a/b/tar/
|
||||
* but without the pre-increment, the one on the right would stay
|
||||
* a/b/.
|
||||
*/
|
||||
if (*end_of_old == '/')
|
||||
end_of_old++;
|
||||
if (*end_of_old != '/')
|
||||
end_of_new++;
|
||||
end_of_old = strchr(end_of_old, '/');
|
||||
end_of_new = strchr(end_of_new, '/');
|
||||
end_of_old = strchr(++end_of_old, '/');
|
||||
end_of_new = strchr(++end_of_new, '/');
|
||||
|
||||
/*
|
||||
* It may have been the case that old_path and new_path were the same
|
||||
* directory all along. Don't claim a rename if they're the same.
|
||||
*/
|
||||
old_len = end_of_old - old_path;
|
||||
new_len = end_of_new - new_path;
|
||||
|
||||
if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
|
||||
*old_dir = xstrndup(old_path, old_len);
|
||||
*new_dir = xstrndup(new_path, new_len);
|
||||
}
|
||||
/* Copy the old and new directories into *old_dir and *new_dir. */
|
||||
*old_dir = xstrndup(old_path, end_of_old - old_path);
|
||||
*new_dir = xstrndup(new_path, end_of_new - new_path);
|
||||
}
|
||||
|
||||
static void remove_hashmap_entries(struct hashmap *dir_renames,
|
||||
|
Reference in New Issue
Block a user