Skip to content

Commit f1cb4a2

Browse files
pawamoyyajo
andauthored
fix: Don't mark files without conflict markers as unmerged (#1813)
Co-authored-by: Jairo Llopis <[email protected]>
1 parent 4a3c765 commit f1cb4a2

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-3
lines changed

copier/main.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1105,8 +1105,15 @@ def _apply_update(self) -> None: # noqa: C901
11051105
)
11061106
# Remove rejection witness
11071107
Path(f"{fname}.rej").unlink()
1108-
# Store file name for marking it as unmerged after the loop
1109-
conflicted.append(fname)
1108+
# The 3-way merge might have resolved conflicts automatically,
1109+
# so we need to check if the file contains conflict markers
1110+
# before storing the file name for marking it as unmerged after the loop.
1111+
with open(fname) as conflicts_candidate:
1112+
if any(
1113+
line.rstrip() in {"<<<<<<< before updating", ">>>>>>> after updating"}
1114+
for line in conflicts_candidate
1115+
):
1116+
conflicted.append(fname)
11101117
# Forcefully mark files with conflict markers as unmerged,
11111118
# see SO post: https://stackoverflow.com/questions/77391627/
11121119
# and Git docs: https://git-scm.com/docs/git-update-index#_using_index_info.

tests/test_updatediff.py

+67-1
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,7 @@ def test_conflicted_files_are_marked_unmerged(
12391239
run_update(dst_path=dst, defaults=True, overwrite=True, conflict="inline")
12401240
assert "_commit: v2" in (dst / ".copier-answers.yml").read_text()
12411241

1242-
# Assert that the file still exists, has inline markers,
1242+
# Assert that the file still exists, has inline conflict markers,
12431243
# and is reported as "unmerged" by Git.
12441244
assert (dst / filename).exists()
12451245

@@ -1263,6 +1263,72 @@ def test_conflicted_files_are_marked_unmerged(
12631263
)
12641264

12651265

1266+
def test_3way_merged_files_without_conflicts_are_not_marked_unmerged(
1267+
tmp_path_factory: pytest.TempPathFactory,
1268+
) -> None:
1269+
filename = "readme.md"
1270+
1271+
# Template in v1 has a file with a single line;
1272+
# in v2 it changes that line.
1273+
# Meanwhile, downstream project made the same change.
1274+
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))
1275+
1276+
# First, create the template with an initial file
1277+
build_file_tree(
1278+
{
1279+
(src / filename): "upstream version 1",
1280+
(src / "{{_copier_conf.answers_file}}.jinja"): (
1281+
"{{_copier_answers|to_nice_yaml}}"
1282+
),
1283+
}
1284+
)
1285+
with local.cwd(src):
1286+
git_init("hello template")
1287+
git("tag", "v1")
1288+
1289+
# Generate the project a first time, assert the file exists
1290+
run_copy(str(src), dst, defaults=True, overwrite=True)
1291+
assert (dst / filename).exists()
1292+
assert "_commit: v1" in (dst / ".copier-answers.yml").read_text()
1293+
1294+
# Start versioning the generated project
1295+
with local.cwd(dst):
1296+
git_init("hello project")
1297+
1298+
# After first commit, change the file, commit again
1299+
Path(filename).write_text("upstream version 2")
1300+
git("commit", "-am", "updated file")
1301+
1302+
# Now change the template
1303+
with local.cwd(src):
1304+
# Update the file
1305+
Path(filename).write_text("upstream version 2")
1306+
1307+
# Commit the changes
1308+
git("add", ".", "-A")
1309+
git("commit", "-m", "change line in file")
1310+
git("tag", "v2")
1311+
1312+
# Finally, update the generated project
1313+
run_update(dst_path=dst, defaults=True, overwrite=True, conflict="inline")
1314+
assert "_commit: v2" in (dst / ".copier-answers.yml").read_text()
1315+
1316+
# Assert that the file still exists, does not have inline conflict markers,
1317+
# and is not reported as "unmerged" by Git.
1318+
assert (dst / filename).exists()
1319+
1320+
expected_contents = "upstream version 2"
1321+
assert (dst / filename).read_text() == expected_contents
1322+
assert not (dst / f"{filename}.rej").exists()
1323+
1324+
with local.cwd(dst):
1325+
lines = git("status", "--porcelain=v1").strip().splitlines()
1326+
assert not any(
1327+
line.startswith("UU") and normalize_git_path(line[3:]) == filename
1328+
for line in lines
1329+
)
1330+
1331+
12661332
def test_update_with_new_file_in_template_and_project(
12671333
tmp_path_factory: pytest.TempPathFactory,
12681334
) -> None:

0 commit comments

Comments
 (0)