Language: English | Simplified Chinese
Download GitHub pull requests, GitLab merge requests, or individual commits as patches, one file per commit or one combined diff.
patchsplit is a command-line tool written in Rust. It fetches patches directly
from GitHub or GitLab without cloning the repository, making it useful for
reviewing, sharing, and applying changes locally.
- Per-commit patches: Keep the original patch content, commit messages, and authorship, with numbered filenames in commit order.
- Single commit: Use
--commit <hash>to download one commit's.patchwith either a short or full hash. - GitLab support: Pass
--gitlabto fetch merge requests and commits from gitlab.com, including projects nested under subgroups. - Combined diff: Use
--squashto export the request's net changes as a single patch. - Predictable output: Choose an output directory; existing files are only
overwritten when you pass
--force. - Localized CLI: English and built-in Simplified Chinese messages.
Installation | Usage | Localization | Development | Contributing | License
patchsplit calls the system curl command to download patches. Make sure
curl is available in your PATH; the Debian/Ubuntu package declares it as a
dependency. Rust is only needed when building from source.
Add the project PPA and refresh the package index:
sudo add-apt-repository ppa:zitzhen/patchsplit
sudo apt updateInstall patchsplit:
sudo apt install patchsplitDownload the .deb package from GitHub Releases,
then install it with the command below. Replace <version> with the version in
the downloaded filename.
sudo apt install ./patchsplit_<version>_amd64.debDownload the main .rpm package from GitHub Releases.
Use its exact filename in place of <package-file>:
sudo dnf install ./<package-file>.rpmChoose patchsplit-<version>-<release>.*.x86_64.rpm, excluding any package with
debuginfo or debugsource in its name.
The community-maintained patchsplit-bin
package is provided by lingbopro.
Install it with either AUR helper:
paru -S patchsplit-bin
# Or:
yay -S patchsplit-binDownload the archive for your platform from GitHub Releases. The release workflow builds x86_64 binaries for Linux, macOS, and Windows.
Extract the Linux archive:
tar -xzf patchsplit-linux-x86_64.tar.gzOr extract the macOS archive:
tar -xzf patchsplit-macos-x86_64.tar.gzThen install the extracted binary:
sudo install -m 755 patchsplit /usr/local/bin/patchsplit
patchsplit --versionIf macOS blocks a binary you have verified and trust, remove its quarantine attribute:
xattr -d com.apple.quarantine /usr/local/bin/patchsplitDownload patchsplit-windows-x86_64.zip from
GitHub Releases, then extract
and run it in PowerShell:
Expand-Archive .\patchsplit-windows-x86_64.zip -DestinationPath .\patchsplit
.\patchsplit\patchsplit.exe --versionTo use it globally, add the extracted patchsplit directory to your user Path
environment variable. The CLI needs curl.exe available in Path.
With Rust, Cargo, and Git installed:
git clone https://github.com/zitzhen/patchsplit.git
cd patchsplit
cargo install --path . --locked
patchsplit --versionEnsure Cargo's binary directory (usually ~/.cargo/bin) is in your PATH.
patchsplit <owner/repo> <pr-number> [--out <dir>] [--force] [--squash]
patchsplit <owner> <repo> <pr-number> [--out <dir>] [--force] [--squash]
patchsplit <owner/repo> --commit <hash> [--out <dir>] [--force]
patchsplit <owner> <repo> --commit <hash> [--out <dir>] [--force]
patchsplit --gitlab <namespace/project> <mr-number> [--out <dir>] [--force] [--squash]
patchsplit --gitlab <namespace/project> --commit <hash> [--out <dir>] [--force]Replace the repository and PR number with the pull request you want to download:
patchsplit rust-lang/rust 12345
patchsplit openai codex 42 -o pr-42-patchesThe default output directory is patches/, created automatically if needed.
Filenames use a zero-padded index and a sanitized commit subject, for example:
patches/
0001-add-parser.patch
0002-wire-cli.patch
Existing output files are not overwritten by default. Pass --force to replace
them.
The per-commit files preserve GitHub's mail-formatted patches. To apply them with commit messages and authorship, run the following inside the target Git repository, checked out at a compatible base:
git am /path/to/patches/*.patchpatchsplit openai/codex 42 --squash -o pr-42-patches-s, --squash downloads GitHub's aggregate PR .diff and writes
pr-<pr-number>.patch. It represents the net change from the PR's merge base
to its head: repeated edits are combined and reverted changes disappear,
rather than concatenating the per-commit patches.
Apply it from inside the target repository on the corresponding base:
git apply --check /path/to/pr-42-patches/pr-42.patch
git apply /path/to/pr-42-patches/pr-42.patch| Mode | Output | Commit messages and authorship | Apply with |
|---|---|---|---|
| Default (GitHub PR) | One numbered patch per commit | Preserved | git am |
--squash (GitHub PR) |
One pr-<pr-number>.patch |
Not included | git apply |
--commit <hash> (GitHub) |
One <hash>.patch |
Preserved | git am |
--gitlab (GitLab MR) |
One numbered patch per commit | Preserved | git am |
--gitlab --squash |
One mr-<mr-number>.patch |
Not included | git apply |
--gitlab --commit <hash> |
One <hash>.patch |
Preserved | git am |
The combined output is a raw diff, not a git am mailbox. An empty net diff
is reported as an error and no file is written. Binary changes are limited to
the data GitHub includes in its diff; binary file contents may not be included.
Pass --commit <hash> (or -commit <hash>) with a short or full commit hash
instead of a pull request number:
patchsplit zitzhen patchsplit -commit b430113
patchsplit zitzhen/patchsplit --commit b4301133226e5c3a464cff9649de0b321c0b0a2eThis fetches https://github.com/<owner>/<repo>/commit/<hash>.patch and writes
the commit's mail-formatted patch verbatim to <hash>.patch in the output
directory, for example patches/b430113.patch. Apply it with git am just
like a per-commit PR patch. --commit cannot be combined with --squash.
Pass --gitlab to download a GitLab merge request or commit from gitlab.com.
The project path uses namespace/project form and may contain subgroups, for
example group/subgroup/project:
patchsplit --gitlab zitzhen/patchsplit 1
patchsplit --gitlab zitzhen/patchsplit --commit fafbad69af7507f41e786aa6685b2fa29716c85f
patchsplit --gitlab group/subgroup/project 1 --squash -o mr-1-patchesA merge request is fetched from
https://gitlab.com/<namespace>/<project>/-/merge_requests/<number>.patch and,
just like a GitHub PR, split into one mail-formatted patch per commit. With
--squash, the request's .diff net change is written to
mr-<number>.patch. A --commit download fetches
https://gitlab.com/<namespace>/<project>/-/commit/<hash>.patch and writes the
verbatim patch to <hash>.patch. The same --out, --force, and --squash
rules apply, and --commit cannot be combined with --squash.
| Option | Description |
|---|---|
-o, --out <dir> |
Output directory (default: patches/). |
-f, --force |
Overwrite existing patch files. |
-s, --squash |
Write the request's net diff as one patch. |
--gitlab |
Download a merge request or commit from gitlab.com. |
--commit <hash> |
Download one commit's .patch; accepts a short or full hash. |
-h, --help |
Show help. |
-V, --version |
Show version. |
patchsplit includes Simplified Chinese translations and uses English when
no matching translation is available. Language selection checks these
environment variables in order: PATCHSPLIT_LANGUAGE, LANGUAGE, LC_ALL,
LC_MESSAGES, then LANG.
Override the language for a single command on Linux or macOS:
PATCHSPLIT_LANGUAGE=zh_CN patchsplit --help
PATCHSPLIT_LANGUAGE=C patchsplit --helpCustom UTF-8 .po catalogs can be loaded from PATCHSPLIT_LOCALEDIR or
installation-relative locations next to the executable. External catalogs
take precedence over the built-in catalog for the same locale.
Build a release binary from the repository root:
cargo build --release --lockedThe binary is written to target/release/patchsplit (patchsplit.exe on Windows).
Run the test suite with:
cargo test --lockedTests on Unix also require Git to verify that aggregate patches apply to the expected file tree.
Refresh the translation template with GNU gettext tools:
scripts/update-pot.shThe template is generated at po/patchsplit.pot. Source files used for extraction are listed in po/POTFILES.in.
Bug reports, feature requests, documentation improvements, and translations
are welcome. Open an issue with
the command you ran, your OS, the patchsplit version, and the expected and
actual behavior when reporting a bug.
For code changes, add or update tests where relevant and run cargo test --locked
before submitting a pull request. Keep CLI documentation and translations in
sync with user-facing changes.
Pushing a v* tag triggers GitHub Actions to build release packages for three
platforms and automatically create a GitHub draft release:
git tag v1.x.x
git push origin v1.x.xYou can also run the Release workflow manually from GitHub Actions. Select the
branch or commit to package, then enter the release tag. If the tag does not
exist, it will point to the workflow commit. The workflow creates these files:
patchsplit-linux-x86_64.tar.gzpatchsplit-macos-x86_64.tar.gzpatchsplit-windows-x86_64.zippatchsplit_<version>_amd64.debpatchsplit-<version>-<release>.*.x86_64.rpm
Releases are created as drafts, so they should be reviewed and published from the GitHub Releases page.
Publishing a GitHub release triggers the Notify PPA workflow. It builds and
signs a Debian source package, then uploads it to Launchpad so the configured
PPA starts building the new version. Configure these repository variables:
PPA_OWNER: Launchpad account name.PPA_NAME: PPA name, without theppa:prefix.PPA_GPG_KEY_ID: full fingerprint of the primary signing key (not a signing subkey ID).PPA_MAINTAINER_NAMEandPPA_MAINTAINER_EMAIL: optional source package metadata.
Store the ASCII-armored private key as the PPA_GPG_PRIVATE_KEY repository
secret. The workflow can also be run manually with a tag, branch, or commit in
the ref input.
patchsplit is open source under the MIT License.