forked from git/git
-
Notifications
You must be signed in to change notification settings - Fork 143
Dir rename fixes #390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Dir rename fixes #390
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1931,6 +1931,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); | ||
|
@@ -1943,8 +1953,8 @@ static void get_renamed_dir_portion(const char *old_path, const char *new_path, | |
char **old_dir, char **new_dir) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the Git mailing list, Johannes Schindelin wrote (reply to this):
|
||
{ | ||
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 +1965,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 */ | ||
|
||
/* | ||
* 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; /* Note: *old_dir and *new_dir are still NULL */ | ||
|
||
/* | ||
* 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. | ||
* 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. | ||
*/ | ||
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); | ||
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 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/. | ||
*/ | ||
end_of_old = strchr(++end_of_old, '/'); | ||
end_of_new = strchr(++end_of_new, '/'); | ||
|
||
/* 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, | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Johannes Schindelin wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Elijah Newren wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Johannes Schindelin wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Elijah Newren wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Johannes Schindelin wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Junio C Hamano wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Elijah Newren wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Johannes Schindelin wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Elijah Newren wrote (reply to this):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Johannes Schindelin wrote (reply to this):