This patchset introduces directory rename detection to merge-recursive. See https://public-inbox.org/git/20171110190550.27059-1-new...@gmail.com/ for the first series (including design considerations, etc.) This series continues to depend on en/merge-recursive-fixes in next, at least contextually. For the curious, follow-up series and comments can also be found at https://public-inbox.org/git/20171120220209.15111-1-new...@gmail.com/ https://public-inbox.org/git/20171121080059.32304-1-new...@gmail.com/ https://public-inbox.org/git/20171129014237.32570-1-new...@gmail.com/ https://public-inbox.org/git/20171228041352.27880-1-new...@gmail.com/ https://public-inbox.org/git/20180105202711.24311-1-new...@gmail.com/ https://public-inbox.org/git/20180130232533.25846-1-new...@gmail.com/
Also, as a reminder, this series fixes a few bugs somewhat as a side effect: * a bug causing dirty files involved in a rename to be overwritten * a few memory leaks Changes since v7 (full tbdiff follows below): * Added Stefan's Reviewed-by. * Squashed commits introducing new hash structs and associated functions into the commit that used them to avoid unused function warnings/errors. * Added or clarified a number of comments where things were unclear * Minor stuff: * Style (and typo) fixes for commit message and comments * Avoiding casting with hash initialization function * s/malloc/xmalloc/ * struct assignment * s/20/GIT_MAX_RAWSZ/ Elijah Newren (29): directory rename detection: basic testcases directory rename detection: directory splitting testcases directory rename detection: testcases to avoid taking detection too far directory rename detection: partially renamed directory testcase/discussion directory rename detection: files/directories in the way of some renames directory rename detection: testcases checking which side did the rename directory rename detection: more involved edge/corner testcases directory rename detection: testcases exploring possibly suboptimal merges directory rename detection: miscellaneous testcases to complete coverage directory rename detection: tests for handling overwriting untracked files directory rename detection: tests for handling overwriting dirty files merge-recursive: move the get_renames() function merge-recursive: introduce new functions to handle rename logic merge-recursive: fix leaks of allocated renames and diff_filepairs merge-recursive: make !o->detect_rename codepath more obvious merge-recursive: split out code for determining diff_filepairs merge-recursive: make a helper function for cleanup for handle_renames merge-recursive: add get_directory_renames() merge-recursive: check for directory level conflicts merge-recursive: add computation of collisions due to dir rename & merging merge-recursive: check for file level conflicts then get new name merge-recursive: when comparing files, don't include trees merge-recursive: apply necessary modifications for directory renames merge-recursive: avoid clobbering untracked files with directory renames merge-recursive: fix overwriting dirty files involved in renames merge-recursive: fix remaining directory rename + dirty overwrite cases directory rename detection: new testcases showcasing a pair of bugs merge-recursive: avoid spurious rename/rename conflict from dir renames merge-recursive: ensure we write updates for directory-renamed file merge-recursive.c | 1243 ++++++++++- merge-recursive.h | 27 + strbuf.c | 16 + strbuf.h | 16 + t/t3501-revert-cherry-pick.sh | 2 +- t/t6043-merge-rename-directories.sh | 3998 +++++++++++++++++++++++++++++++++++ t/t7607-merge-overwrite.sh | 2 +- unpack-trees.c | 4 +- unpack-trees.h | 4 + 9 files changed, 5197 insertions(+), 115 deletions(-) create mode 100755 t/t6043-merge-rename-directories.sh Full tbdiff (the biggest code changes come from commit squashing): 1: 5ba69c9c7b ! 1: 9f1d894d89 directory rename detection: basic testcases @@ -2,6 +2,7 @@ directory rename detection: basic testcases + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 2: e1d23f7f95 ! 2: 36a4b05757 directory rename detection: directory splitting testcases @@ -2,6 +2,7 @@ directory rename detection: directory splitting testcases + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 3: b10cb49cf9 ! 3: 031a835801 directory rename detection: testcases to avoid taking detection too far @@ -2,6 +2,7 @@ directory rename detection: testcases to avoid taking detection too far + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 4: ec3ccf0a95 ! 4: 5a09b80671 directory rename detection: partially renamed directory testcase/discussion @@ -2,6 +2,10 @@ directory rename detection: partially renamed directory testcase/discussion + Add a long note about why we are not considering "partial directory + renames" for the current directory rename detection implementation. + + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh @@ -40,7 +44,15 @@ +# path towards crazy corner cases that are far more complex than what we're +# already dealing with. +# -+# This section contains a test for this partially-renamed-directory case. ++# Note that the wording of the rule ("We don't do directory rename ++# detection if the directory still exists on both sides of the merge.") ++# also excludes "renaming" of a directory into a subdirectory of itself ++# (e.g. /some/dir/* -> /some/dir/subdir/*). It may be possible to carve ++# out an exception for "renaming"-beneath-itself cases without opening ++# weird edge/corner cases for other partial directory renames, but for now ++# we are keeping the rule simple. ++# ++# This section contains a test for a partially-renamed-directory case. +########################################################################### + +# Testcase 4a, Directory split, with original directory still present 5: da018f1adb ! 5: f6f7fe21b4 directory rename detection: files/directories in the way of some renames @@ -2,6 +2,7 @@ directory rename detection: files/directories in the way of some renames + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 6: f7ca54e7f2 ! 6: f34670c87a directory rename detection: testcases checking which side did the rename @@ -2,6 +2,7 @@ directory rename detection: testcases checking which side did the rename + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 7: 8ae28f45fe ! 7: 0bb552373e directory rename detection: more involved edge/corner testcases @@ -2,6 +2,7 @@ directory rename detection: more involved edge/corner testcases + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 8: 8d05b8dc10 ! 8: e9c6bcb5bf directory rename detection: testcases exploring possibly suboptimal merges @@ -2,6 +2,7 @@ directory rename detection: testcases exploring possibly suboptimal merges + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 9: 47ffccc86d ! 9: 80d1c2807f directory rename detection: miscellaneous testcases to complete coverage @@ -8,6 +8,7 @@ into the previous sections because I didn't want to re-label with all the testcase references. :-) + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 10: db7d9850c2 ! 10: 90257851c2 directory rename detection: tests for handling overwriting untracked files @@ -2,6 +2,7 @@ directory rename detection: tests for handling overwriting untracked files + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 11: 0de0a9dfa0 ! 11: e558395fe2 directory rename detection: tests for handling overwriting dirty files @@ -2,6 +2,7 @@ directory rename detection: tests for handling overwriting dirty files + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 12: 9a6777f577 ! 12: f7ce963690 merge-recursive: move the get_renames() function @@ -2,10 +2,12 @@ merge-recursive: move the get_renames() function - I want to re-use some other functions in the file without moving those - other functions or dealing with a handful of annoying split function - declarations and definitions. + Move this function so it can re-use some others (without either + moving all of them or adding an annoying split between function + declarations and definitions). Cheat slightly by adding a blank line + for readability, and in order to silence checkpatch.pl. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 13: ac6a95c7b8 ! 13: 467827818c merge-recursive: introduce new functions to handle rename logic @@ -16,6 +16,7 @@ which is used later in process_entry(). Thus the reason for a separate cleanup_renames(). + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 14: 76b09d49cd ! 14: 2079029a75 merge-recursive: fix leaks of allocated renames and diff_filepairs @@ -8,6 +8,7 @@ return string_list. Make sure all of these are deallocated when we are done with them. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 15: e4189f3da2 ! 15: 6b5b10e76f merge-recursive: make !o->detect_rename codepath more obvious @@ -7,6 +7,7 @@ iterate over. It seems more straightforward to simply avoid calling either function in that case. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 16: 6bc800e369 ! 16: 02cf55e49e merge-recursive: split out code for determining diff_filepairs @@ -7,6 +7,7 @@ get_renames(), I want them to be available to some new functions. No actual logic changes yet. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c @@ -48,10 +49,8 @@ o->needed_rename_limit = opts.needed_rename_limit; - for (i = 0; i < diff_queued_diff.nr; ++i) { + -+ ret = malloc(sizeof(struct diff_queue_struct)); -+ ret->queue = diff_queued_diff.queue; -+ ret->nr = diff_queued_diff.nr; -+ /* Ignore diff_queued_diff.alloc; we won't be changing size at all */ ++ ret = xmalloc(sizeof(*ret)); ++ *ret = diff_queued_diff; + + opts.output_format = DIFF_FORMAT_NO_OUTPUT; + diff_queued_diff.nr = 0; 17: 0757c92ca1 < --: ------- merge-recursive: add a new hashmap for storing directory renames 18: f17343fc2c ! 17: 24f31fa43a merge-recursive: make a helper function for cleanup for handle_renames @@ -8,6 +8,7 @@ helper initial_cleanup_rename(), and leave the big comment in the code about why we can't do all the cleanup at once. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 19: 9b63e257c8 ! 18: ae89010bec merge-recursive: add get_directory_renames() @@ -2,15 +2,68 @@ merge-recursive: add get_directory_renames() - This populates a list of directory renames for us. The list of - directory renames is not yet used, but will be in subsequent commits. + This populates a set of directory renames for us. The set of directory + renames is not yet used, but will be in subsequent commits. + Note that the use of a string_list for possible_new_dirs in the new + dir_rename_entry struct implies an O(n^2) algorithm; however, in practice + I expect the number of distinct directories that files were renamed into + from a single original directory to be O(1). My guess is that n has a + mode of 1 and a mean of less than 2, so, for now, string_list seems good + enough for possible_new_dirs. + + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c --- a/merge-recursive.c +++ b/merge-recursive.c @@ + return ignore_case ? strihash(path) : strhash(path); + } + ++static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap, ++ char *dir) ++{ ++ struct dir_rename_entry key; ++ ++ if (dir == NULL) ++ return NULL; ++ hashmap_entry_init(&key, strhash(dir)); ++ key.dir = dir; ++ return hashmap_get(hashmap, &key, NULL); ++} ++ ++static int dir_rename_cmp(const void *unused_cmp_data, ++ const void *entry, ++ const void *entry_or_key, ++ const void *unused_keydata) ++{ ++ const struct dir_rename_entry *e1 = entry; ++ const struct dir_rename_entry *e2 = entry_or_key; ++ ++ return strcmp(e1->dir, e2->dir); ++} ++ ++static void dir_rename_init(struct hashmap *map) ++{ ++ hashmap_init(map, dir_rename_cmp, NULL, 0); ++} ++ ++static void dir_rename_entry_init(struct dir_rename_entry *entry, ++ char *directory) ++{ ++ hashmap_entry_init(entry, strhash(directory)); ++ entry->dir = directory; ++ entry->non_unique_new_dir = 0; ++ strbuf_init(&entry->new_dir, 0); ++ string_list_init(&entry->possible_new_dirs, 0); ++} ++ + static void flush_output(struct merge_options *o) + { + if (o->buffer_output < 2 && o->obuf.len) { +@@ return ret; } @@ -23,12 +76,13 @@ + *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. ++ /* ++ * For ++ * "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c" ++ * the "e/foo.c" part is the same, we just want to know that ++ * "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. @@ -76,7 +130,23 @@ + struct dir_rename_entry *entry; + int i; + -+ dir_renames = malloc(sizeof(struct hashmap)); ++ /* ++ * Typically, we think of a directory rename as all files from a ++ * certain directory being moved to a target directory. However, ++ * what if someone first moved two files from the original ++ * directory in one commit, and then renamed the directory ++ * somewhere else in a later commit? At merge time, we just know ++ * that files from the original directory went to two different ++ * places, and that the bulk of them ended up in the same place. ++ * We want each directory rename to represent where the bulk of the ++ * files from that directory end up; this function exists to find ++ * where the bulk of the files went. ++ * ++ * The first loop below simply iterates through the list of file ++ * renames, finding out how often each directory rename pair ++ * possibility occurs. ++ */ ++ dir_renames = xmalloc(sizeof(struct hashmap)); + dir_rename_init(dir_renames); + for (i = 0; i < pairs->nr; ++i) { + struct string_list_item *item; @@ -114,6 +184,15 @@ + *count += 1; + } + ++ /* ++ * For each directory with files moved out of it, we find out which ++ * target directory received the most files so we can declare it to ++ * be the "winning" target location for the directory rename. This ++ * winner gets recorded in new_dir. If there is no winner ++ * (multiple target directories received the same number of files), ++ * we set non_unique_new_dir. Once we've determined the winner (or ++ * that there is no winner), we no longer need possible_new_dirs. ++ */ + hashmap_iter_init(dir_renames, &iter); + while ((entry = hashmap_iter_next(&iter))) { + int max = 0; @@ -136,8 +215,13 @@ + assert(entry->new_dir.len == 0); + strbuf_addstr(&entry->new_dir, best); + } -+ /* Strings were xstrndup'ed before inserting into string-list, -+ * so ask string_list to remove the entries for us. ++ /* ++ * The relevant directory sub-portion of the original full ++ * filepaths were xstrndup'ed before inserting into ++ * possible_new_dirs, and instead of manually iterating the ++ * list and free'ing each, just lie and tell ++ * possible_new_dirs that it did the strdup'ing so that it ++ * will free them for us. + */ + entry->possible_new_dirs.strdup_strings = 1; + string_list_clear(&entry->possible_new_dirs, 1); @@ -201,3 +285,32 @@ return clean; } + +diff --git a/merge-recursive.h b/merge-recursive.h +--- a/merge-recursive.h ++++ b/merge-recursive.h +@@ + struct string_list df_conflict_file_set; + }; + ++/* ++ * For dir_rename_entry, directory names are stored as a full path from the ++ * toplevel of the repository and do not include a trailing '/'. Also: ++ * ++ * dir: original name of directory being renamed ++ * non_unique_new_dir: if true, could not determine new_dir ++ * new_dir: final name of directory being renamed ++ * possible_new_dirs: temporary used to help determine new_dir; see comments ++ * in get_directory_renames() for details ++ */ ++struct dir_rename_entry { ++ struct hashmap_entry ent; /* must be the first member! */ ++ char *dir; ++ unsigned non_unique_new_dir:1; ++ struct strbuf new_dir; ++ struct string_list possible_new_dirs; ++}; ++ + /* merge_trees() but with recursive ancestor consolidation */ + int merge_recursive(struct merge_options *o, + struct commit *h1, 20: 6730d8e7b7 ! 19: 4f36512a02 merge-recursive: check for directory level conflicts @@ -7,6 +7,7 @@ directory level. There will be additional checks at the individual file level too, which will be added later. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c @@ -18,7 +19,7 @@ +static int tree_has_path(struct tree *tree, const char *path) +{ -+ unsigned char hashy[20]; ++ unsigned char hashy[GIT_MAX_RAWSZ]; + unsigned int mode_o; + + return !get_tree_entry(tree->object.oid.hash, path, 21: 178ec9e079 < --: ------- merge-recursive: add a new hashmap for storing file collisions 22: 1f3ff65e82 ! 20: 4a9098fba5 merge-recursive: add computation of collisions due to dir rename & merging @@ -7,11 +7,42 @@ the same (otherwise vacant) location. Add checking and reporting for such cases, falling back to no-directory-rename handling for such paths. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c --- a/merge-recursive.c +++ b/merge-recursive.c +@@ + string_list_init(&entry->possible_new_dirs, 0); + } + ++static struct collision_entry *collision_find_entry(struct hashmap *hashmap, ++ char *target_file) ++{ ++ struct collision_entry key; ++ ++ hashmap_entry_init(&key, strhash(target_file)); ++ key.target_file = target_file; ++ return hashmap_get(hashmap, &key, NULL); ++} ++ ++static int collision_cmp(void *unused_cmp_data, ++ const struct collision_entry *e1, ++ const struct collision_entry *e2, ++ const void *unused_keydata) ++{ ++ return strcmp(e1->target_file, e2->target_file); ++} ++ ++static void collision_init(struct hashmap *map) ++{ ++ hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0); ++} ++ + static void flush_output(struct merge_options *o) + { + if (o->buffer_output < 2 && o->obuf.len) { @@ hashy, &mode_o); } @@ -179,3 +210,21 @@ common, head, merge, entries); clean = process_renames(o, ri->head_renames, ri->merge_renames); + +diff --git a/merge-recursive.h b/merge-recursive.h +--- a/merge-recursive.h ++++ b/merge-recursive.h +@@ + struct string_list possible_new_dirs; + }; + ++struct collision_entry { ++ struct hashmap_entry ent; /* must be the first member! */ ++ char *target_file; ++ struct string_list source_files; ++ unsigned reported_already:1; ++}; ++ + /* merge_trees() but with recursive ancestor consolidation */ + int merge_recursive(struct merge_options *o, + struct commit *h1, 23: d28651aeb0 ! 21: fd9129379f merge-recursive: check for file level conflicts then get new name @@ -7,6 +7,7 @@ file level either. If there aren't any, then get the new name from any directory renames. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 24: d6f3d47304 ! 22: 94eaf30851 merge-recursive: when comparing files, don't include trees @@ -15,6 +15,7 @@ for a given path on the different sides of the merge, so create a get_tree_entry_if_blob() helper function and use it. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 25: f91509f9df ! 23: 389b0d6bda merge-recursive: apply necessary modifications for directory renames @@ -6,6 +6,7 @@ necessary changes to the rename struct, it's dst_entry, and the diff_filepair under consideration. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 26: 9d903c98de ! 24: 5a5f25c6e0 merge-recursive: avoid clobbering untracked files with directory renames @@ -2,6 +2,7 @@ merge-recursive: avoid clobbering untracked files with directory renames + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 27: 2ab61d26a3 ! 25: 45819be1f8 merge-recursive: fix overwriting dirty files involved in renames @@ -5,9 +5,10 @@ This fixes an issue that existed before my directory rename detection patches that affects both normal renames and renames implied by directory rename detection. Additional codepaths that only affect - overwriting of directy files that are involved in directory rename + overwriting of dirty files that are involved in directory rename detection will be added in a subsequent commit. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c @@ -186,7 +187,7 @@ + struct unpack_trees_options unpack_opts; }; - struct dir_rename_entry { + /* diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh --- a/t/t3501-revert-cherry-pick.sh 28: d510c260b7 ! 26: b840086726 merge-recursive: fix remaining directory rename + dirty overwrite cases @@ -2,6 +2,7 @@ merge-recursive: fix remaining directory rename + dirty overwrite cases + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c @@ -22,8 +23,7 @@ + rename->path); + } + /* -+ * Stupid double negatives in remove_file; it somehow manages -+ * to repeatedly mess me up. So, just for myself: ++ * Because the double negatives somehow keep confusing me... + * 1) update_wd iff !ren_src_was_dirty. + * 2) no_wd iff !update_wd + * 3) so, no_wd == !!ren_src_was_dirty == ren_src_was_dirty 29: b59a612e68 ! 27: d320f88ef3 directory rename detection: new testcases showcasing a pair of bugs @@ -12,6 +12,7 @@ testcases that showed existing bugs in order to make sure we aren't merely addressing problems in isolation but in general. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/t/t6043-merge-rename-directories.sh b/t/t6043-merge-rename-directories.sh 30: d20b759b63 ! 28: e6574d990c merge-recursive: avoid spurious rename/rename conflict from dir renames @@ -11,6 +11,7 @@ previously reported as a rename/delete conflict will now be reported as a modify/delete conflict. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c 31: f69932adfe ! 29: 32446f2578 merge-recursive: ensure we write updates for directory-renamed file @@ -10,6 +10,7 @@ Update the code that checks whether we can skip the update to also work in the presence of directory renames. + Reviewed-by: Stefan Beller <sbel...@google.com> Signed-off-by: Elijah Newren <new...@gmail.com> diff --git a/merge-recursive.c b/merge-recursive.c -- 2.16.1.232.g28d5be9217