Git 邮件工作流完全指南:从补丁提交到自动化通知
前言
在现代软件开发中,尽管 GitHub、GitLab 等平台提供了便捷的 Pull Request 工作流,但基于邮件的补丁提交方式仍然是 Linux 内核、Git 本身以及众多成熟开源项目的标准协作模式。这种工作流程虽然学习曲线陡峭,但它提供了无可比拟的灵活性、可追溯性和去中心化特性。
本文将深入探讨 Git 邮件工作流的两大核心场景:使用 git send-email 提交补丁,以及通过 Git Hooks 实现自动化通知系统。
第一部分:使用 git send-email 提交 Linux 内核补丁
为什么使用邮件提交补丁?
邮件工作流在开源社区中具有独特优势:
- 去中心化协作:不依赖单一平台,任何人都可以通过邮件客户端参与讨论
- 完整的上下文保留:讨论和代码修改历史完整保存在邮件归档中
- 灵活的评审流程:支持逐行评论、多轮评审和异步协作
- 广泛的工具支持:与各种邮件客户端、归档系统无缝集成
- 低带宽友好:纯文本格式适合网络条件受限的环境
环境准备与工具安装
1. 安装必要的软件包
在基于 Debian/Ubuntu 的系统上:
# 安装 Git 及邮件发送工具
sudo apt-get update
sudo apt-get install git git-email
# 验证安装
git send-email --help
在 Fedora/RHEL 系统上:
sudo dnf install git git-email
在 macOS 上:
brew install git
# git-send-email 通常已包含在 git 中
2. 配置邮件发送参数
编辑 ~/.gitconfig 文件,配置 SMTP 服务器信息:
[user]
name = Your Name
email = your.email@example.com
[sendemail]
# SMTP 服务器配置
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = your.email@gmail.com
smtpserverport = 587
# 邮件线程配置
chainreplyto = false
# 自动确认配置
confirm = auto
# 抑制 CC 自己
suppresscc = self
3. Gmail 专用配置(推荐使用应用专用密码)
自 2022 年起,Gmail 要求使用应用专用密码而非账户密码。配置步骤:
- 访问 Google 账户安全设置
- 启用两步验证
- 生成应用专用密码
- 在
~/.gitconfig中添加密码(可选,否则每次发送时输入):
[sendemail]
smtppass = your-app-specific-password
安全提示:避免在配置文件中明文存储密码。考虑使用 Git 凭据管理器或在发送时手动输入。
4. 使用 OAuth2 认证(推荐方案)
对于 Gmail 用户,使用 Gmail API 和 OAuth2 是更安全的替代方案,可以避免 SMTP 泄露 IP 地址和 SPF 验证失败的问题:
# 安装 sendgmailapi 工具
go install github.com/google/sendgmailapi@latest
# 配置 OAuth2
$(go env GOPATH)/bin/sendgmailapi -setup
# 配置 Git 使用 sendgmailapi
git config sendemail.sendmailcmd "$(go env GOPATH)/bin/sendgmailapi"
准备和生成补丁
1. 代码质量检查
在提交前,务必运行代码风格检查工具:
# 对于 Linux 内核项目
./scripts/checkpatch.pl --strict your-file.c
# 运行内核编译测试
make allmodconfig
make -j$(nproc)
# 使用 sparse 进行静态分析
make C=1 CHECK=sparse
2. 提交代码并添加签名
# 添加修改的文件
git add path/to/modified/file.c
# 提交并自动添加 Signed-off-by
git commit -s
# 编写规范的提交信息
提交信息格式规范:
subsystem: Brief summary in imperative mood (50 chars max)
More detailed explanatory text, if necessary. Wrap it to about 72
characters. The blank line separating the summary from the body is
critical (unless you omit the body entirely).
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Use a hanging indent
Fixes: 1234567890ab ("previous commit title")
Reported-by: Reporter Name <reporter@email.com>
Tested-by: Tester Name <tester@email.com>
Reviewed-by: Reviewer Name <reviewer@email.com>
Signed-off-by: Your Name <your.email@example.com>
3. 生成补丁文件
# 生成最近 1 次提交的补丁
git format-patch -1
# 生成最近 n 次提交的补丁
git format-patch -n
# 生成补丁系列并包含封面信
git format-patch -3 --cover-letter \
--subject-prefix="PATCH v2" \
--output-directory=/tmp/patches/
# 为特定版本生成补丁
git format-patch v6.1..HEAD --output-directory=/tmp/patches/
# 添加基线信息(便于 CI 系统测试)
git format-patch --base=auto HEAD^
常用选项说明:
--cover-letter: 生成0000-cover-letter.patch封面信,用于描述补丁系列的整体目的--subject-prefix: 自定义主题前缀,如 “PATCH v2” 表示第二版补丁--in-reply-to: 指定回复的 Message-ID,用于补丁版本迭代--thread: 将补丁组织为邮件线程--base: 添加基础提交信息,帮助评审者和 CI 系统确定补丁应用基准
4. 编辑封面信
编辑生成的 0000-cover-letter.patch:
Subject: [PATCH v2 0/3] Improve memory management in XYZ subsystem
This patch series improves memory management in the XYZ subsystem by:
1. Fixing memory leak in error path
2. Optimizing buffer allocation strategy
3. Adding comprehensive test coverage
Changes in v2:
- Addressed review comments from Reviewer Name
- Fixed off-by-one error in patch 2/3
- Added additional test cases suggested by Tester Name
Performance impact:
Benchmarks show 15% reduction in memory usage with negligible
performance overhead (<1% CPU impact).
Testing:
All patches have been tested on x86_64, ARM64, and PowerPC platforms.
No regressions observed in existing test suites.
查找维护者和评审人员
Linux 内核项目提供了 get_maintainer.pl 脚本来识别相关维护者和邮件列表:
# 找到补丁的维护者
./scripts/get_maintainer.pl 0001-your-patch.patch
# 输出示例:
# John Doe <john@example.com> (maintainer:SUBSYSTEM X)
# Jane Smith <jane@example.com> (reviewer:SUBSYSTEM X)
# linux-kernel@vger.kernel.org (open list:SUBSYSTEM X)
# subsystem-x@lists.example.com (subscriber list:SUBSYSTEM X)
# 不显示角色信息,便于直接用于 git send-email
./scripts/get_maintainer.pl --norolestats 0001-your-patch.patch
发送补丁
基本发送命令
# 发送单个补丁
git send-email 0001-your-patch.patch \
--to=maintainer@example.com \
--cc=reviewer@example.com \
--cc=linux-kernel@vger.kernel.org
# 发送补丁系列
git send-email *.patch \
--to=maintainer@example.com \
--cc=reviewer@example.com
# 使用 get_maintainer.pl 自动确定收件人
git send-email \
--cc-cmd='./scripts/get_maintainer.pl --norolestats' \
*.patch
# 添加自己到 CC 以接收回复
git send-email --cc=yourself@example.com *.patch
高级发送选项
# 使用 --annotate 在发送前编辑每个补丁
git send-email --annotate *.patch
# 在编辑器中的操作:
# :wn - 保存当前补丁并编辑下一个
# :wq - 保存当前补丁并完成编辑
# :q! - 放弃当前补丁的修改
# 发送补丁的新版本(回复之前的讨论线程)
git send-email \
--in-reply-to="<message-id-of-previous-version@example.com>" \
--subject-prefix="PATCH v3" \
*.patch
# 仅发送封面信(用于宣布补丁系列)
git send-email --compose-only \
--to=mailing-list@example.com
# 在发送前进行测试(不实际发送邮件)
git send-email --dry-run *.patch
使用 identity 管理多个配置
当需要为不同项目配置不同的发送参数时,可以使用 sendemail.<identity> 配置段:
# ~/.gitconfig
# Linux 内核补丁配置
[sendemail "kernel"]
smtpserver = smtp.gmail.com
smtpuser = kernel-dev@gmail.com
smtpencryption = tls
smtpserverport = 587
to = linux-kernel@vger.kernel.org
cc = maintainer@kernel.org
# 公司内部项目配置
[sendemail "company"]
smtpserver = smtp.company.com
smtpuser = employee@company.com
smtpencryption = tls
smtpserverport = 587
to = dev-team@company.com
# PostgreSQL 项目配置
[sendemail "pgsql"]
smtpserver = smtp.gmail.com
smtpuser = your.email@gmail.com
smtpencryption = tls
smtpserverport = 587
to = pgsql-hackers@postgresql.org
使用特定配置发送:
# 使用内核项目配置
git send-email --identity=kernel *.patch
# 使用公司项目配置
git send-email --identity=company *.patch
补丁提交后的跟进
1. 响应评审意见
补丁提交后,通常在 2-3 周内会收到评审意见。回复评审时应注意:
- 及时响应:尽量在收到评审意见后一周内回复
- 礼貌专业:即使评审意见比较严厉,也要保持礼貌和专业
- 逐点回应:使用内联回复方式,逐条回应评审意见
- 解释变更:说明你采纳了哪些建议,以及为什么某些建议没有采纳
回复格式示例:
On Mon, Nov 09, 2025 at 10:00:00AM +0800, Reviewer Name wrote:
> This function could be simplified by using existing helper
> function xyz_helper().
Good catch! I've refactored the code to use xyz_helper() in v3.
> The error handling path seems incorrect here.
You're right. I've fixed this in v3 by adding proper cleanup
before returning the error code.
> Consider adding a comment explaining the locking strategy.
Added a detailed comment block explaining the lock ordering
and rationale in v3.
2. 提交新版本补丁
# 修改代码后重新生成补丁
git format-patch -3 --cover-letter \
--subject-prefix="PATCH v3" \
--in-reply-to="<original-cover-message-id@example.com>" \
--output-directory=/tmp/patches-v3/
# 在封面信中说明变更
# 编辑 0000-cover-letter.patch,添加版本变更日志
3. 等待合并
补丁被接受后,它首先会进入维护者的树,然后在合并窗口期间被发送给 Linus Torvalds。整个过程可能需要几周到几个月不等。
常见问题与解决方案
问题 1: Gmail 包装长行
Gmail 会在第 78 个字符处自动换行,可能破坏补丁格式。解决方案:
- 使用
git send-email而不是 Gmail 网页界面 - 使用上述提到的 OAuth2 方法
- 配置 Gmail 的”无格式文本”模式
问题 2: 邮件客户端修改补丁格式
许多邮件客户端会修改空格、制表符或换行符。解决方案:
# 始终使用 git send-email
git send-email *.patch
# 避免直接从邮件客户端发送补丁
问题 3: 补丁无法应用
补丁必须基于特定版本的内核。确保:
# 基于最新的主线或稳定版本
git checkout v6.6 # 或最新的稳定标签
git checkout -b my-feature
# 在生成补丁前进行 rebase
git fetch origin
git rebase origin/master
第二部分:使用 Git Hooks 实现自动化通知
Git Hooks 概述
Git Hooks 是在特定 Git 事件发生时自动执行的脚本,分为客户端钩子和服务器端钩子两大类。
Hooks 的类型与触发时机
客户端 Hooks(本地仓库):
pre-commit: 提交前执行,用于代码检查、测试prepare-commit-msg: 准备提交信息时执行commit-msg: 提交信息编写后执行,用于验证消息格式post-commit: 提交后执行,用于通知或日志记录pre-push: 推送前执行,用于集成测试post-checkout: 检出后执行,用于环境配置post-merge: 合并后执行,用于清理或通知
服务器端 Hooks(远程仓库):
pre-receive: 接收推送前执行,用于访问控制、策略检查update: 更新引用时执行,针对每个分支post-receive: 接收推送后执行,用于部署、通知post-update: 更新完成后执行,用于维护任务
配置 post-receive Hook 实现提交通知
1. Hook 存储位置与基本配置
Hooks 存储在仓库的 .git/hooks 目录中。对于裸仓库(bare repository):
# 进入服务器端的裸仓库
cd /path/to/repo.git
# 查看示例 hooks
ls -la hooks/
# 输出:
# post-receive.sample
# pre-receive.sample
# update.sample
# ...
自定义 hooks 目录:
# 配置自定义 hooks 路径
git config core.hooksPath /path/to/custom/hooks
# 查看当前配置
git config core.hooksPath
2. 配置邮件通知参数
编辑仓库的 .git/config 或 config 文件(裸仓库):
[hooks]
# 显示提交的命令
showrev = "git show -C %s; echo"
# 邮件主题前缀
emailprefix = "[MyProject] "
# 收件人列表(逗号分隔)
mailinglist = dev@example.com,team-lead@example.com,ci@example.com
# 发件人地址
envelopesender = git-server@example.com
# 邮件发送方式(sendmail 或 SMTP)
emailmaxlines = 500
# 差异显示限制
diffopts = "-M -C --stat --summary --find-copies-harder"
3. 配置项目描述
编辑 .git/description 文件(或裸仓库的 description 文件):
# 设置项目名称
echo "MyProject - Core Backend Services" > .git/description
# 这将影响邮件主题的显示:
# [MyProject] MyProject - Core Backend Services branch master updated. abcd123..efgh456
4. 创建 post-receive Hook
创建并编辑 .git/hooks/post-receive:
#!/bin/bash
#
# Git post-receive hook for sending commit notifications
#
# This hook sends email notifications for every push received
# by the repository. It includes commit details, diffs, and
# affected files.
# 读取 Git 配置
PROJECT=$(cat $(git rev-parse --git-dir)/description 2>/dev/null || echo "UNNAMED PROJECT")
PREFIX=$(git config hooks.emailprefix || echo "[Git] ")
RECIPIENTS=$(git config hooks.mailinglist)
FROM=$(git config hooks.envelopesender || echo "git@$(hostname)")
SHOWREV=$(git config hooks.showrev || echo "git show -C %s")
# 检查必要配置
if [ -z "$RECIPIENTS" ]; then
echo "Error: hooks.mailinglist not configured"
exit 1
fi
# 函数:生成提交邮件
generate_email() {
local oldrev=$1
local newrev=$2
local refname=$3
# 提取分支名
branch=$(echo "$refname" | sed 's#refs/heads/##')
# 获取提交列表
if [ "$oldrev" = "0000000000000000000000000000000000000000" ]; then
# 新分支
commits=$(git rev-list "$newrev")
action="created"
elif [ "$newrev" = "0000000000000000000000000000000000000000" ]; then
# 删除分支
action="deleted"
return
else
# 更新分支
commits=$(git rev-list "$oldrev..$newrev")
action="updated"
fi
# 计数
commit_count=$(echo "$commits" | wc -l)
# 生成邮件主题
subject="${PREFIX}${PROJECT} branch ${branch} ${action}. ${oldrev:0:8}..${newrev:0:8}"
# 生成邮件正文
{
echo "Repository: $PROJECT"
echo "Branch: $branch"
echo "Action: $action"
echo "Commits: $commit_count"
echo ""
echo "New commits:"
echo "============"
echo ""
# 显示每个提交
for commit in $commits; do
eval "$SHOWREV $commit"
echo ""
echo "---"
echo ""
done
# 显示总体统计
echo ""
echo "Summary of changes:"
echo "==================="
git diff --stat "$oldrev..$newrev"
} | mail -s "$subject" -r "$FROM" $RECIPIENTS
}
# 主循环:处理每个引用更新
while read oldrev newrev refname; do
# 只处理分支更新(忽略标签等)
if [ "${refname#refs/heads/}" != "$refname" ]; then
generate_email "$oldrev" "$newrev" "$refname"
fi
done
exit 0
赋予可执行权限:
chmod +x .git/hooks/post-receive
# 对于裸仓库
chmod +x hooks/post-receive
5. 高级 post-receive Hook 示例
包含更多功能的增强版本:
#!/bin/bash
#
# Enhanced post-receive hook with multiple notification channels
# Supports: Email, Slack, CI/CD integration
set -euo pipefail
# 配置读取
PROJECT=$(cat $(git rev-parse --git-dir)/description 2>/dev/null || echo "UNNAMED")
EMAIL_LIST=$(git config hooks.mailinglist || echo "")
SLACK_WEBHOOK=$(git config hooks.slackwebhook || echo "")
CI_TRIGGER_URL=$(git config hooks.citrigger || echo "")
# 函数:发送 Slack 通知
notify_slack() {
local branch=$1
local author=$2
local message=$3
local commit=$4
if [ -n "$SLACK_WEBHOOK" ]; then
payload=$(cat <<EOF
{
"text": "New commit to ${PROJECT}",
"attachments": [{
"color": "good",
"fields": [
{"title": "Branch", "value": "${branch}", "short": true},
{"title": "Author", "value": "${author}", "short": true},
{"title": "Commit", "value": "${commit:0:8}", "short": true},
{"title": "Message", "value": "${message}", "short": false}
]
}]
}
EOF
)
curl -X POST -H 'Content-type: application/json' \
--data "$payload" "$SLACK_WEBHOOK" || true
fi
}
# 函数:触发 CI/CD
trigger_ci() {
local branch=$1
local commit=$2
if [ -n "$CI_TRIGGER_URL" ]; then
curl -X POST "$CI_TRIGGER_URL" \
-H "Content-Type: application/json" \
-d "{\"branch\": \"$branch\", \"commit\": \"$commit\"}" || true
fi
}
# 函数:发送详细邮件通知
send_email_notification() {
local oldrev=$1
local newrev=$2
local refname=$3
[ -z "$EMAIL_LIST" ] && return
branch=$(echo "$refname" | sed 's#refs/heads/##')
if [ "$oldrev" = "0000000000000000000000000000000000000000" ]; then
commits=$(git rev-list "$newrev")
else
commits=$(git rev-list "$oldrev..$newrev")
fi
{
echo "Project: $PROJECT"
echo "Branch: $branch"
echo "Pushed at: $(date)"
echo ""
echo "Commit details:"
echo "==============="
for commit in $(echo "$commits" | tac); do
author=$(git show -s --format='%an <%ae>' "$commit")
date=$(git show -s --format='%cd' "$commit")
subject=$(git show -s --format='%s' "$commit")
echo ""
echo "Commit: $commit"
echo "Author: $author"
echo "Date: $date"
echo "Subject: $subject"
echo ""
git show --stat "$commit"
echo ""
echo "---"
# 同时发送 Slack 通知
notify_slack "$branch" "$author" "$subject" "$commit"
done
} | mail -s "[${PROJECT}] $branch updated" $EMAIL_LIST
}
# 主处理循环
while read oldrev newrev refname; do
if [ "${refname#refs/heads/}" != "$refname" ]; then
branch=$(echo "$refname" | sed 's#refs/heads/##')
echo "Processing push to $branch..."
# 发送邮件通知
send_email_notification "$oldrev" "$newrev" "$refname" &
# 触发 CI/CD
trigger_ci "$branch" "$newrev" &
# 仅为主分支触发部署
if [ "$branch" = "master" ] || [ "$branch" = "main" ]; then
echo "Main branch updated, considering deployment..."
# 在这里添加部署逻辑
fi
fi
done
# 等待后台任务完成
wait
echo "All notifications sent successfully"
exit 0
其他实用 Hooks 示例
pre-commit: 代码质量检查
#!/bin/bash
# .git/hooks/pre-commit
echo "Running pre-commit checks..."
# 检查代码风格
if command -v black >/dev/null 2>&1; then
black --check . || {
echo "Code style check failed. Run 'black .' to fix."
exit 1
}
fi
# 运行 linter
if command -v pylint >/dev/null 2>&1; then
pylint src/ || {
echo "Linting failed. Fix errors before committing."
exit 1
}
fi
# 运行单元测试
if [ -f "pytest.ini" ]; then
pytest tests/ --quiet || {
echo "Tests failed. Fix before committing."
exit 1
}
fi
echo "All pre-commit checks passed!"
exit 0
commit-msg: 验证提交信息格式
#!/bin/bash
# .git/hooks/commit-msg
commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")
# 检查提交信息格式: subsystem: description
if ! echo "$commit_msg" | grep -qE "^[a-z0-9_/-]+: .+"; then
echo "Error: Commit message must follow format 'subsystem: description'"
echo "Examples:"
echo " api: add new user authentication endpoint"
echo " database: optimize query performance"
exit 1
fi
# 检查提交信息长度
first_line=$(echo "$commit_msg" | head -n1)
if [ ${#first_line} -gt 72 ]; then
echo "Error: First line of commit message is too long (max 72 chars)"
exit 1
fi
# 检查是否包含问题编号(可选)
if ! echo "$commit_msg" | grep -qE "(#[0-9]+|Fixes:|Closes:)"; then
echo "Warning: Consider adding issue reference (#123 or Fixes: #123)"
fi
exit 0
pre-push: 推送前检查
#!/bin/bash
# .git/hooks/pre-push
protected_branch='master'
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
# 防止直接推送到主分支
if [ "$current_branch" = "$protected_branch" ]; then
echo "Error: Direct push to $protected_branch is not allowed."
echo "Please create a feature branch and submit a pull request."
exit 1
fi
# 运行集成测试
if [ -f "run_integration_tests.sh" ]; then
echo "Running integration tests..."
./run_integration_tests.sh || {
echo "Integration tests failed. Fix before pushing."
exit 1
}
fi
exit 0
Hook 的版本控制与团队共享
由于 .git/hooks 目录不受版本控制,需要通过其他方式共享 hooks:
方法 1: 在项目根目录创建 hooks 目录
# 项目结构
project/
├── .git/
├── .githooks/ # 共享的 hooks
│ ├── pre-commit
│ ├── commit-msg
│ └── pre-push
├── scripts/
│ └── install-hooks.sh
└── README.md
# install-hooks.sh 内容
#!/bin/bash
HOOKS_DIR="$(git rev-parse --show-toplevel)/.githooks"
GIT_HOOKS_DIR="$(git rev-parse --git-dir)/hooks"
for hook in "$HOOKS_DIR"/*; do
hook_name=$(basename "$hook")
ln -sf "$hook" "$GIT_HOOKS_DIR/$hook_name"
chmod +x "$GIT_HOOKS_DIR/$hook_name"
echo "Installed hook: $hook_name"
done
方法 2: 配置全局 hooks 路径
# 配置仓库使用自定义 hooks 目录
git config core.hooksPath .githooks
# 或在 .git/config 中添加:
[core]
hooksPath = .githooks
Hook 最佳实践
实施 Git hooks 时应遵循以下最佳实践:
- 保持脚本简洁高效:Hooks 会影响 Git 操作速度,避免耗时操作
- 提供清晰的错误信息:失败时给出具体原因和修复建议
- 使脚本可配置:通过
git config读取配置,提高灵活性 - 处理错误情况:使用
set -e和适当的错误处理 - 文档化:在 hook 脚本中添加注释,说明用途和配置选项
- 提供跳过机制:允许在特殊情况下跳过 hooks(如使用
--no-verify) - 充分测试:在部署到生产环境前进行测试
- 版本控制:将 hooks 模板纳入版本控制,便于团队共享
总结与最佳实践
Git 邮件工作流的优势
- 专业性:这是 Linux 内核等顶级开源项目的标准流程
- 灵活性:不依赖特定平台,支持离线工作
- 可审计性:完整的讨论和代码变更历史
- 包容性:降低参与门槛,无需平台账号
成功提交补丁的关键要素
- 遵循项目规范:学习并遵守目标项目的编码标准和提交规范
- 充分测试:在提交前进行全面测试,包括边界条件
- 清晰的沟通:编写详尽的提交信息和补丁说明
- 及时响应:积极回应评审意见,保持礼貌和专业
- 持续改进:根据反馈不断改进代码质量
Git Hooks 的应用场景
- 自动化测试:提交前运行单元测试和代码检查
- 规范执行:强制执行代码风格和提交信息格式
- 通知系统:自动通知团队成员代码变更
- CI/CD 集成:触发持续集成和部署流程
- 审计日志:记录所有 Git 操作用于合规性审计
进阶学习资源
附录:快速参考
git send-email 常用命令
# 生成补丁
git format-patch -n --cover-letter --subject-prefix="PATCH v2"
# 检查补丁
./scripts/checkpatch.pl *.patch
# 查找维护者
./scripts/get_maintainer.pl *.patch
# 发送补丁
git send-email --to=maintainer@example.com *.patch
# 使用特定配置
git send-email --identity=kernel *.patch
常用 Git 配置
# 设置用户信息
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
# 配置 sendemail
git config --global sendemail.smtpserver smtp.gmail.com
git config --global sendemail.smtpserverport 587
git config --global sendemail.smtpencryption tls
# 配置 hooks 路径
git config core.hooksPath .githooks
邮件回复礼仪
- 使用内联(interleaved)回复,不要顶部回复(top-posting)
- 引用相关上下文,但不要过度引用
- 每行限制在 72-80 字符
- 使用纯文本格式,避免 HTML 邮件
- 及时回应,但不要在匆忙中回复
作者注:本文基于 Git 2.43+ 和 Linux 6.6 内核的实践经验编写。工作流程和命令可能因项目而异,请参考各项目的具体贡献指南。
版权声明:本文采用 CC BY-SA 4.0 许可协议。转载请注明出处并保留本声明。
更新日志:
- 2025-11-09: 初始版本发布
- 包含 git send-email 完整指南和 Git Hooks 实践


