summaryrefslogtreecommitdiff
path: root/merge-recursive.c
diff options
context:
space:
mode:
authorElijah Newren <newren@gmail.com>2019-10-22 21:22:49 (GMT)
committerJunio C Hamano <gitster@pobox.com>2019-10-23 02:32:48 (GMT)
commitd3eebaad5ef02284c86d6c4b80199eac58c6729b (patch)
treec7011e7a5f8a7a7476d25451173554dd5d9e224b /merge-recursive.c
parent08da6496b61341ec45eac36afcc8f94242763468 (diff)
downloadgit-d3eebaad5ef02284c86d6c4b80199eac58c6729b.zip
git-d3eebaad5ef02284c86d6c4b80199eac58c6729b.tar.gz
git-d3eebaad5ef02284c86d6c4b80199eac58c6729b.tar.bz2
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>
Diffstat (limited to 'merge-recursive.c')
-rw-r--r--merge-recursive.c60
1 files changed, 36 insertions, 24 deletions
diff --git a/merge-recursive.c b/merge-recursive.c
index 22a12cf..f80e48f 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1943,8 +1943,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;
@@ -1955,43 +1955,55 @@ 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)
- return;
+ return; /* We haven't modified *old_dir or *new_dir yet. */
+
+ /* 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 */
+
/*
- * 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 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 == '/')
- end_of_old++;
- if (*end_of_old != '/')
- end_of_new++;
- end_of_old = strchr(end_of_old, '/');
- end_of_new = strchr(end_of_new, '/');
+ 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. */
/*
- * 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.
+ * 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/.
*/
- old_len = end_of_old - old_path;
- new_len = end_of_new - new_path;
+ end_of_old = strchr(++end_of_old, '/');
+ end_of_new = strchr(++end_of_new, '/');
- 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,