Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

Language: English | [Simplified Chinese](README_zh-cn.md)

Download GitHub pull requests as patches, one file per commit or one combined diff.
Download GitHub pull 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 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 `.patch`
with either a short or full hash.
- **Combined diff:** Use `--squash` to export the PR's net changes as a single patch.
- **Predictable output:** Choose an output directory; existing files are only
overwritten when you pass `--force`.
Expand Down Expand Up @@ -137,6 +140,8 @@ Ensure Cargo's binary directory (usually `~/.cargo/bin`) is in your `PATH`.
```sh
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]
```

### Split a pull request by commit
Expand Down Expand Up @@ -190,18 +195,35 @@ git apply /path/to/pr-42-patches/pr-42.patch
| --- | --- | --- | --- |
| Default | One numbered patch per commit | Preserved | `git am` |
| `--squash` | One `pr-<pr-number>.patch` | Not included | `git apply` |
| `--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.

### Download a single commit

Pass `--commit <hash>` (or `-commit <hash>`) with a short or full commit hash
instead of a pull request number:

```sh
patchsplit zitzhen patchsplit -commit b430113
patchsplit zitzhen/patchsplit --commit b4301133226e5c3a464cff9649de0b321c0b0a2e
```

This 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`.

### Options

| Option | Description |
| --- | --- |
| `-o, --out <dir>` | Output directory (default: `patches/`). |
| `-f, --force` | Overwrite existing patch files. |
| `-s, --squash` | Write the PR's net diff as one patch. |
| `--commit <hash>` | Download one commit's `.patch`; accepts a short or full hash. |
| `-h, --help` | Show help. |
| `-V, --version` | Show version. |

Expand Down
22 changes: 20 additions & 2 deletions README_zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

语言:[English](README.md) | 简体中文

`patchsplit` 是一个 Rust CLI,用来从 GitHub 下载 Pull Request 的 `.patch`
文件,并按 commit 拆分成多个独立 patch 文件。
`patchsplit` 是一个 Rust CLI,用来从 GitHub 下载 Pull Request 或单个 commit 的
`.patch` 文件,PR 补丁可按 commit 拆分成多个独立 patch 文件。

## 用法

```sh
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]
```

示例:
Expand Down Expand Up @@ -43,11 +45,27 @@ git apply pr-42-patches/pr-42.patch
作者信息,不能作为 `git am` 邮件补丁使用。净变化为空时会报补丁为空,不生成文件。
二进制变更受 GitHub diff 返回内容限制,可能不包含二进制文件内容。

### 下载单个 commit

不传 PR 编号,改用 `--commit <hash>`(也可写作 `-commit <hash>`)传入短哈希或
完整哈希:

```sh
patchsplit zitzhen patchsplit -commit b430113
patchsplit zitzhen/patchsplit --commit b4301133226e5c3a464cff9649de0b321c0b0a2e
```

对应下载 `https://github.com/<owner>/<repo>/commit/<hash>.patch`,并把该 commit
的邮件格式补丁原样写入输出目录下的 `<hash>.patch`,例如
`patches/b430113.patch`。它和按 commit 拆分的 PR 补丁一样可用 `git am` 应用。
`--commit` 不能与 `--squash` 同时使用。

## 参数

- `-o, --out <dir>`:指定 patch 文件的输出目录。
- `-f, --force`:允许覆盖已存在的 patch 文件。
- `-s, --squash`:将 PR 的最终净变化输出为一个补丁。
- `--commit <hash>`:下载单个 commit 的 `.patch`,接受短哈希或完整哈希。
- `-h, --help`:显示帮助。
- `-V, --version`:显示版本。

Expand Down
49 changes: 47 additions & 2 deletions debian/patchsplit.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.TH PATCHSPLIT 1 "August 2026" "patchsplit 1.0.2" "User Commands"
.SH NAME
patchsplit \- download a GitHub pull request patch and split it by commit
patchsplit \- download a GitHub pull request patch, split it by commit, or fetch a single commit patch
.SH SYNOPSIS
.B patchsplit
.RI < owner/repo > " <pr-number>"
Expand All @@ -21,6 +21,26 @@ patchsplit \- download a GitHub pull request patch and split it by commit
[\c
.B \-\-force ]
.RE
.br
.B patchsplit
.RI < owner/repo > " \-\-commit <hash>"
.RS
[\c
.B \-\-out
.IR dir ]
[\c
.B \-\-force ]
.RE
.br
.B patchsplit
.RI < owner > " <repo> \-\-commit <hash>"
.RS
[\c
.B \-\-out
.IR dir ]
[\c
.B \-\-force ]
.RE
.SH DESCRIPTION
.B patchsplit
downloads the patch for a GitHub pull request and splits it into
Expand All @@ -35,6 +55,16 @@ Repeated edits are combined and reverted changes disappear.
The output is a raw diff for git apply, without commit metadata.
An empty net diff is an error. Binary contents may be omitted by GitHub.
.PP
With
.BR \-\-commit " " \fIhash\fR ,
download the patch of a single commit identified by a short or full hash and
write it verbatim to
.BR <hash>.patch .
The result is a mail-formatted patch for git am.
.B \-\-commit
cannot be combined with
.BR \-\-squash .
.PP
The resulting patch files are written to the
.B patches
directory by default.
Expand All @@ -43,6 +73,15 @@ directory by default.
.BR \-s ", " \-\-squash
Write the PR's net diff as one patch instead of splitting by commit.
.TP
.BR \-\-commit " " \fIhash\fR
Download the .patch of a single commit instead of a pull request.
.I hash
may be a short or full commit hash.
The output file is named
.BR <hash>.patch .
Cannot be combined with
.BR \-\-squash .
.TP
.BR \-o ", " \-\-out " " \fIdir\fR
Write patch files to
.IR dir .
Expand All @@ -69,11 +108,17 @@ Download pull request 42 from
.BR openai/codex
and write the resulting patches to
.BR pr-42-patches .
.TP
.B patchsplit zitzhen patchsplit \-commit b430113
Download the patch for commit b430113 from
.BR zitzhen/patchsplit
and write it to
.BR b430113.patch .
.SH REQUIREMENTS
.B patchsplit
uses
.B curl
to download pull request patches from GitHub.
to download pull request and commit patches from GitHub.
.SH SEE ALSO
.BR curl (1)
.SH AUTHORS
Expand Down
49 changes: 47 additions & 2 deletions packaging/patchsplit.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.TH PATCHSPLIT 1 "August 2026" "patchsplit 1.0.2" "User Commands"
.SH NAME
patchsplit \- download a GitHub pull request patch and split it by commit
patchsplit \- download a GitHub pull request patch, split it by commit, or fetch a single commit patch
.SH SYNOPSIS
.B patchsplit
.RI < owner/repo > " <pr-number>"
Expand All @@ -21,6 +21,26 @@ patchsplit \- download a GitHub pull request patch and split it by commit
[\c
.B \-\-force ]
.RE
.br
.B patchsplit
.RI < owner/repo > " \-\-commit <hash>"
.RS
[\c
.B \-\-out
.IR dir ]
[\c
.B \-\-force ]
.RE
.br
.B patchsplit
.RI < owner > " <repo> \-\-commit <hash>"
.RS
[\c
.B \-\-out
.IR dir ]
[\c
.B \-\-force ]
.RE
.SH DESCRIPTION
.B patchsplit
downloads the patch for a GitHub pull request and splits it into
Expand All @@ -35,6 +55,16 @@ Repeated edits are combined and reverted changes disappear.
The output is a raw diff for git apply, without commit metadata.
An empty net diff is an error. Binary contents may be omitted by GitHub.
.PP
With
.BR \-\-commit " " \fIhash\fR ,
download the patch of a single commit identified by a short or full hash and
write it verbatim to
.BR <hash>.patch .
The result is a mail-formatted patch for git am.
.B \-\-commit
cannot be combined with
.BR \-\-squash .
.PP
The resulting patch files are written to the
.B patches
directory by default.
Expand All @@ -43,6 +73,15 @@ directory by default.
.BR \-s ", " \-\-squash
Write the PR's net diff as one patch instead of splitting by commit.
.TP
.BR \-\-commit " " \fIhash\fR
Download the .patch of a single commit instead of a pull request.
.I hash
may be a short or full commit hash.
The output file is named
.BR <hash>.patch .
Cannot be combined with
.BR \-\-squash .
.TP
.BR \-o ", " \-\-out " " \fIdir\fR
Write patch files to
.IR dir .
Expand All @@ -69,11 +108,17 @@ Download pull request 42 from
.BR openai/codex
and write the resulting patches to
.BR pr-42-patches .
.TP
.B patchsplit zitzhen patchsplit \-commit b430113
Download the patch for commit b430113 from
.BR zitzhen/patchsplit
and write it to
.BR b430113.patch .
.SH REQUIREMENTS
.B patchsplit
uses
.B curl
to download pull request patches from GitHub.
to download pull request and commit patches from GitHub.
.SH SEE ALSO
.BR curl (1)
.SH AUTHORS
Expand Down
18 changes: 17 additions & 1 deletion po/patchsplit.pot
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ msgstr ""
msgid "pull request number must be a positive integer, got {value}"
msgstr ""

#: src/main.rs
msgid "expected a GitHub repository with --commit <hash>"
msgstr ""

#: src/main.rs
msgid "commit hash must consist of 4 to 40 hexadecimal characters, got {value}"
msgstr ""

#: src/main.rs
msgid "--commit cannot be combined with --squash"
msgstr ""

#: src/main.rs
msgid "missing value for {option}"
msgstr ""
Expand Down Expand Up @@ -119,17 +131,21 @@ msgid ""
"Usage:\n"
" patchsplit <owner/repo> <pr-number> [--out <dir>] [--force] [--squash]\n"
" patchsplit <owner> <repo> <pr-number> [--out <dir>] [--force] [--squash]\n"
" patchsplit <owner/repo> --commit <hash> [--out <dir>] [--force]\n"
" patchsplit <owner> <repo> --commit <hash> [--out <dir>] [--force]\n"
"\n"
"Options:\n"
" -o, --out <dir> Output directory for patch files [default: "
"patches]\n"
" -f, --force Overwrite existing patch files\n"
" -s, --squash Write the PR's net diff as one patch\n"
" --commit <hash> Download one commit's .patch (short or full hash)\n"
" -h, --help Show this help\n"
" -V, --version Show version\n"
"\n"
"Examples:\n"
" patchsplit rust-lang/rust 12345\n"
" patchsplit openai codex 42 -o pr-42-patches\n"
" patchsplit openai/codex 42 --squash"
" patchsplit openai/codex 42 --squash\n"
" patchsplit zitzhen patchsplit -commit b430113"
msgstr ""
24 changes: 22 additions & 2 deletions po/zh_CN.po
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ msgstr "无效的 GitHub 仓库{kind}:{value}"
msgid "pull request number must be a positive integer, got {value}"
msgstr "Pull Request 编号必须是正整数,实际为 {value}"

#: src/main.rs
msgid "expected a GitHub repository with --commit <hash>"
msgstr "需要提供 GitHub 仓库,并通过 --commit <hash> 指定提交"

#: src/main.rs
msgid "commit hash must consist of 4 to 40 hexadecimal characters, got {value}"
msgstr "commit 哈希必须为 4 到 40 位十六进制字符,实际为 {value}"

#: src/main.rs
msgid "--commit cannot be combined with --squash"
msgstr "--commit 不能与 --squash 同时使用"

#: src/main.rs
msgid "missing value for {option}"
msgstr "缺少 {option} 的值"
Expand Down Expand Up @@ -118,32 +130,40 @@ msgid ""
"Usage:\n"
" patchsplit <owner/repo> <pr-number> [--out <dir>] [--force] [--squash]\n"
" patchsplit <owner> <repo> <pr-number> [--out <dir>] [--force] [--squash]\n"
" patchsplit <owner/repo> --commit <hash> [--out <dir>] [--force]\n"
" patchsplit <owner> <repo> --commit <hash> [--out <dir>] [--force]\n"
"\n"
"Options:\n"
" -o, --out <dir> Output directory for patch files [default: "
"patches]\n"
" -f, --force Overwrite existing patch files\n"
" -s, --squash Write the PR's net diff as one patch\n"
" --commit <hash> Download one commit's .patch (short or full hash)\n"
" -h, --help Show this help\n"
" -V, --version Show version\n"
"\n"
"Examples:\n"
" patchsplit rust-lang/rust 12345\n"
" patchsplit openai codex 42 -o pr-42-patches\n"
" patchsplit openai/codex 42 --squash"
" patchsplit openai/codex 42 --squash\n"
" patchsplit zitzhen patchsplit -commit b430113"
msgstr ""
"用法:\n"
" patchsplit <owner/repo> <pr-number> [--out <dir>] [--force] [--squash]\n"
" patchsplit <owner> <repo> <pr-number> [--out <dir>] [--force] [--squash]\n"
" patchsplit <owner/repo> --commit <hash> [--out <dir>] [--force]\n"
" patchsplit <owner> <repo> --commit <hash> [--out <dir>] [--force]\n"
"\n"
"选项:\n"
" -o, --out <dir> patch 文件输出目录 [默认:patches]\n"
" -f, --force 覆盖已有 patch 文件\n"
" -s, --squash 将 PR 的最终净变化输出为一个补丁\n"
" --commit <hash> 下载单个提交的 .patch(短哈希或完整哈希)\n"
" -h, --help 显示帮助信息\n"
" -V, --version 显示版本\n"
"\n"
"示例:\n"
" patchsplit rust-lang/rust 12345\n"
" patchsplit openai codex 42 -o pr-42-patches\n"
" patchsplit openai/codex 42 --squash"
" patchsplit openai/codex 42 --squash\n"
" patchsplit zitzhen patchsplit -commit b430113"
Loading
Loading