Skip to content

Commit

Permalink
feat: Mark files with conflict markers as unmerged
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Oct 31, 2023
1 parent ef8aca2 commit 1be47a2
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
18 changes: 18 additions & 0 deletions copier/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@ def _apply_update(self):
apply_cmd = apply_cmd["--exclude", skip_pattern]
(apply_cmd << diff)(retcode=None)
if self.conflict == "inline":
conflicted = []
status = git("status", "--porcelain").strip().splitlines()
for line in status:
# Filter merge rejections (part 1/2)
Expand Down Expand Up @@ -933,6 +934,23 @@ def _apply_update(self):
)
# Remove rejection witness
Path(f"{fname}.rej").unlink()
# Store file name for marking it as unmerged after the loop
conflicted.append(fname)
# Forcefully mark files with conflict markers as unmerged,
# see SO post: https://stackoverflow.com/questions/77391627/
# and Git docs: https://git-scm.com/docs/git-update-index#_using_index_info.
# For each file with conflict markers, we update the index to add
# higher order versions of their paths, without entries for resolved contents.
input_lines = []
for line in (
git("ls-files", "--stage", *conflicted).strip().splitlines()
):
perms_sha_mode, path = line.split("\t")
perms, sha, _ = perms_sha_mode.split()
input_lines.append(f"0 {'0' * 40}\t{path}")
for mode in (1, 2, 3):
input_lines.append(f"{perms} {sha} {mode}\t{path}")
(git["update-index", "--index-info"] << "\n".join(input_lines))()
# Trigger recursive removal of deleted files in last template version
_remove_old_files(subproject_top, compared)

Expand Down
84 changes: 84 additions & 0 deletions tests/test_updatediff.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
SUFFIX_TMPL,
Spawn,
build_file_tree,
git_init,
)


Expand Down Expand Up @@ -983,3 +984,86 @@ def function_two():
print("Previous line lied.")
"""
)


@pytest.mark.parametrize(
"filename",
[
"README.md",
"spa ces",
'qu`o"tes',
"m4â4ñ4a",
],
)
def test_conflicted_files_are_marked_unmerged(
tmp_path_factory: pytest.TempPathFactory,
filename: str,
) -> None:
# Template in v1 has a file with a single line;
# in v2 it changes that line.
# Meanwhile, downstream project appended contents to the first line.
src, dst = map(tmp_path_factory.mktemp, ("src", "dst"))

# First, create the template with an initial file
build_file_tree(
{
(src / filename): "upstream version 1",
(src / "{{_copier_conf.answers_file}}.jinja"): (
"{{_copier_answers|to_nice_yaml}}"
),
}
)
with local.cwd(src):
git_init("hello template")
git("tag", "v1")

# Generate the project a first time, assert the file exists
run_copy(str(src), dst, defaults=True, overwrite=True)
assert (dst / filename).exists()
assert "_commit: v1" in (dst / ".copier-answers.yml").read_text()

# Start versioning the generated project
with local.cwd(dst):
git_init("hello project")

# After first commit, change the file, commit again
Path(filename).write_text("upstream version 1 + downstream")
git("commit", "-am", "updated file")

# Now change the template
with local.cwd(src):
# Update the file
Path(filename).write_text("upstream version 2")

# Commit the changes
git("add", ".", "-A")
git("commit", "-m", "change line in file")
git("tag", "v2")

# Finally, update the generated project
run_update(dst_path=dst, defaults=True, overwrite=True, conflict="inline")
assert "_commit: v2" in (dst / ".copier-answers.yml").read_text()

# Assert that the file still exists, has inline markers,
# and is reported as "unmerged" by Git.
assert (dst / filename).exists()

expected_contents = dedent(
"""\
<<<<<<< before updating
upstream version 1 + downstream
=======
upstream version 2
>>>>>>> after updating
"""
)
assert (dst / filename).read_text().splitlines() == expected_contents.splitlines()
assert not (dst / f"{filename}.rej").exists()

with local.cwd(dst):
print(dst)
lines = git("ls-files", "--stage", filename).strip().splitlines()
assert len(lines) == 3
assert " 1\t" in lines[0]
assert " 2\t" in lines[1]
assert " 3\t" in lines[2]

0 comments on commit 1be47a2

Please sign in to comment.