| name | backport-commit |
| description | Backport changes from a GitHub commit in a project generated by the python-package-copier Copier template into the template source files and tests. Use when given a GitHub commit URL from a generated repo and asked to incorporate those changes into the template. Covers fetching the commit diff, mapping generated files back to template sources, converting literal values to Jinja variables, updating template files, and generating or updating tests. |
Backport Commit
Backport a commit from a generated project into the Copier template source.
Workflow
Step 1: Fetch the commit diff
Fetch the raw diff by appending .diff to the GitHub commit URL:
# Given: https://github.com/user/repo/commit/abc123
# Fetch: https://github.com/user/repo/commit/abc123.diff
Use fetch_webpage with the .diff URL. Parse the diff to identify:
- Which files were changed (added/modified/deleted)
- The actual content changes (hunks)
- The project's package name (from directory structure, e.g.,
src/actual_name/)
Skip files outside the template's scope (application-specific code, data files, CI artifacts).
Step 2: Map generated files → template files
Read references/file-mapping.md for the complete mapping rules.
For each changed file in the diff:
- Prepend
template/ to the path
- Add
.jinja suffix if the template source has one
- Replace the literal package name directory with
{{ package_name }}
- Handle conditional directories (
examples/ → {% if include_examples %}examples{% endif %}/)
Example:
src/my_pkg/hello.py → template/src/{{ package_name }}/hello.py.jinja
pyproject.toml → template/pyproject.toml.jinja
.github/workflows/tests.yml → template/.github/{% if include_actions %}workflows{% endif %}/tests.yml.jinja
If a file doesn't map to an existing template file, it may need to be created as a new template file.
Step 3: Analyze changes for template applicability
Read references/jinja-patterns.md for conditional logic guidance.
For each change, determine:
- Universal or conditional? — Does the change apply to all generated projects, or only when a specific option is enabled?
- Variable substitution needed? — Are there literal values (package name, author, GitHub username) that should become
{{ variable }} placeholders?
- Existing conditional context? — Is the change inside an existing
{% if %} block that must be preserved?
Step 4: Modify template files
Apply changes to the template source files:
- Read the current template file
- Locate the corresponding section (match surrounding context, not line numbers)
- Apply the diff changes, converting literals back to template variables
- Wrap in
{% if %} blocks if the change is conditional (per Step 3)
- Preserve existing Jinja delimiters, whitespace style, and indentation
Reverse substitution checklist (see file-mapping.md for full table):
- Actual package name →
{{ package_name }}
- Actual project name →
{{ project_name }}
- Actual slug →
{{ project_slug }}
- Actual GitHub username →
{{ github_username }}
Step 5: Update tests
Read references/test-patterns.md for test writing patterns.
For each template change, add a test that verifies:
- The expected content appears in the generated file
- Template variables are substituted (no raw
{{ }} in output)
- If conditional: parametrize with both True/False to verify inclusion/exclusion
Choose the test file based on the change type (see test-patterns.md for the mapping).
Step 6: Verify
just test-fast
just fix
Both commands must pass. Fix any failures before considering the backport complete.
Appropriate for: Templates, boilerplate code, document templates, images, icons, fonts, or any files meant to be copied or used in the final output.
Any unneeded directories can be deleted. Not every skill requires all three types of resources.