Update a generated project while preserving its own behavior and configuration. Audit the upstream change range as well as Copier's output: a successful update can still leave relevant changes unapplied.
For projects generated from iloveitaly/python-starter-template or iloveitaly/python-package-template, read references/python-templates.md. Use those examples only for the matching template and project options.
1. Find the project's update recipe first
Confirm the destination repository and generated subdirectory. Inspect its Justfile, included files, and available recipes before choosing an update command:
just --list
just --show update_from_upstream_template
Use the actual recipe name if it differs. Inspect the command, recipe dependencies, and relevant variables for the Copier version, extensions, answers-file path, template ref, exclusions, and task policy. If Just is unavailable, inspect the files directly and resolve tool availability; that does not mean the recipe is absent.
Prefer the suitable local recipe, such as just update_from_upstream_template. Preserve its flags and exclusions. Run it after completing the preflight below. If it fails, diagnose the failure and any partial changes before retrying; do not bypass its exclusions with a bare Copier command.
Use a manual command only when no suitable recipe exists or the user requests one. Start from copier update using the project's configured tool version and required extensions. Select the answers file, ref, exclusions, and task flags from the project configuration and requested scope. Do not impose copier@latest, --vcs-ref=HEAD, or a particular Jinja extension on every project. Copier normally selects the latest release; HEAD explicitly selects the latest commit. See Copier's update documentation.
2. Preserve existing work and capture the baseline
Inspect staged, unstaged, and untracked changes:
git status --short
git diff --stat
git diff --cached --stat
Copier needs a clean destination repository. Keep unrelated work intact. A clean isolated worktree can hold the update while the original work remains in place; it starts from committed content, so reconcile the result with existing edits before claiming the original destination is updated. Record the baseline revision and the location of any isolated result. Do not discard or commit unrelated changes to make Copier proceed.
Use .git/info/exclude only for identified local scratch files that should remain untracked and are unrelated to generated paths. Ignoring a path does not resolve tracked modifications.
Locate the answers file used by the recipe or project configuration; .copier-answers.yml is only the default. If several answers files exist, identify the one for this template. Read its current _src_path, _commit, and relevant question answers directly, and retain the pre-update values for the audit. Git history is supplementary evidence, not the source of the current template revision.
Use _src_path to locate or clone the correct upstream repository separately. Resolve the recorded old ref and intended target ref to full commit SHAs there, fetching missing refs as needed. Keep both the recorded identifiers and resolved SHAs; do not assume _commit is a bare SHA or reconstruct it by truncating a Git description.
Change question answers through Copier's prompts or data options. Do not hand-edit the answers file to simulate a completed update.
3. Inspect execution behavior, then update
Before running the recipe, inspect the old and target template configurations, their included configuration files, and code invoked by extensions, tasks, and migrations. Check the template's minimum Copier version against the tool selected by the project.
--skip-tasksskips ordinary_tasks; it does not skip_migrations.--trustenables features that can execute code, including Jinja extensions and migrations. Use it according to the known template's requirements and the established project workflow.- Task and migration conditions matter: initial-copy setup may be inappropriate during an update. Inspect the commands' effects before manually substituting for skipped tasks.
See task skipping, migrations, and template trust.
Run the selected recipe in the intended destination. Preserve recorded answers unless a change is requested. For new questions, use the project's behavior, documentation, and tests as evidence; ask when the answer materially changes behavior and cannot be determined from the repository.
Afterward, read the answers file again and resolve the actually applied ref to a full SHA. Use this revision for the audit, rather than assuming that a moving HEAD or latest-release selection still matches the preflight target. If execution fails, retain the old baseline and inspect the partial diff and answers before deciding how to recover.
4. Resolve conflicts in generated output
Inspect Git status and diffs, unmerged index entries, inline conflict markers, and rejection files:
git status --short
git diff --name-only --diff-filter=U
rg -n --hidden -g '!.git' '^(<<<<<<< |\|\|\|\|\|\|\| |=======\s*$|>>>>>>> )' .
rg --files --hidden -g '!.git' -g '*.rej'
Review matches; legitimate content can resemble markers. Scan affected ignored paths explicitly if the update touched them. Git status alone is insufficient to establish that inline markers or .rej files are gone.
Resolve each conflict using the upstream change and project intent. Preserve project identity, runtime behavior, dependencies, and custom configuration unless the update requires a specific adaptation. Copier compares generated outputs; raw Jinja tags are not themselves the merge input. If a mapping or conditional block is unclear, render the old and new templates with the corresponding answers in temporary destinations and compare their outputs. Use the appropriate tool version and extensions, skip ordinary tasks for diagnostic renders, and inspect any remaining execution behavior before rendering.
5. Audit and reconcile the upstream range
In the upstream template repository, inspect the complete old-to-applied range, including additions, deletions, and renames:
git log --oneline "$old_sha..$new_sha"
git diff --find-renames --name-status "$old_sha" "$new_sha"
Use the recorded full SHAs for old_sha and new_sha. Inspect detailed diffs for affected source paths. Map changed template paths to their rendered destination paths and any subsequent project or task-driven moves. Review these potential gaps:
- Effective exclusions: combine recipe/CLI exclusions with the template's
_exclude, including conditional patterns. Exclusions match destination paths. An excluded template configuration file still governs generation; exclusion does not disable its settings. - Preserved or deleted paths: distinguish
_skip_if_existsfrom_exclude. Copier respects paths previously deleted from the generated project; do not recreate them without understanding why they were removed. - Skipped tasks: determine which effects are relevant to this update and already satisfied. Reconcile needed file moves or generated artifacts selectively. Do not replay initialization, dependency upgrades, baseline generation, or external repository changes merely because they appear in
_tasks. - Relocated and conditional files: inspect task-driven renames, renamed package directories, and changed question answers. Apply the selected variant to the project's actual destination, preserving its customizations.
- New files and tool changes: check relevant workflows, dotfiles, tool configuration, and lockfile inputs. Regenerate project lockfiles with the project's commands when their inputs changed; do not copy the template's lockfiles wholesale.
Apply improvements supported by this range and relevant to the project. Inspect rendered workflow changes for action versions, tool setup, matrix behavior, permissions, and release steps without treating any particular setting as mandatory. Record significant changes deliberately omitted and why. See exclusions and deleted-path handling.
6. Validate and report completion
Discover the destination's dependency setup, formatting, linting, tests, and build commands from its recipes, configuration, and project instructions. Run the checks appropriate to the changed behavior. Review automated fixes for unintended changes, then repeat the conflict checks and review the complete diff:
git diff --check
git diff --stat
git status --short
Finish with the template source, old and applied revisions, manual reconciliations, significant omissions, checks passed or blocked, and any remaining gaps. Identify where the changes live if an isolated worktree was used. Distinguish local checks from CI or deployment evidence. Follow the user's scope for staging, committing, and publishing.