From: Elijah Newren <newren@gmail.com>
To: git@vger.kernel.org
Cc: Elijah Newren <newren@gmail.com>
Subject: [PATCH 21/30] merge-recursive: Add get_directory_renames()
Date: Fri, 10 Nov 2017 11:05:41 -0800 [thread overview]
Message-ID: <20171110190550.27059-22-newren@gmail.com> (raw)
In-Reply-To: <20171110190550.27059-1-newren@gmail.com>
This populates a list of directory renames for us. The list of
directory renames is not yet used, but will be in subsequent commits.
Signed-off-by: Elijah Newren <newren@gmail.com>
---
merge-recursive.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 146 insertions(+)
diff --git a/merge-recursive.c b/merge-recursive.c
index 89a9b32635..b5770d3d7f 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1376,6 +1376,124 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
return ret;
}
+static void get_renamed_dir_portion(const char *old_path, const char *new_path,
+ char **old_dir, char **new_dir) {
+ *old_dir = NULL;
+ *new_dir = NULL;
+
+ /* For
+ * "a/b/c/d/foo.c" -> "a/b/something-else/d/foo.c"
+ * the "d/foo.c" part is the same, we just want to know that
+ * "a/b/c" was renamed to "a/b/something-else"
+ * so, for this example, this function returns "a/b/c" in
+ * *old_dir and "a/b/something-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.
+ */
+ char *end_of_old = strrchr(old_path, '/');
+ char *end_of_new = strrchr(new_path, '/');
+ 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
+ /*
+ * 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_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, '/');
+
+ /*
+ * 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.
+ */
+ int old_len = end_of_old - old_path;
+ int new_len = end_of_new - new_path;
+
+ if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
+ *old_dir = strndup(old_path, old_len);
+ *new_dir = strndup(new_path, new_len);
+ }
+}
+
+static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
+ struct tree *tree) {
+ struct hashmap *dir_renames;
+ struct hashmap_iter iter;
+ struct dir_rename_entry *entry;
+ int i;
+
+ dir_renames = malloc(sizeof(struct hashmap));
+ dir_rename_init(dir_renames);
+ for (i = 0; i < pairs->nr; ++i) {
+ struct string_list_item *item;
+ int *count;
+ struct diff_filepair *pair = pairs->queue[i];
+
+ char *old_dir, *new_dir;
+ get_renamed_dir_portion(pair->one->path, pair->two->path,
+ &old_dir, &new_dir);
+ if (!old_dir)
+ // Directory didn't change at all; ignore this one.
+ continue;
+
+ entry = dir_rename_find_entry(dir_renames, old_dir);
+ if (!entry) {
+ entry = xcalloc(1, sizeof(struct dir_rename_entry));
+ hashmap_entry_init(entry, strhash(old_dir));
+ hashmap_put(dir_renames, entry);
+ entry->dir = old_dir;
+ } else {
+ free(old_dir);
+ }
+ item = string_list_lookup(&entry->possible_new_dirs, new_dir);
+ if (!item) {
+ item = string_list_insert(&entry->possible_new_dirs, new_dir);
+ item->util = xcalloc(1, sizeof(int));
+ } else {
+ free(new_dir);
+ }
+ count = item->util;
+ *count += 1;
+ }
+
+ hashmap_iter_init(dir_renames, &iter);
+ while ((entry = hashmap_iter_next(&iter))) {
+ int max = 0;
+ int bad_max = 0;
+ char *best = NULL;
+ for (i = 0; i < entry->possible_new_dirs.nr; i++) {
+ int *count = entry->possible_new_dirs.items[i].util;
+ if (*count == max)
+ bad_max = max;
+ else if (*count > max) {
+ max = *count;
+ best = entry->possible_new_dirs.items[i].string;
+ }
+ }
+ if (bad_max == max)
+ entry->non_unique_new_dir = 1;
+ else
+ entry->new_dir = strdup(best);
+ /* Strings were strndup'ed before inserting into string-list,
+ * so ask string_list to remove the entries for us.
+ */
+ entry->possible_new_dirs.strdup_strings = 1;
+ string_list_clear(&entry->possible_new_dirs, 1);
+ }
+
+ return dir_renames;
+}
+
/*
* Get information of all renames which occurred in 'pairs', making use of
* any implicit directory renames inferred from the other side of history.
@@ -1695,6 +1813,9 @@ static struct rename_info *handle_renames(struct merge_options *o,
{
struct rename_info *rei = xcalloc(1, sizeof(struct rename_info));
struct diff_queue_struct *head_pairs, *merge_pairs;
+ struct hashmap *dir_re_head, *dir_re_merge;
+ struct hashmap_iter iter;
+ struct dir_rename_entry *e;
*clean = 1;
if (!o->detect_rename)
@@ -1703,6 +1824,9 @@ static struct rename_info *handle_renames(struct merge_options *o,
head_pairs = get_diffpairs(o, common, head);
merge_pairs = get_diffpairs(o, common, merge);
+ dir_re_head = get_directory_renames(head_pairs, head);
+ dir_re_merge = get_directory_renames(merge_pairs, merge);
+
rei->head_renames = get_renames(o, head_pairs, head,
common, head, merge, entries);
rei->merge_renames = get_renames(o, merge_pairs, merge,
@@ -1716,6 +1840,28 @@ static struct rename_info *handle_renames(struct merge_options *o,
* process_entry(). But there are a few things we can free now.
*/
+ hashmap_iter_init(dir_re_head, &iter);
+ while ((e = hashmap_iter_next(&iter))) {
+ free(e->dir);
+ if (e->new_dir)
+ free(e->new_dir);
+ /* possible_new_dirs already cleared in get_directory_renames */
+ //string_list_clear(&e->possible_new_dirs, 1);
+ }
+ hashmap_free(dir_re_head, 1);
+ free(dir_re_head);
+
+ hashmap_iter_init(dir_re_merge, &iter);
+ while ((e = hashmap_iter_next(&iter))) {
+ free(e->dir);
+ if (e->new_dir)
+ free(e->new_dir);
+ /* possible_new_dirs already cleared in get_directory_renames */
+ //string_list_clear(&e->possible_new_dirs, 1);
+ }
+ hashmap_free(dir_re_merge, 1);
+ free(dir_re_merge);
+
free(head_pairs->queue);
free(head_pairs);
free(merge_pairs->queue);
--
2.15.0.5.g9567be9905
next prev parent reply other threads:[~2017-11-10 19:07 UTC|newest]
Thread overview: 81+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-11-10 19:05 [PATCH 00/30] Add directory rename detection to git Elijah Newren
2017-11-10 19:05 ` [PATCH 01/30] Tighten and correct a few testcases for merging and cherry-picking Elijah Newren
2017-11-13 19:32 ` Stefan Beller
2017-11-10 19:05 ` [PATCH 02/30] merge-recursive: Fix logic ordering issue Elijah Newren
2017-11-13 19:48 ` Stefan Beller
2017-11-13 22:04 ` Elijah Newren
2017-11-13 22:12 ` Stefan Beller
2017-11-13 23:39 ` Elijah Newren
2017-11-13 23:46 ` Stefan Beller
2017-11-10 19:05 ` [PATCH 03/30] merge-recursive: Add explanation for src_entry and dst_entry Elijah Newren
2017-11-13 21:06 ` Stefan Beller
2017-11-13 22:57 ` Elijah Newren
2017-11-13 23:11 ` Stefan Beller
2017-11-14 1:26 ` Junio C Hamano
2017-11-10 19:05 ` [PATCH 04/30] directory rename detection: basic testcases Elijah Newren
2017-11-13 22:04 ` Stefan Beller
2017-11-14 0:57 ` Elijah Newren
2017-11-14 1:21 ` Stefan Beller
2017-11-14 1:40 ` Elijah Newren
2017-11-14 2:03 ` Junio C Hamano
2017-11-10 19:05 ` [PATCH 05/30] directory rename detection: directory splitting testcases Elijah Newren
2017-11-13 23:20 ` Stefan Beller
2017-11-10 19:05 ` [PATCH 06/30] directory rename detection: testcases to avoid taking detection too far Elijah Newren
2017-11-13 23:25 ` Stefan Beller
2017-11-14 1:02 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 07/30] directory rename detection: partially renamed directory testcase/discussion Elijah Newren
2017-11-14 0:07 ` Stefan Beller
2017-11-10 19:05 ` [PATCH 08/30] directory rename detection: files/directories in the way of some renames Elijah Newren
2017-11-14 0:15 ` Stefan Beller
2017-11-14 1:19 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 09/30] directory rename detection: testcases checking which side did the rename Elijah Newren
2017-11-14 0:25 ` Stefan Beller
2017-11-14 1:30 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 10/30] directory rename detection: more involved edge/corner testcases Elijah Newren
2017-11-14 0:42 ` Stefan Beller
2017-11-14 21:11 ` Elijah Newren
2017-11-14 22:47 ` Stefan Beller
2017-11-10 19:05 ` [PATCH 11/30] directory rename detection: testcases exploring possibly suboptimal merges Elijah Newren
2017-11-14 20:33 ` Stefan Beller
2017-11-14 21:42 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 12/30] directory rename detection: miscellaneous testcases to complete coverage Elijah Newren
2017-11-15 20:03 ` Stefan Beller
2017-11-16 21:17 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 13/30] directory rename detection: tests for handling overwriting untracked files Elijah Newren
2017-11-10 19:05 ` [PATCH 14/30] directory rename detection: tests for handling overwriting dirty files Elijah Newren
2017-11-10 19:05 ` [PATCH 15/30] merge-recursive: Move the get_renames() function Elijah Newren
2017-11-14 4:46 ` Junio C Hamano
2017-11-14 17:41 ` Elijah Newren
2017-11-15 1:20 ` Junio C Hamano
2017-11-10 19:05 ` [PATCH 16/30] merge-recursive: Introduce new functions to handle rename logic Elijah Newren
2017-11-14 4:56 ` Junio C Hamano
2017-11-14 5:14 ` Junio C Hamano
2017-11-14 18:24 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 17/30] merge-recursive: Fix leaks of allocated renames and diff_filepairs Elijah Newren
2017-11-14 4:58 ` Junio C Hamano
2017-11-10 19:05 ` [PATCH 18/30] merge-recursive: Make !o->detect_rename codepath more obvious Elijah Newren
2017-11-10 19:05 ` [PATCH 19/30] merge-recursive: Split out code for determining diff_filepairs Elijah Newren
2017-11-14 5:20 ` Junio C Hamano
2017-11-10 19:05 ` [PATCH 20/30] merge-recursive: Add a new hashmap for storing directory renames Elijah Newren
2017-11-10 19:05 ` Elijah Newren [this message]
2017-11-14 5:30 ` [PATCH 21/30] merge-recursive: Add get_directory_renames() Junio C Hamano
2017-11-14 18:38 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 22/30] merge-recursive: Check for directory level conflicts Elijah Newren
2017-11-10 19:05 ` [PATCH 23/30] merge-recursive: Add a new hashmap for storing file collisions Elijah Newren
2017-11-10 19:05 ` [PATCH 24/30] merge-recursive: Add computation of collisions due to dir rename & merging Elijah Newren
2018-06-10 10:56 ` René Scharfe
2018-06-10 11:03 ` René Scharfe
2018-06-10 20:44 ` Jeff King
2018-06-11 15:03 ` Elijah Newren
2018-06-14 17:36 ` Junio C Hamano
2017-11-10 19:05 ` [PATCH 25/30] merge-recursive: Check for file level conflicts then get new name Elijah Newren
2017-11-10 19:05 ` [PATCH 26/30] merge-recursive: When comparing files, don't include trees Elijah Newren
2017-11-10 19:05 ` [PATCH 27/30] merge-recursive: Apply necessary modifications for directory renames Elijah Newren
2017-11-15 20:23 ` Stefan Beller
2017-11-16 3:54 ` Elijah Newren
2017-11-10 19:05 ` [PATCH 28/30] merge-recursive: Avoid clobbering untracked files with " Elijah Newren
2017-11-10 19:05 ` [RFC PATCH 29/30] merge-recursive: Fix overwriting dirty files involved in renames Elijah Newren
2017-11-10 19:05 ` [PATCH 30/30] merge-recursive: Fix remaining directory rename + dirty overwrite cases Elijah Newren
2017-11-10 22:27 ` [PATCH 00/30] Add directory rename detection to git Philip Oakley
2017-11-10 23:26 ` Elijah Newren
2017-11-13 15:04 ` Philip Oakley
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20171110190550.27059-22-newren@gmail.com \
--to=newren@gmail.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).