Merge branch 'en/ort-dir-rename-and-symlink-fix'

Merging a branch with directory renames into a branch that changes
the directory to a symlink was mishandled by the ort merge
strategy, which has been corrected.

* en/ort-dir-rename-and-symlink-fix:
  merge-ort: fix bug with dir rename vs change dir to symlink
This commit is contained in:
Taylor Blau 2022-10-30 21:04:43 -04:00
commit 969230b64f
2 changed files with 90 additions and 2 deletions

View File

@ -2619,8 +2619,40 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
}
assert(ci->filemask == 2 || ci->filemask == 4);
assert(ci->dirmask == 0);
strmap_remove(&opt->priv->paths, old_path, 0);
assert(ci->dirmask == 0 || ci->dirmask == 1);
if (ci->dirmask == 0)
strmap_remove(&opt->priv->paths, old_path, 0);
else {
/*
* This file exists on one side, but we still had a directory
* at the old location that we can't remove until after
* processing all paths below it. So, make a copy of ci in
* new_ci and only put the file information into it.
*/
new_ci = mem_pool_calloc(&opt->priv->pool, 1, sizeof(*new_ci));
memcpy(new_ci, ci, sizeof(*ci));
assert(!new_ci->match_mask);
new_ci->dirmask = 0;
new_ci->stages[1].mode = 0;
oidcpy(&new_ci->stages[1].oid, null_oid());
/*
* Now that we have the file information in new_ci, make sure
* ci only has the directory information.
*/
ci->filemask = 0;
ci->merged.clean = 1;
for (i = MERGE_BASE; i <= MERGE_SIDE2; i++) {
if (ci->dirmask & (1 << i))
continue;
/* zero out any entries related to files */
ci->stages[i].mode = 0;
oidcpy(&ci->stages[i].oid, null_oid());
}
// Now we want to focus on new_ci, so reassign ci to it
ci = new_ci;
}
branch_with_new_path = (ci->filemask == 2) ? opt->branch1 : opt->branch2;
branch_with_dir_rename = (ci->filemask == 2) ? opt->branch2 : opt->branch1;

View File

@ -5304,6 +5304,62 @@ test_expect_merge_algorithm failure success '12l (A into B): Rename into each ot
)
'
# Testcase 12m, Directory rename, plus change of parent dir to symlink
# Commit O: dir/subdir/file
# Commit A: renamed-dir/subdir/file
# Commit B: dir/subdir
# In words:
# A: dir/subdir/ -> renamed-dir/subdir
# B: delete dir/subdir/file, add dir/subdir as symlink
#
# Expected: CONFLICT (rename/delete): renamed-dir/subdir/file,
# CONFLICT (file location): renamed-dir/subdir vs. dir/subdir
# CONFLICT (directory/file): renamed-dir/subdir symlink has
# renamed-dir/subdir in the way
test_setup_12m () {
git init 12m &&
(
cd 12m &&
mkdir -p dir/subdir &&
echo 1 >dir/subdir/file &&
git add . &&
git commit -m "O" &&
git branch O &&
git branch A &&
git branch B &&
git switch A &&
git mv dir/ renamed-dir/ &&
git add . &&
git commit -m "A" &&
git switch B &&
git rm dir/subdir/file &&
mkdir dir &&
ln -s /dev/null dir/subdir &&
git add . &&
git commit -m "B"
)
}
test_expect_merge_algorithm failure success '12m: Change parent of renamed-dir to symlink on other side' '
test_setup_12m &&
(
cd 12m &&
git checkout -q A^0 &&
test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 &&
test_stdout_line_count = 3 git ls-files -s &&
test_stdout_line_count = 2 ls -1 renamed-dir &&
test_path_is_missing dir
)
'
###########################################################################
# SECTION 13: Checking informational and conflict messages
#