Skip to content

Commit ef02b31

Browse files
newrengitster
authored andcommitted
merge-recursive: Make room for directories in D/F conflicts
When there are unmerged entries present, make sure to check for D/F conflicts first and remove any files present in HEAD that would be in the way of creating files below the correspondingly named directory. Such files will be processed again at the end of the merge in process_df_entry(); at that time we will be able to tell if we need to and can reinstate the file, whether we need to place its contents in a different file due to the directory still being present, etc. Signed-off-by: Elijah Newren <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 84a08a4 commit ef02b31

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

merge-recursive.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,63 @@ static struct string_list *get_unmerged(void)
346346
return unmerged;
347347
}
348348

349+
static void make_room_for_directories_of_df_conflicts(struct merge_options *o,
350+
struct string_list *entries)
351+
{
352+
/* If there are D/F conflicts, and the paths currently exist
353+
* in the working copy as a file, we want to remove them to
354+
* make room for the corresponding directory. Such paths will
355+
* later be processed in process_df_entry() at the end. If
356+
* the corresponding directory ends up being removed by the
357+
* merge, then the file will be reinstated at that time;
358+
* otherwise, if the file is not supposed to be removed by the
359+
* merge, the contents of the file will be placed in another
360+
* unique filename.
361+
*
362+
* NOTE: This function relies on the fact that entries for a
363+
* D/F conflict will appear adjacent in the index, with the
364+
* entries for the file appearing before entries for paths
365+
* below the corresponding directory.
366+
*/
367+
const char *last_file = NULL;
368+
int last_len;
369+
struct stage_data *last_e;
370+
int i;
371+
372+
for (i = 0; i < entries->nr; i++) {
373+
const char *path = entries->items[i].string;
374+
int len = strlen(path);
375+
struct stage_data *e = entries->items[i].util;
376+
377+
/*
378+
* Check if last_file & path correspond to a D/F conflict;
379+
* i.e. whether path is last_file+'/'+<something>.
380+
* If so, remove last_file to make room for path and friends.
381+
*/
382+
if (last_file &&
383+
len > last_len &&
384+
memcmp(path, last_file, last_len) == 0 &&
385+
path[last_len] == '/') {
386+
output(o, 3, "Removing %s to make room for subdirectory; may re-add later.", last_file);
387+
unlink(last_file);
388+
}
389+
390+
/*
391+
* Determine whether path could exist as a file in the
392+
* working directory as a possible D/F conflict. This
393+
* will only occur when it exists in stage 2 as a
394+
* file.
395+
*/
396+
if (S_ISREG(e->stages[2].mode) || S_ISLNK(e->stages[2].mode)) {
397+
last_file = path;
398+
last_len = len;
399+
last_e = e;
400+
} else {
401+
last_file = NULL;
402+
}
403+
}
404+
}
405+
349406
struct rename
350407
{
351408
struct diff_filepair *pair;
@@ -1488,6 +1545,7 @@ int merge_trees(struct merge_options *o,
14881545
get_files_dirs(o, merge);
14891546

14901547
entries = get_unmerged();
1548+
make_room_for_directories_of_df_conflicts(o, entries);
14911549
re_head = get_renames(o, head, common, head, merge, entries);
14921550
re_merge = get_renames(o, merge, common, head, merge, entries);
14931551
clean = process_renames(o, re_head, re_merge);

t/t6020-merge-df.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ test_expect_success 'modify/delete + directory/file conflict' '
8383
test -f letters~modify
8484
'
8585

86-
test_expect_failure 'modify/delete + directory/file conflict; other way' '
86+
test_expect_success 'modify/delete + directory/file conflict; other way' '
8787
git reset --hard &&
8888
git clean -f &&
8989
git checkout modify^0 &&

0 commit comments

Comments
 (0)