Skip to content

Commit 9c0743f

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 5b047ac commit 9c0743f

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,
@@ -1121,6 +1144,18 @@ static int merge_file_one(struct merge_options *o,
11211144
return merge_file_1(o, &one, &a, &b, branch1, branch2, mfi);
11221145
}
11231146

1147+
static int conflict_rename_dir(struct merge_options *o,
1148+
struct diff_filepair *pair,
1149+
const char *rename_branch,
1150+
const char *other_branch)
1151+
{
1152+
const struct diff_filespec *dest = pair->two;
1153+
1154+
if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
1155+
return -1;
1156+
return 0;
1157+
}
1158+
11241159
static int handle_change_delete(struct merge_options *o,
11251160
const char *path, const char *old_path,
11261161
const struct object_id *o_oid, int o_mode,
@@ -1390,6 +1425,24 @@ static int conflict_rename_rename_2to1(struct merge_options *o,
13901425
if (!ret)
13911426
ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
13921427
new_path2);
1428+
/*
1429+
* unpack_trees() actually populates the index for us for
1430+
* "normal" rename/rename(2to1) situtations so that the
1431+
* correct entries are at the higher stages, which would
1432+
* make the call below to update_stages_for_stage_data
1433+
* unnecessary. However, if either of the renames came
1434+
* from a directory rename, then unpack_trees() will not
1435+
* have gotten the right data loaded into the index, so we
1436+
* need to do so now. (While it'd be tempting to move this
1437+
* call to update_stages_for_stage_data() to
1438+
* apply_directory_rename_modifications(), that would break
1439+
* our intermediate calls to would_lose_untracked() since
1440+
* those rely on the current in-memory index. See also the
1441+
* big "NOTE" in update_stages()).
1442+
*/
1443+
if (update_stages_for_stage_data(o, path, ci->dst_entry1))
1444+
ret = -1;
1445+
13931446
free(new_path2);
13941447
free(new_path1);
13951448
}
@@ -1952,6 +2005,111 @@ static char *check_for_directory_rename(struct merge_options *o,
19522005
return new_path;
19532006
}
19542007

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

20022160
re = xmalloc(sizeof(*re));
20032161
re->processed = 0;
2162+
re->add_turned_into_rename = 0;
20042163
re->pair = pair;
20052164
item = string_list_lookup(entries, re->pair->one->path);
20062165
if (!item)
@@ -2017,6 +2176,12 @@ static struct string_list *get_renames(struct merge_options *o,
20172176
re->dst_entry = item->util;
20182177
item = string_list_insert(renames, pair->one->path);
20192178
item->util = re;
2179+
if (new_path)
2180+
apply_directory_rename_modifications(o, pair, new_path,
2181+
re, tree, o_tree,
2182+
a_tree, b_tree,
2183+
entries,
2184+
clean_merge);
20202185
}
20212186

20222187
hashmap_iter_init(&collisions, &iter);
@@ -2186,7 +2351,19 @@ static int process_renames(struct merge_options *o,
21862351
dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
21872352
try_merge = 0;
21882353

2189-
if (oid_eq(&src_other.oid, &null_oid)) {
2354+
if (oid_eq(&src_other.oid, &null_oid) &&
2355+
ren1->add_turned_into_rename) {
2356+
setup_rename_conflict_info(RENAME_DIR,
2357+
ren1->pair,
2358+
NULL,
2359+
branch1,
2360+
branch2,
2361+
ren1->dst_entry,
2362+
NULL,
2363+
o,
2364+
NULL,
2365+
NULL);
2366+
} else if (oid_eq(&src_other.oid, &null_oid)) {
21902367
setup_rename_conflict_info(RENAME_DELETE,
21912368
ren1->pair,
21922369
NULL,
@@ -2603,6 +2780,14 @@ static int process_entry(struct merge_options *o,
26032780
o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
26042781
conflict_info);
26052782
break;
2783+
case RENAME_DIR:
2784+
clean_merge = 1;
2785+
if (conflict_rename_dir(o,
2786+
conflict_info->pair1,
2787+
conflict_info->branch1,
2788+
conflict_info->branch2))
2789+
clean_merge = -1;
2790+
break;
26062791
case RENAME_DELETE:
26072792
clean_merge = 0;
26082793
if (conflict_rename_delete(o,

0 commit comments

Comments
 (0)