Skip to content

feat(update): preserve developer-owned regions across updates (#2184)#2762

Open
DrKat0m wants to merge 3 commits into
copier-org:masterfrom
DrKat0m:feat/preserve-user-sections-2184
Open

feat(update): preserve developer-owned regions across updates (#2184)#2762
DrKat0m wants to merge 3 commits into
copier-org:masterfrom
DrKat0m:feat/preserve-user-sections-2184

Conversation

@DrKat0m

@DrKat0m DrKat0m commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Templates often ship files with dummy content or TODO markers that the developer replaces right after generation. Previously copier update treated those regions like any other template-owned content, so a change to the placeholder in the template could clobber the developer's work or raise a needless merge conflict.

Template authors can now wrap a developer-owned region in literal marker comments that survive rendering:

# copier:preserve:start <optional-id>
...developer-owned content...
# copier:preserve:end <optional-id>

During an update the content between the markers is captured from the current project and injected into every intermediate render (old render, new render, and the freshly rendered destination) so all three merge sides agree on the region. The merge therefore keeps the developer's content conflict-free, while the marker lines and surrounding content keep updating from the template as usual.

  • Added copier/_preserve.py with the marker parsing, capture and restore primitives.
  • Wired capture/restore into Worker._apply_update, scoping the scan to template-managed files discovered in the old render.
  • Documented the feature in docs/updating.md.
  • Added unit and integration tests in tests/test_preserve.py.

Closes #2184

DrKat0m and others added 2 commits July 15, 2026 14:43
…-org#2184)

Templates often ship files with dummy content or TODO markers that the
developer replaces right after generation. Previously `copier update`
treated those regions like any other template-owned content, so a change
to the placeholder in the template could clobber the developer's work or
raise a needless merge conflict.

Template authors can now wrap a developer-owned region in literal marker
comments that survive rendering:

    # copier:preserve:start <optional-id>
    ...developer-owned content...
    # copier:preserve:end <optional-id>

During an update the content between the markers is captured from the
current project and injected into every intermediate render (old render,
new render, and the freshly rendered destination) so all three merge
sides agree on the region. The merge therefore keeps the developer's
content conflict-free, while the marker lines and surrounding content
keep updating from the template as usual.

- Added `copier/_preserve.py` with the marker parsing, capture and restore
  primitives.
- Wired capture/restore into `Worker._apply_update`, scoping the scan to
  template-managed files discovered in the old render.
- Documented the feature in `docs/updating.md`.
- Added unit and integration tests in `tests/test_preserve.py`.
@DrKat0m

DrKat0m commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Hi @sisp, whenever you have a chance, could you please take a look at this PR? I'd really appreciate any feedback on the approach (especially the choice to keep the region conflict free by aligning all three merge sides rather than post processing conflict output) and on the marker syntax. Happy to iterate on anything. Thank you!

`test_tools.py::test_types` runs `mypy .` over the whole tree and flagged
three `_apply_regions` calls in `tests/test_preserve.py`: the local dicts
were inferred as `dict[str, str]`, which is not assignable to the expected
`dict[_RegionKey, str]` because `dict` is invariant in its key type.

Annotate the three test dicts with `_RegionKey` so they match the
parameter type.
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.42857% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.16%. Comparing base (02e1574) to head (9170be0).

Files with missing lines Patch % Lines
copier/_preserve.py 92.52% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2762      +/-   ##
==========================================
- Coverage   97.29%   97.16%   -0.13%     
==========================================
  Files          60       62       +2     
  Lines        7584     7808     +224     
==========================================
+ Hits         7379     7587     +208     
- Misses        205      221      +16     
Flag Coverage Δ
unittests 97.16% <96.42%> (-0.13%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for initial content

1 participant