Skip to main content

makepatch

A Git-based patch tool for Python packages that integrates with Hatchling.

Inspired by paperweight and pnpm patch.

Mode Approach Output
Source patch (makepatch src) Fetch an upstream Git repository at a pinned ref and patch it sdist/wheel of a new package (fork)
Package patch (makepatch pkg) Patch installed packages in site-packages patched virtual environment

There are two kinds of patches:

  • Source patch: created with git diff and applied with git apply. One patch per file.
  • Feature patch: created with git format-patch -p --minimal --zero-commit --no-numbered and applied with git am --3way. One patch per commit; available in source patch mode only.

All patches are applied statically; there is no runtime monkey-patching.

Requirements

  • Python ≥ 3.13
  • Git ≥ 2.32

Source patch mode

A fork repository needs only pyproject.toml and patches/.

[build-system]
requires = ["hatchling", "makepatch"]
build-backend = "hatchling.build"

[project]            # Define the fork's metadata yourself.
name = "requests-fork"
version = "2.32.3.post1"
dependencies = ["urllib3>=1.21.1,<3", "idna>=2.5,<4", "charset_normalizer>=2,<4", "certifi>=2017.4.17"]

[tool.makepatch.source]
upstream = "https://github.com/psf/requests.git"
ref = "v2.32.3"                           # a 40-digit commit SHA is recommended; tags and branches work too
include = { "src/requests" = "requests" } # upstream path → wheel path
exclude = ["**/*.pyi"]                    # excluded from sdist/wheel (relative to include paths)
work-exclude = ["docs/", "tests/"]        # excluded from work/ and the build tree (relative to the upstream root)
# work-dir = "work"                       # default
# patches-dir = "patches"                 # default

[tool.hatch.build.hooks.makepatch]        # enables the build hook (settings live in the table above)

[tool.hatch.build.targets.wheel]
bypass-selection = true

Add work/ and .makepatch/ to .gitignore so that they are excluded from the sdist.

Excluding files

Both options use gitignore syntax (including ! negation), but differ in base path and effect.

Option Base path Effect
exclude each include path (e.g. src/requests) Excluded from the sdist and wheel. Still present in work/.
work-exclude upstream root Not checked out into work/ or the build tree, via git sparse-checkout. Meant to save time and disk space.
  • If a patch touches a file excluded by work-exclude, makepatch src apply and the build stop with an error.
  • After changing work-exclude, run makepatch src apply again.

Workflow

makepatch src apply     # fetch upstream → create work/ → apply source patches → git am feature patches
# edit files in work/
makepatch src fixup     # fold working tree changes into the source patch commit
# or make regular commits in work/ → feature patches
makepatch src rebuild   # regenerate patches/sources/** and patches/features/*.patch
makepatch src status

The work/ repository is laid out as follows:

<upstream ref>                 tag makepatch/base
makepatch: source patches      tag makepatch/sources   ← patches/sources/<path>.patch
<feature commits> ...                                  ← patches/features/NNNN-*.patch

If a feature patch conflicts, the git am session is left in place. Resolve the conflict in work/, run git am --continue, then run makepatch src rebuild.

Building

uv build
  • The build hook does not use work/; it applies the patches afresh in .makepatch/build/tree. Uncommitted work therefore never leaks into the output.
  • The sdist contains the patched sources (_makepatch/tree/). Building a wheel from the sdist needs neither git nor network access.
  • Set MAKEPATCH_OFFLINE=1 to use only the .makepatch/upstream.git cache.
  • Upstream's own build steps (C extensions, etc.) are not run. Pure Python sources are the target.

Package patch mode

Install makepatch as a development dependency of the project.

uv add --dev makepatch
uv run makepatch pkg edit requests     # create an editable copy in .makepatch/edit/requests@2.32.3/
# edit files in the copy
uv run makepatch pkg commit requests   # save patches/packages/requests@2.32.3.patch and apply it
uv run makepatch pkg apply             # apply every patch (--check: only check that they apply)
uv run makepatch pkg status
uv run makepatch pkg revert requests   # restore the original files
  • Patch files are named <normalized name>@<version>.patch, with paths relative to site-packages. An error is raised if the installed version differs.
  • Deleting a patch file and running pkg apply restores that package to its original state.
  • The patch directory can be changed with [tool.makepatch.packages] patches-dir.
  • --python <interpreter> targets a different environment.
  • Editable installs are not supported; edit their sources directly.

Compatibility with uv

  • Cache protection: on Linux, uv installs files from its cache as hardlinks by default (--link-mode selects clone, copy or symlink instead). makepatch never modifies files in place: it runs git apply on the affected files in a temporary directory and swaps them in with os.replace. Only the replaced paths get new inodes, so the uv cache stays untouched. The same holds in symlink mode.
  • Records: the hashes in RECORD are updated, and the applied state (makepatch.json, makepatch.patch) is written into the dist-info and registered in RECORD, so it is removed together with the package.
  • Recovery after reinstall: uv sync does not inspect the contents of installed files, so patches persist. If a reinstall or version change drops a patch, the makepatch-startup.pth installed by makepatch detects this at interpreter start-up and re-applies it.
    • Normally it only compares the hashes of the patch files and markers.
    • Only on a mismatch does it take the environment lock and do the same work as pkg apply.
    • Disable it with MAKEPATCH_DISABLE_STARTUP=1.
    • It is skipped when the interpreter runs with -I (isolated), as when uv queries an interpreter.

Development

uv sync
uv run pytest

Release files for makepatch 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for makepatch 0.3.0
File Size Uploaded
makepatch-0.3.0.tar.gz 52.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for makepatch 0.3.0
File Interpreter ABI Platform
makepatch-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 79.9 kB

Release files / makepatch-0.3.0.tar.gz

Download URL makepatch-0.3.0.tar.gz
Size 52.1 kB
Tags Source
SHA-256 checksum
How to use checksums
0b33ae7a27872eb92a89c9118c97fca9cfc204f55c7d9194b2d3886398364f62
BLAKE2b-256 checksum
How to use checksums
fc931f80e8c8bab048632274b44d77fed5940d764d36d1d5dec63694736a1bec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"45","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / makepatch-0.3.0-py3-none-any.whl

Download URL makepatch-0.3.0-py3-none-any.whl
Size 27.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c68b6799a24331be8573991cc6be36c8856fa6ae8e7088ad8ba50206d34a82dc
BLAKE2b-256 checksum
How to use checksums
f87062003d79eaa2529d6e6768f9a6064f05aa63a0dcc81bca40d562c0e44e29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"45","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page