Git 的归档与补丁功能不仅是代码的"打包工具"和"传送协议",更是实现备份恢复、离线同步、团队协作与代码评审的核心机制。以下将为你完整拆解这些命令的每一处细节。
一、归档相关命令
归档命令用于将 Git 仓库中的文件打包成单个文件,便于备份、分发或离线传输。
1.1 git archive ------ 生成代码快照归档
git archive 将指定提交、分支或标签中的文件打包成 tar 或 zip 等格式的归档文件。它生成的快照包含干净的代码内容,但不包含 .git 目录,非常适合发布和分发。
命令语法
bash
git archive [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>] [-o <file>] [--worktree-attributes] [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish> [<path>...]
全部选项详解
格式控制选项:
| 选项 | 说明 |
|---|---|
--format=<fmt> |
指定归档格式:tar、zip、tar.gz、tgz,或通过 tar.<format>.command 配置的自定义格式 |
-l / --list |
列出所有可用的归档格式 |
-v / --verbose |
报告进度信息到标准错误输出 |
文件和路径控制选项:
| 选项 | 说明 |
|---|---|
--prefix=<prefix>/ |
为归档内所有文件路径添加指定前缀,可以重复使用;右边的值会被用于所有跟踪文件 |
-o <file> / --output=<file> |
将归档写入指定文件而非标准输出 |
--add-file=<file> |
将非跟踪文件 添加到归档中。在归档中的路径由最后一次 --prefix 的值和该文件的基础名拼接而成 |
--add-virtual-file=<path>:<content> |
直接通过内容添加虚拟文件。路径中的冒号需要转义,可以通过引号包裹。该选项不受 --prefix 影响 |
--worktree-attributes |
查找工作区 .gitattributes 文件中的属性设置 |
--mtime=<time> |
设置归档中所有文件的修改时间 |
远程操作选项:
| 选项 | 说明 |
|---|---|
--remote=<repo> |
从远程仓库生成归档,无需本地克隆 |
--exec=<git-upload-archive> |
与 --remote 配合,指定远程端的 git-upload-archive 命令路径 |
归档中的元数据特性:
- 当使用提交 ID 或标签 ID 时,每个文件的修改时间取自该提交的提交时间,且 tar 格式会在全局扩展 pax 头中存储提交 ID,便于追溯
- 当使用树 ID 时,使用当前时间作为修改时间
全部使用示例
1. 基本打包
bash
# 将当前 HEAD 打包为 tar 格式
git archive --format=tar HEAD > project.tar
# 利用扩展名自动推断格式
git archive -o project.tar.gz HEAD # 自动使用 tar.gz
git archive -o project.zip HEAD # 自动使用 zip
2. 添加路径前缀
bash
# 所有文件放入项目根目录
git archive --prefix=myapp-1.0/ -o myapp-1.0.tar.gz HEAD
# 解压测试
git archive --prefix=myapp-1.0/ HEAD | tar -x -C /tmp/
3. 打包指定路径
bash
# 只打包 src 和 docs 目录
git archive -o src-only.zip HEAD src/ docs/
# 打包单个文件
git archive -o README.zip HEAD README.md
4. 添加非跟踪文件
bash
# 打包时额外包含配置文件
git archive -o deploy.tar.gz --add-file=.env.production HEAD
# 添加多个文件
git archive -o bundle.tar.gz --add-file=LICENSE --add-file=README.md HEAD
# 使用 prefix 控制非跟踪文件位置
git archive --prefix=config/ --add-file=.env.production -o deploy.tar.gz HEAD
5. 添加虚拟文件
bash
# 在归档中创建包含版本信息的文件
git archive -o release.tar.gz --add-virtual-file=version.txt:v1.0.0 HEAD
# 文件名含特殊字符时需加引号
git archive -o release.tar.gz --add-virtual-file='"path/with:colon.txt":content' HEAD
# 从环境变量读取内容
git archive -o release.tar.gz --add-virtual-file="build-info.txt:$(git describe)" HEAD
6. 指定其他提交/分支/标签
bash
# 打包指定标签
git archive --format=tar.gz --prefix=myapp-1.0/ -o myapp-1.0.tar.gz v1.0.0
# 打包指定分支
git archive -o feature.zip feature/login
# 打包指定提交
git archive -o snapshot.zip abc1234
7. 从远程仓库打包
bash
# 从远程仓库直接打包,无需克隆
git archive --remote=https://github.com/user/repo.git --format=tar.gz HEAD > remote.tar.gz
# 指定远程分支
git archive --remote=git@github.com:user/repo.git --format=zip v1.0.0 > remote.zip
8. 组合使用不同压缩格式
bash
# 打包并压缩为 gz
git archive --format=tar --prefix=myapp/ HEAD | gzip > myapp.tar.gz
# 打包为 bz2
git archive --format=tar HEAD | bzip2 > myapp.tar.bz2
# 打包为 xz
git archive --format=tar HEAD | xz > myapp.tar.xz
9. 查看归档内容
bash
# 预览 tar 包内容
git archive --format=tar HEAD | tar -t
# 预览 zip 包内容
git archive --format=zip HEAD | zipinfo /dev/stdin
# 查看详细输出
git archive -v --format=tar HEAD > verbose.tar
10. 使用 .gitattributes 控制导出
bash
# 在 .gitattributes 中定义导出行为
echo "tests/ export-ignore" >> .gitattributes
echo "*.log export-ignore" >> .gitattributes
echo "README.md export-subst" >> .gitattributes
# 使用--worktree-attributes包含工作区属性
git archive --worktree-attributes -o release.zip HEAD
注意事项
- 归档不包含
.git目录和历史信息 - 默认遵循
.gitattributes中的export-ignore和export-subst设置 --remote需要远程仓库启用git-upload-archive服务--add-virtual-file受平台命令行长度限制,复杂内容建议使用--add-file
1.2 git bundle ------ 离线传输 Git 对象
git bundle 将 Git 对象和引用打包成单个文件,用于没有网络连接情况下的仓库传输 。它支持增量备份和完整备份,可以配合 git clone、git fetch 使用。
命令语法
bash
git bundle create [-q | --quiet | --progress] [--version=<version>] <file> <git-rev-list-args>
git bundle verify [-q | --quiet] <file>
git bundle list-heads <file> [<refname>...]
git bundle unbundle [--progress] <file> [<refname>...]
全部选项详解
| 子命令 | 说明 |
|---|---|
create |
创建 bundle 文件,需指定 git-rev-list-args 定义包内容 |
verify |
验证 bundle 文件是否有效,检查前提提交是否存在 |
list-heads |
列出 bundle 中包含的引用(分支、标签) |
unbundle |
将 bundle 中的对象解包到当前仓库 |
| 选项 | 说明 |
|---|---|
-q / --quiet |
静默模式,不输出进度信息 |
--progress |
显示进度信息(适用于 create 和 unbundle) |
--version=<version> |
指定 bundle 版本号 |
Bundle 格式原理
Bundle 本质上是带有头部的 .pack 文件,头部指示包含哪些引用。使用修订排除创建的 bundle 是 "瘦包" ,体积更小。Git 不支持将 git push 直接推送到 bundle 文件。
全部使用示例
1. 创建完整仓库的 bundle
bash
# 备份整个仓库(包含所有引用)
git bundle create repo.bundle --all
# 验证 bundle 完整性
git bundle verify repo.bundle
# 使用安静模式
git bundle create -q repo.bundle --all
2. 创建增量 bundle
bash
# 打包从 v1.0 到 master 的所有提交
git bundle create updates.bundle v1.0..master
# 或使用负向引用
git bundle create updates.bundle ^v1.0 master
# 打包从特定提交到当前 HEAD
git bundle create feature.bundle abc1234..HEAD
3. 创建指定分支的 bundle
bash
# 只打包 feature 分支
git bundle create feature.bundle feature/login
# 打包多个分支
git bundle create branches.bundle main feature/login
# 打包所有标签
git bundle create tags.bundle --tags
4. 查看 bundle 内容
bash
# 查看 bundle 中的引用列表
git bundle list-heads updates.bundle
# 输出示例:
# abc1234 refs/heads/master
# def5678 refs/heads/feature
# 验证 bundle 对当前仓库是否可用
git bundle verify updates.bundle
# 查看特定引用
git bundle list-heads repo.bundle refs/heads/main
5. 从 bundle 克隆仓库
bash
# 从 bundle 文件直接克隆
git clone updates.bundle new-repo
# 克隆后添加远程仓库
git clone repo.bundle my-repo
cd my-repo
git remote add origin https://github.com/user/repo.git
# 克隆时指定分支
git clone -b feature updates.bundle feature-repo
6. 从 bundle 获取更新
bash
# 在已有仓库中从 bundle fetch
git fetch updates.bundle master:temp-branch
# fetch 后合并
git merge temp-branch
# 或直接 fetch 到当前分支
git pull updates.bundle master
# fetch 所有引用
git fetch updates.bundle --all
7. 使用标准输入输出
bash
# 创建 bundle 并写入 stdout
git bundle create - --all > repo.bundle
# 从 stdin 读取 bundle
git bundle verify - < repo.bundle
git clone - repo2 < repo.bundle
8. 创建增量备份脚本
bash
#!/bin/bash
# 增量备份脚本
LAST_BACKUP="refs/tags/last-backup"
CURRENT_BACKUP="refs/tags/backup-$(date +%Y%m%d)"
# 检查是否已有上次备份标签
if git rev-parse -q --verify $LAST_BACKUP >/dev/null; then
# 创建增量 bundle
git bundle create backup-$CURRENT_BACKUP.bundle $LAST_BACKUP..HEAD
else
# 创建完整 bundle
git bundle create backup-$CURRENT_BACKUP.bundle --all
fi
# 更新备份标签
git tag -f $CURRENT_BACKUP HEAD
9. 跨网络传输完整工作流
bash
# 源机器:创建 bundle
git bundle create repo.bundle --all
# 通过 U 盘、scp 等方式传输 repo.bundle 到目标机器
# 目标机器:克隆 bundle
git clone repo.bundle my-repo
# 增量更新:源机器更新 bundle
git bundle create update.bundle v1.0..master
# 目标机器获取增量
cd my-repo
git fetch /path/to/update.bundle master:new-master
git merge new-master
10. 基于时间点的增量备份
bash
# 备份最近一周的提交
git bundle create weekly-backup.bundle --since="1 week ago" --all
# 备份特定作者的所有提交
git bundle create author-backup.bundle --author="John Doe" --all
二、补丁相关命令(完整详解)
补丁命令是 Git 邮件驱动开发工作流的核心,下面以 git format-patch 到 git am 的闭环操作为主线进行深度展开。
补丁工作流全景图
scss
┌─────────────────────────────────────────────────────────────────────────────┐
│ Git 补丁工作流全景图 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ 提交历史 │ ──► │ git format-patch│ ──► │ .patch 文件(邮件格式) │ │
│ │ (commits) │ │ │ │ ▸ 每个提交独立文件 │ │
│ └─────────────┘ └─────────────────┘ │ ▸ 包含完整提交元数据 │ │
│ └───────────┬─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ 传输方式 │ │
│ ├───────────────┬─────────────────┤ │
│ │ 邮件 (email) │ 文件传输 (USB等)│ │
│ └───────┬───────┴────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────────┐ ┌─────────────────────┐ │
│ │ git send-email │ │ 直接传递 .patch │ │
│ │ ▸ 发送到邮件列表 │ │ 文件给对方 │ │
│ │ ▸ 支持附注/内嵌 │ │ │ │
│ └───────────┬────────────┘ └──────────┬──────────┘ │
│ │ │ │
│ └──────────┬───────────────┘ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ 接收方获取补丁 │ │
│ └─────────┬───────────┘ │
│ │ │
│ ┌─────────────────────────┼─────────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐│
│ │ git am │ │ git apply │ │ git request- ││
│ │ ▸ 应用邮件补丁 │ │ ▸ 应用普通补丁 │ │ pull ││
│ │ ▸ 保留作者信息 │ │ ▸ 不自动提交 │ │ ▸ 生成拉取请求 ││
│ └────────┬─────────┘ └────────┬─────────┘ └───────┬────────┘│
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐│
│ │ 生成新提交 │ │ 仅修改工作区 │ │ 通知上游 ││
│ │ ▸ 保留原作者 │ │ ▸ 不创建提交 │ │ ▸ 执行 git ││
│ │ ▸ 保留提交信息 │ │ ▸ 可选择性提交 │ │ pull ││
│ └──────────────────┘ └──────────────────┘ └────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.1 git format-patch ------ 生成邮件格式补丁
git format-patch 将每个提交转换为独立的补丁文件,完整保留提交元数据(作者、日期、提交信息),格式类似 UNIX 邮箱格式,便于邮件提交或与 git am 配合使用。
命令语法
bash
git format-patch [-k] [(-o|--output-directory) <dir> | --stdout] [--no-thread | --thread[=<style>]] [(--attach|--inline)[=<boundary>] | --no-attach] [-s | --signoff] [--signature=<signature> | --no-signature] [--signature-file=<file>] [-n | --numbered | -N | --no-numbered] [--start-number <n>] [--numbered-files] [--in-reply-to=<message-id>] [--suffix=.<sfx>] [--ignore-if-in-upstream] [--always] [--cover-from-description=<mode>] [--rfc[=<rfc>]] [--subject-prefix=<subject-prefix>] [(--reroll-count|-v) <n>] [--to=<email>] [--cc=<email>] [--[no-]cover-letter] [--quiet] [--[no-]encode-email-headers] [--no-notes | --notes[=<ref>]] [--interdiff=<previous>] [--range-diff=<previous> [--creation-factor=<percent>]] [--filename-max-length=<n>] [--progress] [<common-diff-options>] [ <since> | <revision-range> ]
全部选项详解
输出控制选项:
| 选项 | 说明 |
|---|---|
-o <dir> / --output-directory=<dir> |
指定补丁输出目录 |
--stdout |
输出到标准输出(适合管道操作) |
--numbered-files |
使用纯数字作为文件名 |
--start-number <n> |
设置起始编号 |
--filename-max-length=<n> |
限制文件名最大长度 |
--progress |
显示生成进度 |
-q / --quiet |
静默模式 |
格式控制选项:
| 选项 | 说明 |
|---|---|
--suffix=.<sfx> |
设置补丁文件后缀,默认 .patch |
-k / --keep-subject |
保持提交信息的原样,不添加 [PATCH] 前缀 |
-N / --no-numbered |
不添加编号(不推荐) |
-n / --numbered |
强制添加编号 |
--attach[=<boundary>] |
以附件形式生成补丁,可选边界字符串 |
--inline[=<boundary>] |
以内嵌形式生成补丁 |
--no-attach |
禁用附件模式 |
--encode-email-headers / --no-encode-email-headers |
控制是否编码邮件头(如非 ASCII 字符) |
提交范围选项:
| 选项 | 说明 |
|---|---|
--root |
从初始提交开始生成补丁 |
--always |
即使没有变更也生成补丁 |
<since> |
单个提交:输出从此提交之后(直到 HEAD)的所有提交 |
<revision-range> |
标准修订范围表达式,如 main..feature |
邮件相关选项:
| 选项 | 说明 |
|---|---|
--to=<email> |
添加 To 收件人 |
--cc=<email> |
添加 Cc 抄送 |
--subject-prefix=<prefix> |
设置邮件主题前缀,默认 [PATCH] |
-v <n> / --reroll-count=<n> |
设置补丁版本号,如 -v2 生成 [PATCH v2] |
--rfc[=<rfc>] |
将主题前缀修改为 [RFC PATCH](请求意见稿) |
--in-reply-to=<message-id> |
设置回复的 Message-ID |
--no-thread / --thread[=<style>] |
设置邮件线程化:shallow 或 deep |
--cover-letter |
生成封面信(cover letter) |
--cover-from-description=<mode> |
控制封面信内容来源:message、subject、auto、none |
--notes[=<ref>] / --no-notes |
在提交信息中包含 Notes |
--range-diff=<previous> |
生成与之前版本的差异说明 |
--interdiff=<previous> |
生成与之前版本的补丁差异 |
--creation-factor=<percent> |
调整 range-diff 的匹配相似度 |
签名选项:
| 选项 | 说明 |
|---|---|
-s / --signoff |
添加 Signed-off-by 签名 |
--signature=<signature> |
自定义邮件签名 |
--signature-file=<file> |
从文件读取签名内容 |
--no-signature |
不添加签名 |
其他选项:
| 选项 | 说明 |
|---|---|
--ignore-if-in-upstream |
跳过已在上游存在的提交 |
--keep-non-patch |
保留非补丁部分的输出 |
全部使用示例
1. 基本用法
bash
# 生成最近 3 次提交的补丁
git format-patch -3
# 生成从 v1.0 到 HEAD 之间的所有提交补丁
git format-patch v1.0..HEAD
# 生成从初始提交到 HEAD 的所有补丁
git format-patch --root
# 仅生成单个提交的补丁
git format-patch -1 abc1234
2. 指定输出位置和格式
bash
# 输出到指定目录
git format-patch -o patches/ main..feature
# 输出到标准输出(适合管道)
git format-patch --stdout main..feature > all.patches
# 使用纯数字文件名(不包含提交主题)
git format-patch --numbered-files -3
# 设置起始编号
git format-patch --start-number 100 -3
# 自定义文件后缀
git format-patch --suffix=.diff -3
3. 邮件主题控制
bash
# 添加版本号
git format-patch -v2 main..feature
# 生成主题: [PATCH v2 1/3] ...
# 自定义主题前缀
git format-patch --subject-prefix="RFC PATCH" main..feature
# 生成主题: [RFC PATCH 1/3] ...
# 添加 RFC 标识(快捷方式)
git format-patch --rfc main..feature
# 生成主题: [RFC PATCH 1/3] ...
# 使用多个前缀
git format-patch --subject-prefix="PATCH net-next" main..feature
4. 添加收件人
bash
# 添加 To 和 Cc
git format-patch --to=maintainer@example.com --cc=reviewer@example.com main..feature
# 多个收件人
git format-patch --to=team@example.com --cc=lead@example.com --cc=qa@example.com main..feature
# 从文件读取收件人列表
git format-patch --to="$(cat maintainers.txt)" main..feature
5. 版本系列对比
bash
# 生成与上一版本的差异说明(range-diff)
git format-patch --range-diff=origin/main~5 main
# 调整匹配相似度(默认 100,数值越小允许更大差异)
git format-patch --range-diff=origin/main~5 --creation-factor=80 main
# 生成简化的版本之间的补丁差异(interdiff)
git format-patch --interdiff=origin/main~5 main
6. 生成封面信
bash
# 生成包含封面信的补丁系列
git format-patch --cover-letter -o patches/ main..feature
# 自动从提交内容生成封面信
git format-patch --cover-letter --cover-from-description=message -3
# 编辑生成的 patches/0000-cover-letter.patch 文件
7. 添加签名
bash
# 自动添加 Signed-off-by
git format-patch -s main..feature
# 自定义签名内容
git format-patch --signature="My Project Team" main..feature
# 从文件读取签名
git format-patch --signature-file=signature.txt main..feature
8. 回复已有邮件线程
bash
# 设置 In-Reply-To,使补丁成为某个邮件的回复
git format-patch --in-reply-to="<message-id@example.com>" main..feature
# 设置线程化(所有补丁回复给第一封)
git format-patch --thread main..feature
# 深度线程化(每个补丁回复前一个)
git format-patch --thread=deep main..feature
9. 附件和内嵌模式
bash
# 以附件形式生成补丁
git format-patch --attach main..feature
# 以内嵌形式生成补丁
git format-patch --inline main..feature
# 指定边界字符串
git format-patch --attach="boundary-string" main..feature
10. 高级用法
bash
# 生成补丁时忽略已在上游存在的提交
git format-patch --ignore-if-in-upstream main..feature
# 保持提交信息中原有的 [PATCH] 前缀
git format-patch -k main..feature
# 显示生成进度
git format-patch --progress -100
# 控制文件名长度(避免过长文件名)
git format-patch --filename-max-length=80 -3
# 静默模式(不输出任何提示)
git format-patch -q main..feature
2.2 git am ------ 应用邮箱格式补丁
git am(apply mailbox)从邮箱文件中读取补丁,自动分割邮件,提取提交信息、作者信息和补丁内容,并在当前分支上创建提交。
命令语法
bash
git am [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--[no-]verify] [--[no-]3way] [--interactive] [--committer-date-is-author-date] [--ignore-date] [--ignore-space-change | --ignore-whitespace] [--whitespace=<action>] [-C<n>] [-p<n>] [--directory=<dir>] [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet] [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>] [--quoted-cr=<action>] [--empty=(stop|drop|keep)] [(<mbox> | <Maildir>)...]
git am (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
全部选项详解
应用控制选项:
| 选项 | 说明 |
|---|---|
-s / --signoff |
在提交信息中添加 Signed-off-by |
-k / --keep |
保留提交信息中原有的 [PATCH] 前缀 |
--keep-non-patch |
保留邮件中非补丁部分的内容 |
--keep-cr / --no-keep-cr |
保留或去除行尾的 CR 字符(回车符) |
-c / --scissors |
忽略邮件中的剪刀线(-- >8 --)之后的内容 |
--no-scissors |
不忽略剪刀线 |
编码和格式控制:
| 选项 | 说明 |
|---------------------------|-------------------------------------------|---------|----------------------------------------|
| --utf8 / --no-utf8 | 将提交信息从 ISO-8859-1 转换为 UTF-8(默认启用) |
| --patch-format=<format> | 指定补丁格式:mbox、stgit、stgit-series、hg |
| --quoted-cr=<action> | 处理带引号的可打印编码中的回车符 |
| `--empty=(stop | drop | keep)` | 处理空补丁:stop停止、drop跳过、keep保留为仅信息提交 |
合并和冲突处理:
| 选项 | 说明 |
|---|---|
--3way / --no-3way |
尝试三方合并,增强补丁应用的兼容性 |
--interactive |
交互模式,逐个确认每个补丁的应用 |
--committer-date-is-author-date |
使用作者日期作为提交日期 |
--ignore-date |
使用当前日期作为提交日期,忽略作者日期 |
--ignore-space-change / --ignore-whitespace |
忽略空白字符变化 |
--whitespace=<action> |
处理空白字符错误:nowarn、warn、fix、error、error-all |
-C<n> |
减少上下文匹配行数(默认 3) |
-p<n> |
剥离路径前缀的级数 |
--directory=<dir> |
将补丁应用到指定子目录 |
--exclude=<path> |
排除指定路径 |
--include=<path> |
只包含指定路径 |
--reject |
冲突时保留 .rej 文件 |
-S[<keyid>] |
使用 GPG 签名提交(使用指定密钥) |
其他选项:
| 选项 | 说明 |
|---|---|
--verify / --no-verify |
跳过 pre-applypatch 和 post-applypatch 钩子 |
-q / --quiet |
静默模式 |
--retry |
重新尝试应用失败的补丁 |
冲突处理子命令
| 子命令 | 说明 |
|--------------------------------|------------------|---------------|
| --continue / -r | 解决冲突后继续应用剩余补丁 |
| --skip | 跳过当前有问题的补丁 |
| --abort | 放弃整个 am 操作,恢复原状态 |
| --quit | 退出 am 操作,但不恢复原状态 |
| `--show-current-patch[=(diff | raw)]` | 显示当前正在应用的补丁内容 |
| --allow-empty | 允许创建空提交 |
全部使用示例
1. 基本用法
bash
# 应用单个补丁文件
git am 0001-fix-bug.patch
# 应用目录下的所有补丁(按文件名顺序)
git am patches/*.patch
# 从标准输入读取
git am < incoming.patch
2. 添加签名
bash
# 应用补丁时自动添加 Signed-off-by
git am --signoff patches/*.patch
# 使用 GPG 签名提交
git am -S patches/*.patch
# 指定签名密钥
git am -S=0A46826A patches/*.patch
3. 三方合并模式
bash
# 使用三方合并,提高补丁兼容性
git am --3way patches/*.patch
# 检查冲突时减少上下文匹配
git am -C1 patches/*.patch
4. 交互模式
bash
# 逐个确认每个补丁的应用
git am --interactive patches/*.patch
# 每个补丁前会询问: Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all
5. 日期控制
bash
# 使用作者日期作为提交日期
git am --committer-date-is-author-date patches/*.patch
# 忽略作者日期,使用当前日期
git am --ignore-date patches/*.patch
6. 处理空白字符
bash
# 忽略空白字符差异
git am --ignore-space-change patches/*.patch
# 自动修复空白字符错误
git am --whitespace=fix patches/*.patch
# 将空白字符错误视为警告但不阻止应用
git am --whitespace=warn patches/*.patch
7. 调整路径
bash
# 将补丁应用到 subdir 目录下
git am --directory=subdir/ patches/*.patch
# 剥离路径前缀(-p1 表示去掉第一级路径)
git am -p2 patches/*.patch
# 排除特定路径
git am --exclude="docs/*" patches/*.patch
# 只包含特定路径
git am --include="src/*" patches/*.patch
8. 冲突处理流程
bash
# 开始应用补丁
git am patches/*.patch
# 如果出现冲突...
# 方法一:解决冲突后继续
git status # 查看冲突文件
# 手动解决冲突...
git add <resolved-files>
git am --continue
# 方法二:跳过当前补丁
git am --skip
# 方法三:放弃整个操作
git am --abort
9. 查看当前补丁
bash
# 查看正在应用的补丁内容
git am --show-current-patch
# 查看补丁的 diff 部分
git am --show-current-patch=diff
# 查看补丁的原始内容(含邮件头)
git am --show-current-patch=raw
10. 高级选项
bash
# 使用剪刀线(忽略邮件签名等)
git am --scissors patches/*.patch
# 处理空提交(保留空提交)
git am --empty=keep patches/*.patch
# 跳过空提交
git am --empty=drop patches/*.patch
# 遇到空提交时停止
git am --empty=stop patches/*.patch
# 强制重新尝试应用(之前失败的补丁)
git am --retry
# 退出但不恢复(保留当前状态)
git am --quit
2.3 git apply ------ 应用普通补丁(不自动提交)
git apply 读取 diff 输出并将其应用到文件。与 git am 不同,git apply 不会自动创建提交,仅修改工作区文件。可以在非 Git 仓库中使用。
命令语法
bash
git apply [--stat] [--numstat] [--summary] [--check] [--index | --intent-to-add] [--3way] [--ours | --theirs | --union] [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse] [--allow-binary-replacement | --binary] [--reject] [-z] [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached] [--ignore-space-change | --ignore-whitespace] [--whitespace=(nowarn|warn|fix|error|error-all)] [--exclude=<path>] [--include=<path>] [--directory=<root>] [--verbose | --quiet] [--unsafe-paths] [--allow-empty] [<patch>...]
全部选项详解
检查模式选项(不实际应用补丁):
| 选项 | 说明 |
|---|---|
--stat |
输出 diffstat 统计,不应用补丁 |
--numstat |
输出机器可读的统计(添加/删除行数) |
--summary |
输出扩展头信息摘要(创建、重命名、模式变更) |
--check |
检查补丁是否可应用,不实际修改 |
应用范围选项:
| 选项 | 说明 |
|---|---|
--index |
同时应用到索引和工作区(需在 Git 仓库中执行) |
--cached |
只应用到索引(暂存区),不修改工作区 |
--intent-to-add / -N |
标记新文件为"待添加",配合工作区使用 |
-z |
使用 NUL 字符分隔输出,而非 LF |
合并和冲突处理:
| 选项 | 说明 |
|---|---|
--3way / -3 |
尝试三方合并,应对冲突 |
--ours |
三方合并时优先使用我们的版本 |
--theirs |
三方合并时优先使用对方的版本 |
--union |
合并所有版本(并集) |
--reject |
冲突时保留 .rej 文件 |
--build-fake-ancestor=<file> |
构建假祖先树,用于调试 |
路径处理选项:
| 选项 | 说明 |
|---|---|
-p<n> |
剥离路径前缀级数 |
--directory=<root> |
将补丁应用到指定子目录 |
--exclude=<path> |
排除指定路径 |
--include=<path> |
只包含指定路径 |
--unsafe-paths |
允许应用到工作区外的路径 |
空白字符处理:
| 选项 | 说明 |
|---|---|
--ignore-space-change / --ignore-whitespace |
忽略空白字符变化 |
--whitespace=<action> |
处理空白字符:nowarn、warn、fix、error、error-all |
其他选项:
| 选项 | 说明 |
|---|---|
--apply |
强制应用操作(默认) |
--no-add |
不添加新文件 |
-R / --reverse |
反向应用补丁(撤销) |
--allow-binary-patch-replacement / --binary |
允许二进制补丁替换 |
--recount |
忽略补丁中的行数信息,重新计算 |
--inaccurate-eof |
允许不精确的 EOF 处理 |
--allow-empty |
允许应用空补丁 |
-v / --verbose |
详细输出 |
-q / --quiet |
静默模式 |
全部使用示例
1. 基本应用
bash
# 应用补丁到工作区(不创建提交)
git apply patchfile.patch
# 在非 Git 仓库中也可使用
cd /some/non-git/directory
git apply /path/to/patchfile.patch
2. 检查补丁
bash
# 检查补丁是否可应用
git apply --check patchfile.patch
# 查看补丁的统计信息
git apply --stat patchfile.patch
# 查看补丁的数字统计(机器可读)
git apply --numstat patchfile.patch
# 查看补丁的摘要信息(创建、重命名、模式变更)
git apply --summary patchfile.patch
3. 应用到索引
bash
# 应用到索引和工作区(需在 Git 仓库中)
git apply --index patchfile.patch
# 只应用到索引(暂存区)
git apply --cached patchfile.patch
# 标记新文件为待添加
git apply --intent-to-add patchfile.patch
4. 反向应用(撤销补丁)
bash
# 反向应用,撤销补丁
git apply -R patchfile.patch
# 等价写法
git apply --reverse patchfile.patch
# 应用补丁时遇到已存在的内容 - 可反向处理
git apply --reverse fix.patch
5. 处理冲突
bash
# 尝试三方合并解决冲突
git apply --3way patchfile.patch
# 三方合并时使用我们的版本
git apply --3way --ours patchfile.patch
# 使用对方的版本
git apply --3way --theirs patchfile.patch
# 合并所有版本(并集)
git apply --3way --union patchfile.patch
# 冲突时保留 .rej 文件
git apply --reject patchfile.patch
# 之后可以手动处理 .rej 文件
6. 路径控制
bash
# 将补丁应用到子目录
git apply --directory=src/ patchfile.patch
# 剥离路径前缀(-p1 去掉第一级路径)
git apply -p2 patchfile.patch
# 只包含特定路径的文件
git apply --include=src/*.c patchfile.patch
# 排除特定路径的文件
git apply --exclude=docs/* patchfile.patch
# 允许应用到工作区外的路径(谨慎使用)
git apply --unsafe-paths patchfile.patch
7. 空白字符处理
bash
# 忽略空白字符差异
git apply --ignore-space-change patchfile.patch
# 自动修复空白字符错误
git apply --whitespace=fix patchfile.patch
# 将空白字符错误视为错误
git apply --whitespace=error patchfile.patch
# 只警告不处理
git apply --whitespace=warn patchfile.patch
# 不输出警告
git apply --whitespace=nowarn patchfile.patch
8. 二进制文件处理
bash
# 允许二进制补丁替换
git apply --binary patchfile.patch
# 允许二进制补丁替换(旧选项)
git apply --allow-binary-replacement patchfile.patch
9. 上下文和行数控制
bash
# 减少上下文匹配行数(更宽松的匹配)
git apply -C1 patchfile.patch
# 忽略补丁中的行数信息,重新计算
git apply --recount patchfile.patch
# 允许不精确的 EOF 处理
git apply --inaccurate-eof patchfile.patch
10. 组合使用
bash
# 先检查,再应用
git apply --check patchfile.patch && git apply patchfile.patch
# 将补丁应用到索引并提交
git apply --index patchfile.patch
git commit -m "Apply patch from contributor"
# 从标准输入读取补丁
git apply --index - < patchfile.patch
# 详细输出模式
git apply -v patchfile.patch
# 静默模式
git apply -q patchfile.patch
2.4 git send-email ------ 发送补丁邮件
git send-email 将补丁通过电子邮件发送出去,是邮件驱动开发工作流的核心工具。
子命令语法
bash
git send-email [<options>] (<file>|<directory>)...
git send-email [<options>] <format-patch-options>
git send-email --dump-aliases
git send-email --translate-aliases
全部选项详解
邮件撰写选项(Composing):
| 选项 | 说明 |
|---|---|
--annotate |
发送前审查和编辑每封邮件,默认值为 sendemail.annotate |
--bcc=<address> |
指定密送,可多次使用,默认 sendemail.bcc |
--cc=<address> |
指定抄送,可多次使用,默认 sendemail.cc |
--compose |
编写封面信(cover letter),打开编辑器编辑 |
--from=<address> |
指定发件人,否则使用 sendemail.from |
--in-reply-to=<message-id> |
使补丁成为某邮件的回复 |
--reply-to=<address> |
指定 Reply-To 地址 |
--subject=<string> |
指定邮件主题 |
--to=<address> |
指定收件人,可多次使用 |
补丁格式选项:
| 选项 | 说明 |
|---|---|
--attach |
以附件形式发送补丁 |
--inline |
以内嵌形式发送补丁 |
--no-attach |
禁用附件形式(默认) |
SMTP 配置选项:
| 选项 | 说明 |
|---|---|
--smtp-server=<server> |
SMTP 服务器地址 |
--smtp-server-port=<port> |
SMTP 服务器端口 |
--smtp-user=<user> |
SMTP 用户名 |
--smtp-pass=<password> |
SMTP 密码(不建议命令行传入) |
--smtp-encryption=<type> |
加密类型:tls、ssl |
--smtp-domain=<domain> |
指定 EHLO/HELO 域名 |
其他选项:
| 选项 | 说明 |
|---|---|
--dry-run |
模拟运行,不实际发送 |
-v / --verbose |
详细输出 |
-q / --quiet |
静默模式 |
--confirm=<mode> |
发送前确认模式:always、never、cc、compose、auto |
--dump-aliases |
显示邮件别名 |
--translate-aliases |
翻译邮件别名 |
--no-validate |
跳过补丁格式验证 |
--force-send |
即使没有收件人也强制发送 |
SMTP 配置示例
配置 Gmail:
bash
git config --global sendemail.smtpserver smtp.gmail.com
git config --global sendemail.smtpserverport 587
git config --global sendemail.smtpencryption tls
git config --global sendemail.smtpuser your@gmail.com
配置 Outlook.com:
bash
git config --global sendemail.smtpserver smtp-mail.outlook.com
git config --global sendemail.smtpserverport 587
git config --global sendemail.smtpencryption tls
常用发送设置:
bash
# 设置默认发件人
git config --global sendemail.from "Your Name <you@example.com>"
# 启用发送前确认
git config --global sendemail.confirm always
# 设置别名文件
git config --global sendemail.aliasfiletype mutt
git config --global sendemail.aliasfile ~/.mutt_aliases
全部使用示例
1. 基本发送
bash
# 发送单个补丁文件
git send-email --to=maintainer@example.com 0001-fix.patch
# 发送目录下的所有补丁
git send-email --to=maintainer@example.com patches/
# 发送最近 3 个提交的补丁
git send-email --to=maintainer@example.com -3
2. 多个收件人
bash
# 多个 To 和 Cc
git send-email --to=maintainer@example.com --to=co-maintainer@example.com \
--cc=reviewer@example.com --cc=testing@example.com patches/
# 添加密送
git send-email --bcc=others@example.com patches/
3. 编写封面信
bash
# 编写封面信
git send-email --compose --to=maintainer@example.com patches/
# 发送前会打开编辑器编写封面信
# 封面信中可以设置 From、To、Cc、Subject、Reply-To、In-Reply-To 等
4. 审查和编辑
bash
# 发送前审查每个补丁
git send-email --annotate --to=maintainer@example.com patches/
# 启用多文件编辑(每个补丁单独编辑)
git config sendemail.multiEdit true
git send-email --annotate --to=maintainer@example.com patches/
5. 版本号控制
bash
# 发送 v2 版本的补丁
git send-email -v2 --to=maintainer@example.com patches/
# 设置主题前缀
git send-email --subject-prefix="PATCH v3" --to=maintainer@example.com patches/
# 使用 RFC 前缀
git send-email --rfc --to=maintainer@example.com patches/
6. 回复邮件线程
bash
# 使补丁成为某个邮件的回复
git send-email --in-reply-to="<message-id@example.com>" patches/
# 设置 Reply-To 地址
git send-email --reply-to=reviewer@example.com patches/
7. 使用不同的 SMTP 服务器
bash
# 临时覆盖 SMTP 设置
git send-email --smtp-server=smtp.company.com \
--smtp-user=yourname \
--smtp-encryption=tls \
--to=maintainer@example.com patches/
8. 模拟运行
bash
# 模拟发送,不实际发送
git send-email --dry-run --to=maintainer@example.com patches/
# 详细模式,查看发送过程
git send-email --verbose --to=maintainer@example.com patches/
9. 发送前确认
bash
# 每次发送前都确认
git send-email --confirm=always --to=maintainer@example.com patches/
# 只在添加了 Cc 时确认
git send-email --confirm=cc --to=maintainer@example.com patches/
# 只在编写封面信时确认(默认)
git send-email --confirm=compose --to=maintainer@example.com patches/
10. 高级用法
bash
# 使用附件格式
git send-email --attach --to=maintainer@example.com patches/
# 内嵌格式
git send-email --inline --to=maintainer@example.com patches/
# 结合 format-patch 选项
git send-email --to=maintainer@example.com --cover-letter -3
# 使用别名文件
git send-email --to=maintainer --cc=reviewer patches/
# 显示别名
git send-email --dump-aliases
2.5 git request-pull ------ 生成拉取请求
git request-pull 生成一个拉取请求摘要,输出到标准输出,内容包含分支描述、变更摘要和拉取地址,便于向上游项目请求拉取变更。
命令语法
bash
git request-pull [-p] <start> <URL> [<end>]
参数说明
| 参数 | 说明 |
|---|---|
-p |
在输出中包含补丁文本 |
<start> |
起始提交,必须是上游已存在的提交 |
<URL> |
公共仓库的 URL |
<end> |
结束提交,默认 HEAD,可以是提交、分支或标签 |
<local>:<remote> |
当本地和远程分支名不同时使用此语法 |
全部使用示例
1. 基本用法
bash
# 生成拉取请求(基于 v1.0 到当前分支)
git request-pull v1.0 https://github.com/user/repo.git
# 输出示例:
# The following changes since commit abc1234:
# Release v1.0 (2024-01-01 10:00:00 +0800)
#
# are available in the Git repository at:
#
# https://github.com/user/repo.git master
#
# for you to fetch changes up to def5678:
#
# Add new feature (2024-01-15 14:30:00 +0800)
#
# ----------------------------------------------------------------
# 详细提交列表...
2. 指定结束提交
bash
# 指定结束提交为 feature 分支
git request-pull v1.0 https://github.com/user/repo.git feature/login
# 指定结束提交为具体哈希
git request-pull v1.0 https://github.com/user/repo.git abc1234
3. 包含补丁内容
bash
# 在输出中包含完整补丁
git request-pull -p v1.0 https://github.com/user/repo.git master
4. 处理分支名差异
bash
# 本地分支名为 master,远程分支名为 for-linus
git request-pull v1.0 https://github.com/user/repo.git master:for-linus
# 输出中的拉取地址会显示正确的远程分支名
5. 完整工作流示例
bash
# 1. 在本地完成开发
git checkout -b feature/new-ui
# ... 开发提交 ...
# 2. 推送到公共仓库
git push origin feature/new-ui
# 3. 生成拉取请求并发送给维护者
git request-pull main https://github.com/user/repo.git feature/new-ui | \
mail -s "Pull request: New UI feature" maintainer@example.com
6. 与邮件集成
bash
# 存储输出到文件
git request-pull -p origin/main https://github.com/user/repo.git feature > pull_request.txt
# 使用 git send-email 发送
git send-email --to=maintainer@example.com pull_request.txt
2.6 git patch-id ------ 生成补丁唯一标识
git patch-id 为补丁生成稳定的唯一标识符,基于补丁的 diff 内容(忽略空白和提交信息差异)。
使用示例
bash
# 为补丁文件生成 patch-id
git patch-id < 0001-fix.patch
# 输出格式: <patch-id> <commit-hash>
# abc1234... 9a8b7c6...
# 比较两个补丁是否等价(忽略提交信息)
git patch-id < patch1.patch > id1
git patch-id < patch2.patch > id2
diff id1 id2
应用场景
- 检测不同提交是否引入了相同的代码变更
- 在
git rebase中识别重复提交 - 避免重复应用相同的补丁
2.7 git format-patch 与 git am 的闭环协作
下图展示了社区协作中最常用的标准补丁工作流,覆盖了从补丁生成到应用的全部链路:
bash
# 补丁生成与邮件发送 (补丁制作者)
git checkout -b feature/bug-fix
# ... 开发并提交 ...
# 生成补丁文件并发送
git format-patch --cover-letter --to=maintainer@example.com -v1 origin/main..HEAD
git send-email --to=maintainer@example.com *.patch
# 补丁接收与应用 (维护者)
git checkout main
git pull origin main
git checkout -b review/bug-fix
# 下载或接收邮件补丁后应用
git am --signoff --3way /path/to/patches/*.patch
# 测试无误后合并
git checkout main
git merge --ff-only review/bug-fix
git push origin main
这个闭环涵盖了补丁工作的所有环节,每一步都有对应的 Git 命令支撑,确保协作流程完整、可追溯。
三、git am 与 git apply 详细对比
| 特性 | git am |
git apply |
|---|---|---|
| 输入格式 | mbox 邮箱格式(git format-patch 输出) |
普通 diff/patch 格式 |
| Git 仓库要求 | ✅ 必须在仓库中执行 | ❌ 可在非仓库中执行 |
| 自动提交 | ✅ 是,每个补丁生成一个提交 | ❌ 否,只修改工作区 |
| 保留提交元数据 | ✅ 保留作者、日期、提交信息 | ❌ 不保留 |
| 冲突处理机制 | --continue、--skip、--abort、--quit |
--reject、--3way、--ours、--theirs |
| 交互模式 | ✅ --interactive |
❌ 无 |
| 三方合并 | ✅ --3way |
✅ --3way |
| 签名支持 | ✅ --signoff、-S(GPG 签名) |
❌ 无 |
| 适用场景 | 接收邮件补丁、保留贡献者信息 | 快速测试补丁、非 Git 环境、精细控制 |
| 路径过滤 | ✅ --exclude、--include、--directory |
✅ --exclude、--include、--directory |
四、速查表
| 分类 | 命令 | 核心功能 | 关键选项 |
|---|---|---|---|
| 归档 | git archive |
生成代码快照归档 | --format、-o、--prefix、--add-file、--remote |
git bundle |
离线传输 Git 对象(备份) | create、verify、list-heads、unbundle |
|
| 补丁生成 | git format-patch |
生成邮件格式补丁 | -v、--to、--cc、--cover-letter、--rfc |
| 补丁应用 | git am |
应用邮箱补丁并提交 | --signoff、--3way、--continue、--skip、--abort |
git apply |
应用普通补丁(不提交) | --check、--index、--3way、-R |
|
| 补丁发送 | git send-email |
发送补丁邮件 | --to、--cc、--compose、--dry-run |
| 请求拉取 | git request-pull |
生成拉取请求摘要 | -p、<start>、<URL>、<end> |
| 辅助工具 | git patch-id |
生成补丁唯一标识 | 用于补丁去重检测 |
五、最佳实践建议
- 正式协作补丁 :使用
git format-patch+git am流程,完整保留贡献者元数据 - 快速测试补丁 :使用
git apply --check先验证兼容性,再用git apply应用或git apply -R撤销 - 邮件发送前 :务必用
--dry-run和--confirm=always检查收件人和补丁内容 - 远程仓库受限时 :
git bundle是最佳的离线传输替代方案 - 补丁版本管理 :使用
git format-patch -v<N>生成带版本号的补丁,便于维护者追溯迭代历史 - 仓库备份 :定期使用
git bundle create创建完整备份,或结合git rev-list做增量备份 - 跨团队合作 :团队间无法直接
git push/pull时,补丁流转是最安全、规范的选择,保持审计链完整
💡 特别注意 :
git diff生成的普通 diff 格式不包含提交元数据 ,只适用于git apply;而git format-patch生成的 mbox 格式补丁必须使用git am提交,才能保留作者信息和提交消息。