Skip to content

Commit 3b9616f

Browse files
newrengitster
authored andcommitted
merge-recursive: apply necessary modifications for directory renames
This commit hooks together all the directory rename logic by making the necessary changes to the rename struct, it's dst_entry, and the diff_filepair under consideration. Reviewed-by: Stefan Beller <[email protected]> Signed-off-by: Elijah Newren <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7d38849 commit 3b9616f

File tree

2 files changed

+211
-26
lines changed

2 files changed

+211
-26
lines changed

merge-recursive.c

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ static int oid_eq(const struct object_id *a, const struct object_id *b)
180180

181181
enum rename_type {
182182
RENAME_NORMAL = 0,
183+
RENAME_DIR,
183184
RENAME_DELETE,
184185
RENAME_ONE_FILE_TO_ONE,
185186
RENAME_ONE_FILE_TO_TWO,
@@ -610,6 +611,7 @@ struct rename {
610611
*/
611612
struct stage_data *src_entry;
612613
struct stage_data *dst_entry;
614+
unsigned add_turned_into_rename:1;
613615
unsigned processed:1;
614616
};
615617

@@ -644,6 +646,27 @@ static int update_stages(struct merge_options *opt, const char *path,
644646
return 0;
645647
}
646648

649+
static int update_stages_for_stage_data(struct merge_options *opt,
650+
const char *path,
651+
const struct stage_data *stage_data)
652+
{
653+
struct diff_filespec o, a, b;
654+
655+
o.mode = stage_data->stages[1].mode;
656+
oidcpy(&o.oid, &stage_data->stages[1].oid);
657+
658+
a.mode = stage_data->stages[2].mode;
659+
oidcpy(&a.oid, &stage_data->stages[2].oid);
660+
661+
b.mode = stage_data->stages[3].mode;
662+
oidcpy(&b.oid, &stage_data->stages[3].oid);
663+
664+
return update_stages(opt, path,
665+
is_null_oid(&o.oid) ? NULL : &o,
666+
is_null_oid(&a.oid) ? NULL : &a,
667+
is_null_oid(&b.oid) ? NULL : &b);
668+
}
669+
647670
static void update_entry(struct stage_data *entry,
648671
struct diff_filespec *o,
649672
struct diff_filespec *a,
@@ -1111,6 +1134,18 @@ static int merge_file_one(struct merge_options *o,
11111134
return merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);
11121135
}
11131136

1137+
static int conflict_rename_dir(struct merge_options *o,
1138+
struct diff_filepair *pair,
1139+
const char *rename_branch,
1140+
const char *other_branch)
1141+
{
1142+
const struct diff_filespec *dest = pair->two;
1143+
1144+
if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
1145+
return -1;
1146+
return 0;
1147+
}
1148+
11141149
static int handle_change_delete(struct merge_options *o,
11151150
const char *path, const char *old_path,
11161151
const struct object_id *o_oid, int o_mode,
@@ -1380,6 +1415,24 @@ static int conflict_rename_rename_2to1(struct merge_options *o,
13801415
if (!ret)
13811416
ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
13821417
new_path2);
1418+
/*
1419+
* unpack_trees() actually populates the index for us for
1420+
* "normal" rename/rename(2to1) situtations so that the
1421+
* correct entries are at the higher stages, which would
1422+
* make the call below to update_stages_for_stage_data
1423+
* unnecessary. However, if either of the renames came
1424+
* from a directory rename, then unpack_trees() will not
1425+
* have gotten the right data loaded into the index, so we
1426+
* need to do so now. (While it'd be tempting to move this
1427+
* call to update_stages_for_stage_data() to
1428+
* apply_directory_rename_modifications(), that would break
1429+
* our intermediate calls to would_lose_untracked() since
1430+
* those rely on the current in-memory index. See also the
1431+
* big "NOTE" in update_stages()).
1432+
*/
1433+
if (update_stages_for_stage_data(o, path, ci->dst_entry1))
1434+
ret = -1;
1435+
13831436
free(new_path2);
13841437
free(new_path1);
13851438
}
@@ -1942,6 +1995,111 @@ static char *check_for_directory_rename(struct merge_options *o,
19421995
return new_path;
19431996
}
19441997

1998+
static void apply_directory_rename_modifications(struct merge_options *o,
1999+
struct diff_filepair *pair,
2000+
char *new_path,
2001+
struct rename *re,
2002+
struct tree *tree,
2003+
struct tree *o_tree,
2004+
struct tree *a_tree,
2005+
struct tree *b_tree,
2006+
struct string_list *entries,
2007+
int *clean)
2008+
{
2009+
struct string_list_item *item;
2010+
int stage = (tree == a_tree ? 2 : 3);
2011+
2012+
/*
2013+
* In all cases where we can do directory rename detection,
2014+
* unpack_trees() will have read pair->two->path into the
2015+
* index and the working copy. We need to remove it so that
2016+
* we can instead place it at new_path. It is guaranteed to
2017+
* not be untracked (unpack_trees() would have errored out
2018+
* saying the file would have been overwritten), but it might
2019+
* be dirty, though.
2020+
*/
2021+
remove_file(o, 1, pair->two->path, 0 /* no_wd */);
2022+
2023+
/* Find or create a new re->dst_entry */
2024+
item = string_list_lookup(entries, new_path);
2025+
if (item) {
2026+
/*
2027+
* Since we're renaming on this side of history, and it's
2028+
* due to a directory rename on the other side of history
2029+
* (which we only allow when the directory in question no
2030+
* longer exists on the other side of history), the
2031+
* original entry for re->dst_entry is no longer
2032+
* necessary...
2033+
*/
2034+
re->dst_entry->processed = 1;
2035+
2036+
/*
2037+
* ...because we'll be using this new one.
2038+
*/
2039+
re->dst_entry = item->util;
2040+
} else {
2041+
/*
2042+
* re->dst_entry is for the before-dir-rename path, and we
2043+
* need it to hold information for the after-dir-rename
2044+
* path. Before creating a new entry, we need to mark the
2045+
* old one as unnecessary (...unless it is shared by
2046+
* src_entry, i.e. this didn't use to be a rename, in which
2047+
* case we can just allow the normal processing to happen
2048+
* for it).
2049+
*/
2050+
if (pair->status == 'R')
2051+
re->dst_entry->processed = 1;
2052+
2053+
re->dst_entry = insert_stage_data(new_path,
2054+
o_tree, a_tree, b_tree,
2055+
entries);
2056+
item = string_list_insert(entries, new_path);
2057+
item->util = re->dst_entry;
2058+
}
2059+
2060+
/*
2061+
* Update the stage_data with the information about the path we are
2062+
* moving into place. That slot will be empty and available for us
2063+
* to write to because of the collision checks in
2064+
* handle_path_level_conflicts(). In other words,
2065+
* re->dst_entry->stages[stage].oid will be the null_oid, so it's
2066+
* open for us to write to.
2067+
*
2068+
* It may be tempting to actually update the index at this point as
2069+
* well, using update_stages_for_stage_data(), but as per the big
2070+
* "NOTE" in update_stages(), doing so will modify the current
2071+
* in-memory index which will break calls to would_lose_untracked()
2072+
* that we need to make. Instead, we need to just make sure that
2073+
* the various conflict_rename_*() functions update the index
2074+
* explicitly rather than relying on unpack_trees() to have done it.
2075+
*/
2076+
get_tree_entry(tree->object.oid.hash,
2077+
pair->two->path,
2078+
re->dst_entry->stages[stage].oid.hash,
2079+
&re->dst_entry->stages[stage].mode);
2080+
2081+
/* Update pair status */
2082+
if (pair->status == 'A') {
2083+
/*
2084+
* Recording rename information for this add makes it look
2085+
* like a rename/delete conflict. Make sure we can
2086+
* correctly handle this as an add that was moved to a new
2087+
* directory instead of reporting a rename/delete conflict.
2088+
*/
2089+
re->add_turned_into_rename = 1;
2090+
}
2091+
/*
2092+
* We don't actually look at pair->status again, but it seems
2093+
* pedagogically correct to adjust it.
2094+
*/
2095+
pair->status = 'R';
2096+
2097+
/*
2098+
* Finally, record the new location.
2099+
*/
2100+
pair->two->path = new_path;
2101+
}
2102+
19452103
/*
19462104
* Get information of all renames which occurred in 'pairs', making use of
19472105
* any implicit directory renames inferred from the other side of history.
@@ -1991,6 +2149,7 @@ static struct string_list *get_renames(struct merge_options *o,
19912149

19922150
re = xmalloc(sizeof(*re));
19932151
re->processed = 0;
2152+
re->add_turned_into_rename = 0;
19942153
re->pair = pair;
19952154
item = string_list_lookup(entries, re->pair->one->path);
19962155
if (!item)
@@ -2007,6 +2166,12 @@ static struct string_list *get_renames(struct merge_options *o,
20072166
re->dst_entry = item->util;
20082167
item = string_list_insert(renames, pair->one->path);
20092168
item->util = re;
2169+
if (new_path)
2170+
apply_directory_rename_modifications(o, pair, new_path,
2171+
re, tree, o_tree,
2172+
a_tree, b_tree,
2173+
entries,
2174+
clean_merge);
20102175
}
20112176

20122177
hashmap_iter_init(&collisions, &iter);
@@ -2176,7 +2341,19 @@ static int process_renames(struct merge_options *o,
21762341
dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
21772342
try_merge = 0;
21782343

2179-
if (oid_eq(&src_other.oid, &null_oid)) {
2344+
if (oid_eq(&src_other.oid, &null_oid) &&
2345+
ren1->add_turned_into_rename) {
2346+
setup_rename_conflict_info(RENAME_DIR,
2347+
ren1->pair,
2348+
NULL,
2349+
branch1,
2350+
branch2,
2351+
ren1->dst_entry,
2352+
NULL,
2353+
o,
2354+
NULL,
2355+
NULL);
2356+
} else if (oid_eq(&src_other.oid, &null_oid)) {
21802357
setup_rename_conflict_info(RENAME_DELETE,
21812358
ren1->pair,
21822359
NULL,
@@ -2593,6 +2770,14 @@ static int process_entry(struct merge_options *o,
25932770
o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
25942771
conflict_info);
25952772
break;
2773+
case RENAME_DIR:
2774+
clean_merge = 1;
2775+
if (conflict_rename_dir(o,
2776+
conflict_info->pair1,
2777+
conflict_info->branch1,
2778+
conflict_info->branch2))
2779+
clean_merge = -1;
2780+
break;
25962781
case RENAME_DELETE:
25972782
clean_merge = 0;
25982783
if (conflict_rename_delete(o,

0 commit comments

Comments
 (0)