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);
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;
* "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.
- */
-
- /*
- * If the basename of the file changed, we don't care. We want
- * to know which portion of the directory, if any, changed.
+ *
+ * Also, 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)
- return; /* We haven't modified *old_dir or *new_dir yet. */
- /* Find the first non-matching character traversing backwards */
+ if (end_of_old == NULL || end_of_new == NULL)
+ return;
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; /* We haven't modified *old_dir or *new_dir yet. */
-
- /*
- * 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.
- * 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).
+ * 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.
*/
- if (end_of_new == new_path &&
- end_of_old != old_path && end_of_old[-1] == '/') {
- *old_dir = xstrndup(old_path, end_of_old-1 - old_path);
- *new_dir = xstrndup(new_path, end_of_new - new_path);
- return;
- }
+ 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, '/');
/*
- * We've found the first non-matching character in the directory
- * 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/.
+ * 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.
*/
- end_of_old = strchr(++end_of_old, '/');
- end_of_new = strchr(++end_of_new, '/');
+ old_len = end_of_old - old_path;
+ new_len = end_of_new - new_path;
- /* 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);
+ 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);
+ }
}
static void remove_hashmap_entries(struct hashmap *dir_renames,
)
'
-# Testcase 12d, Rename/merge of subdirectory into the root
-# Commit O: a/b/{foo.c}
-# Commit A: foo.c
-# Commit B: a/b/{foo.c,bar.c}
-# Expected: a/b/{foo.c,bar.c}
-
-test_expect_success '12d-setup: Rename (merge) of subdirectory into the root' '
- test_create_repo 12d &&
- (
- cd 12d &&
-
- mkdir -p a/b/subdir &&
- test_commit a/b/subdir/foo.c &&
-
- git branch O &&
- git branch A &&
- git branch B &&
-
- git checkout A &&
- mkdir subdir &&
- git mv a/b/subdir/foo.c.t subdir/foo.c.t &&
- test_tick &&
- git commit -m "A" &&
-
- git checkout B &&
- test_commit a/b/bar.c
- )
-'
-
-test_expect_success '12d-check: Rename (merge) of subdirectory into the root' '
- (
- cd 12d &&
-
- git checkout A^0 &&
-
- git -c merge.directoryRenames=true merge -s recursive B^0 &&
-
- git ls-files -s >out &&
- test_line_count = 2 out &&
-
- git rev-parse >actual \
- HEAD:subdir/foo.c.t HEAD:bar.c.t &&
- git rev-parse >expect \
- O:a/b/subdir/foo.c.t B:a/b/bar.c.t &&
- test_cmp expect actual &&
-
- git hash-object bar.c.t >actual &&
- git rev-parse B:a/b/bar.c.t >expect &&
- test_cmp expect actual &&
-
- test_must_fail git rev-parse HEAD:a/b/subdir/foo.c.t &&
- test_must_fail git rev-parse HEAD:a/b/bar.c.t &&
- test_path_is_missing a/
- )
-'
-
###########################################################################
# SECTION 13: Checking informational and conflict messages
#