Actual Situation
When adopting a copier template in an existing project that wasn't originally created with that template, users are forced into a binary choice with no good middle ground:
copier copy --overwrite destroys project-specific content:
pyproject.toml dependencies get wiped
src/pkg/__init__.py exports get replaced
- Custom CLI code, test fixtures, etc. are overwritten
copier copy --skip / _skip_if_exists preserves project files but skips all template improvements:
- Project doesn't get updated ruff rules, poe tasks, coverage config
- Project doesn't get updated pre-commit hooks
- No way to "merge" template config sections with existing config
copier update is designed for projects already using the template. It uses 3-way merge, but requires a valid _commit pointing to a template version the project was created from. For adoption, there's no "old version" to diff against.
The current documented workaround (from #955 / PR #981) is to copier copy --overwrite and then use git difftool to manually cherry-pick changes back. This is tedious, error-prone, and doesn't scale when adopting a template across many existing repositories.
Root cause
Copier's merge logic only works in update mode with a known baseline. For first-time adoption, there's no baseline to diff against, so it's binary: overwrite or skip. This gap means there is no practical path from "existing project" to "template-managed project" without significant manual effort.
Related issues
Desired Situation
A first-class copier adopt command (or equivalent workflow) that lets an existing project adopt a template with intelligent handling of conflicts:
- Add new files from the template that don't exist in the project (docs, CI configs, etc.) — no conflict
- Merge overlapping config files (
pyproject.toml tool sections, .pre-commit-config.yaml, .gitignore, etc.) — using conflict markers or a 3-way merge where the "base" is an empty file or a synthetic baseline
- Preserve project-specific content (dependencies, custom code, package exports) — never silently overwrite
- Establish a baseline for future
copier update — after adoption, the project should be in the same state as if it had been created from the template, enabling the normal update workflow going forward
The result: a single command that takes a project from "not template-managed" to "template-managed and updatable" with explicit, reviewable conflict resolution rather than silent data loss.
Proposed solution
A new copier adopt subcommand that works roughly like this:
copier adopt https://github.com/org/template.git ./my-existing-project
MVP behavior:
- Render the template into a temporary directory (answering questions as normal)
- For each rendered file:
- If the file doesn't exist in the project → copy it in
- If the file exists and is identical → skip (no-op)
- If the file exists and differs → write the file with git-style conflict markers (treating the existing file as "ours" and the template output as "theirs"), or use a 3-way merge with an empty base
- Write
.copier-answers.yml with the current template version and answers
- Commit (or leave uncommitted for user review)
After this, the project has a valid .copier-answers.yml and _commit, so future copier update calls work normally with proper 3-way merge.
Key design consideration: The synthetic baseline. Since there's no "old template version" for a project that was never created from the template, the adopt command could use an empty tree or the template's initial version as the merge base. This is similar to how git merge --allow-unrelated-histories handles merging two trees with no common ancestor.
Alternative (lighter-weight): Rather than a new subcommand, a --adopt flag on copier copy that triggers the conflict-marker behavior instead of the overwrite-or-skip binary. This would also write valid .copier-answers.yml for future updates.
Actual Situation
When adopting a copier template in an existing project that wasn't originally created with that template, users are forced into a binary choice with no good middle ground:
copier copy --overwritedestroys project-specific content:pyproject.tomldependencies get wipedsrc/pkg/__init__.pyexports get replacedcopier copy --skip/_skip_if_existspreserves project files but skips all template improvements:copier updateis designed for projects already using the template. It uses 3-way merge, but requires a valid_commitpointing to a template version the project was created from. For adoption, there's no "old version" to diff against.The current documented workaround (from #955 / PR #981) is to
copier copy --overwriteand then usegit difftoolto manually cherry-pick changes back. This is tedious, error-prone, and doesn't scale when adopting a template across many existing repositories.Root cause
Copier's merge logic only works in
updatemode with a known baseline. For first-time adoption, there's no baseline to diff against, so it's binary: overwrite or skip. This gap means there is no practical path from "existing project" to "template-managed project" without significant manual effort.Related issues
copier copyon existing files (open)--merge-filesfor multi-template scenarios (open)Desired Situation
A first-class
copier adoptcommand (or equivalent workflow) that lets an existing project adopt a template with intelligent handling of conflicts:pyproject.tomltool sections,.pre-commit-config.yaml,.gitignore, etc.) — using conflict markers or a 3-way merge where the "base" is an empty file or a synthetic baselinecopier update— after adoption, the project should be in the same state as if it had been created from the template, enabling the normal update workflow going forwardThe result: a single command that takes a project from "not template-managed" to "template-managed and updatable" with explicit, reviewable conflict resolution rather than silent data loss.
Proposed solution
A new
copier adoptsubcommand that works roughly like this:MVP behavior:
.copier-answers.ymlwith the current template version and answersAfter this, the project has a valid
.copier-answers.ymland_commit, so futurecopier updatecalls work normally with proper 3-way merge.Key design consideration: The synthetic baseline. Since there's no "old template version" for a project that was never created from the template, the adopt command could use an empty tree or the template's initial version as the merge base. This is similar to how
git merge --allow-unrelated-historieshandles merging two trees with no common ancestor.Alternative (lighter-weight): Rather than a new subcommand, a
--adoptflag oncopier copythat triggers the conflict-marker behavior instead of the overwrite-or-skip binary. This would also write valid.copier-answers.ymlfor future updates.