Projects created from this template can pull in later template changes with the
utils/update_template.py script. It fetches the template, replays only the changes made
since your project last synced, and leaves you a branch to review and merge.
How it works
The script links your project’s history to the template’s by temporarily grafting your first commit onto a known template reference commit, then squash-merges the template branch on top. Git therefore sees a real merge base and produces a diff of only the template’s changes since that reference point, not its entire history.
The reference point is recorded in pyproject.toml, so each run knows where the previous one
left off:
[tool.template]
template_commit = "<last synced template commit>"
template_branch = "main"
On the first run, when this section is absent, the script estimates the reference commit from your project’s creation date.
Prerequisites
- uv and
gitavailable on yourPATH. - Read access to the template repository, over SSH by default.
- A clean working tree on your project’s default branch.
Running the update
From the project root:
uv run python utils/update_template.py
The available options are:
| Option | Description | Default |
|---|---|---|
-u, --template-url | URL of the template repository. | The template’s Git URL |
-b, --branch | Template branch to pull changes from. | [tool.template].template_branch, else main |
-c, --commit | Template commit to treat as the last synced reference. | [tool.template].template_commit, else estimated |
For example, to sync from a specific template branch:
uv run python utils/update_template.py --branch main
What the script does
- Verifies
uvis installed and that no leftover graft commits remain. - Creates an
update-templatebranch from your project’s default branch. - Adds the template as a read-only remote, grafts the histories, and squash-merges the chosen template branch.
- Updates the
[tool.template]reference inpyproject.toml. - Removes the temporary graft commit and template remote.
The graft commit and the template remote are temporary and are
removed even if the update fails partway through, so they are
never pushed.
Resolving conflicts
A clean merge is committed automatically. When the squash-merge hits conflicts, the script stops and asks you to resolve them by hand:
- Resolve the conflicts in your editor and stage the results.
- Commit the resolution.
- Re-run the script. Because you are already on the
update-templatebranch with a clean staging area, it resumes from there.
Once the update-template branch looks right, open a pull request against your default
branch as usual.