Patch third-party libraries without forking them.
git-third-party is a small zero-dependency utility that is an alternative to
git-submodules and
git-subtree.
It stores only the delta (your changes) inside your tree, not the full vendored
source.
It is not oriented for highly concurrent modification of third-party tools by multiple users.
With Nix (flakes):
nix profile install github:kp2pml30/git-third-partyOr just drop the single script on your PATH — it needs only python3 and
git:
curl -o ~/.local/bin/git-third-party https://raw.githubusercontent.com/kp2pml30/git-third-party/main/git-third-party
chmod +x ~/.local/bin/git-third-party# add a third-party repository
git-third-party add third-party/RustPython https://github.com/RustPython/RustPython.git a13b99642b0bc13ca89d01768a7ddbec18fe8219
# ^ git-third-party add <relative path> <repository url> <commit hash>
# now modify it as a regular repository, and commit your changes
# save patches to push to your origin
git-third-party save third-party/RustPython
# ^ git-third-party save <relative path>
# reapply patches (e.g. on a fresh checkout)
git-third-party update third-party/RustPython
# ^ git-third-party update <relative path>Without installing, you can run it straight from the flake:
nix run github:kp2pml30/git-third-party -- add third-party/RustPython <url> <commit>It stores a manifest under /.git-third-party/manifest.json that describes all
third-party repositories and their patches. save updates the patches, update
reapplies them. Older versions called that file config.json; it is still read,
and renamed the next time the tool writes.
Patch files are content addressed: the file name is the SHA-3 digest of the
patch bytes, in Crockford Base32 (case-insensitive and safe in paths). The order
of the series lives in manifest.json, so editing history in the middle of a
series renames nothing — only the patches that actually changed show up in a
diff. Repositories saved by an older version, which numbered patches 1, 2,
3, …, are migrated in place by the next save or update.
The .git-third-party directory must be tracked by git, while the third-party
working trees themselves should not be. The tool keeps a .gitattributes and a
.gitignore of its own in there: patch bytes are what their names are derived
from, so the enclosing repository must not normalize their line endings. Commit
those alongside the manifest.
Each managed checkout gets its push url disabled (origin fetches from
upstream, but pushing fails), so local patch commits can not accidentally be
pushed to the third-party project.
Best effort is made to keep patches deterministic and to strip metadata from them, including:
- git version
- commit hash (which depends on the committer)
- …
See docs/contributing/.
GPL-3.0 (C) 2024-2026 Kira Prokopenko