Repolish¶
A hybrid templating and diff/patch system for maintaining repository consistency while preserving local customizations.
Why this exists¶
Teams often need to enforce repository-level conventions — CI config, build tools, metadata, shared docs — while letting individual projects keep local customizations. The naive approaches are painful:
- Copying templates into many repos means drift over time and manual syncs.
- Running destructive templating can overwrite local changes developers rely on.
Repolish solves this by combining templating (to generate canonical files) with a set of careful, reversible operations that preserve useful local content. Instead of blindly replacing files, Repolish can:
- Fill placeholders from provider-supplied context.
- Apply anchor-driven replacements to keep developer-customized sections intact.
- Track provider-specified deletions and record provenance so reviewers can see why a path was requested for deletion.
Key concepts¶
Providers supply templates and context. Each provider lives in a template
directory and may include a repolish.py module that exports
create_context(), create_anchors(), and/or create_delete_files() helpers.
Anchors are markers placed in templates (and optionally in project files)
that mark blocks or regex lines to preserve. Block anchors (repolish-start /
repolish-end) preserve entire sections; regex anchors (repolish-regex) keep
individual lines matching a pattern.
File mappings let providers conditionally select which template file to copy
to a given destination, allowing clean filenames without {% if %} clutter.
Create-only files are copied once on first run and skipped on subsequent runs, making them ideal for initial scaffolding that users own after creation.
Delete semantics let providers request file removals. A ! prefix negates
(keeps) a path. A delete_history provenance record is maintained so reviewers
can see why a path was flagged.
How it works¶
- Load providers configured in
repolish.yaml. - Merge provider contexts; config-level context overrides provider values.
- Merge anchors from providers and config.
- Stage all provider template directories into a single merged template under
.repolish/setup-input. - Preprocess staged templates by applying anchor-driven replacements using local project files.
- Render the merged template into
.repolish/setup-output. - In
--checkmode: compare generated files to project files and report diffs, missing files, or paths that providers wanted deleted but are still present. - In apply mode: copy generated files into the project and apply deletions.
Why it is useful¶
- Safe consistency: teams get centralized templates without forcing destructive rollouts.
- Clear explainability: the
delete_historyprovenance makes it easy to review why a file was targeted for deletion or kept. - CI-friendly:
--checkdetects drift; structured logs and diffs make it straightforward to require PRs to run repolish before merging.
Cookiecutter deprecation & migration (planned for v1)
Cookiecutter-based final rendering will be removed in the v1 release. You
can migrate now by enabling the opt-in native Jinja renderer
(no_cookiecutter: true) and validating your templates with
repolish preview.
Why migrate
- Jinja avoids Cookiecutter CLI/option quirks (arrays treated as options),
provides stricter validation (
StrictUndefined) and clearer error messages, and enables new features such astuple-valuedfile_mappings(per-file extra context).
Quick migration steps
- Enable
no_cookiecutter: trueand runrepolish apply. - Update templates to prefer top-level context access
(
{{ my_provider.* }}) — thecookiecutternamespace remains available during migration. - Follow the examples in the Templates guide.
See also: Loader context for file_mappings
examples.