Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Exclude on Update #1732

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions copier/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,7 @@ def _apply_update(self) -> None: # noqa: C901
# Do a normal update in final destination
with replace(
self,
exclude=self.exclude + self.template.exclude_on_update,
# Files can change due to the historical diff, and those
# changes are not detected in this process, so it's better to
# say nothing than lie.
Expand All @@ -968,6 +969,7 @@ def _apply_update(self) -> None: # noqa: C901
dst_path=new_copy / subproject_subdir,
data=self.answers.combined, # type: ignore[arg-type]
defaults=True,
exclude=self.exclude + self.template.exclude_on_update,
quiet=True,
src_path=self.subproject.template.url, # type: ignore[union-attr]
) as new_worker:
Expand Down
8 changes: 8 additions & 0 deletions copier/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,14 @@ def exclude(self) -> tuple[str, ...]:
DEFAULT_EXCLUDE if Path(self.subdirectory) == Path(".") else [],
)
)

@cached_property
def exclude_on_update(self) -> tuple[str, ...]:
"""Get update exclusions specified in the template.

See [exclude_on_update][].
"""
return tuple(self.config_data.get("exclude_on_update"))

@cached_property
def jinja_extensions(self) -> tuple[str, ...]:
Expand Down
17 changes: 17 additions & 0 deletions docs/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,23 @@ The CLI option can be passed several times to add several patterns.
copier copy --exclude '*' --exclude '!file-i-want' ./template ./destination
```

### `exclude_on_update`

- Format: `List[str]`
- CLI flags: N/A
- Default value: `[]`

Excluded files applied only on update command.

!!! example

```yaml title="copier.yml"
_exclude_on_update:
- "pre-commit.sample"
- "myfile.example"
- "*.dummy"
```

### `force`

- Format: `bool`
Expand Down
3 changes: 3 additions & 0 deletions tests/demo_exclude_on_update/copier.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
_exclude_on_update:
- "do_not_update.sample"

Empty file.
61 changes: 61 additions & 0 deletions tests/test_updatediff.py
Original file line number Diff line number Diff line change
Expand Up @@ -1290,3 +1290,64 @@ def test_update_with_new_file_in_template_and_project_via_migration(
>>>>>>> after updating
"""
)



def test_exclude_on_update(tmp_path_factory: pytest.TempPathFactory) -> 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"))
filename = "README.md"

# First, create the template with an initial file
build_file_tree(
{
(src / filename): "upstream version 1",
(src / "copier.yaml"): f"_exclude_on_update: ['{filename}']",
(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, and was left untouched
assert (dst / filename).exists()

expected_contents = dedent(
"""\
upstream version 1 + downstream
"""
)
assert (dst / filename).read_text().splitlines() == expected_contents.splitlines()
assert not (dst / f"{filename}.rej").exists()
Loading