Git 历史邮箱统一 + 推送 GitHub 后的远程分支处理指南
详情:GitHub 的 contribution(贡献记录)是通过提交时的邮箱地址识别的。如果你在提交历史中使用了旧邮箱,而这个邮箱没有绑定到你现在的 GitHub 账户,GitHub 就不会将其算作你的贡献。
场景:使用
git filter-repo
统一 Git 历史邮箱,重写历史后强推 GitHub,修复 (旧地址删除后) contributions 丢失的问题,并恢复远程分支跟踪。
❓ 为什么使用 git filter-repo
为了统一历史记录中的邮箱地址,我们需要重写 Git 提交历史。
而 git filter-repo
是目前 Git 官方推荐的历史重写工具,优点:
🚀 速度快(比
filter-branch
快几十倍)✅ 支持
.mailmap
映射(批量替换作者/邮箱)💡 更安全、更易用
🔧 如何安装 git filter-repo
📦 macOS(推荐 Homebrew 安装):
brew install git-filter-repo
🐧 Ubuntu / Debian:
sudo apt install git-filter-repo
🪟 Windows(Git Bash 或 Python 环境):
pip install git-filter-repo
或前往 GitHub Release 页面下载:
👉 https://github.com/newren/git-filter-repo
📄 什么是 .mailmap
?
.mailmap
是 Git 提供的一个“身份映射”文件,它可以让你统一不同邮箱或用户名的提交记录,例如:
Git用户名 <邮箱A@163.com> <邮箱B@qq.com>
Git用户名 <邮箱A@163.com> <94172257+GitHub-Name@users.noreply.github.com>
当配合 git filter-repo --use-mailmap
使用时,它可以批量将旧提交中的作者名/邮箱替换为你指定的目标值,适合用于:
修复 GitHub contributions 不显示的问题
统一多设备提交产生的不同用户名
🎯 目标说明
将 Git 提交历史中的多个旧邮箱统一为:
邮箱A@163.com
姓名统一为:
Git用户名
修改完成后强制推送至 GitHub
解决重写历史后远程分支丢失跟踪的问题
🧩 旧邮箱列表
旧邮箱 | 来源说明 |
---|---|
邮箱B@qq.com | 最初本地提交邮箱 |
94172257+GitHub-Name@users.noreply.github.com | GitHub 网页提交生成 |
DESKTOP-F0C5U3P\hfs | Windows 本地自动生成用户名 |
🛠 步骤 1:创建 .mailmap
文件并重写提交历史
cat > .mailmap <<EOF
Git用户名 <邮箱A@163.com> <邮箱B@qq.com>
Git用户名 <邮箱A@163.com> <94172257+GitHub-Name@users.noreply.github.com>
Git用户名 <邮箱A@163.com> DESKTOP-F0C5U3P\hfs <邮箱A@163.com>
EOF
git filter-repo --use-mailmap --force
🔍 步骤 2:验证修改是否生效
git log --format='%an <%ae>' | sort | uniq -c
应输出:
次数 Git 用户名 <邮箱A@163.com>
🌐 步骤 3:重新添加远程仓库地址
由于 git filter-repo 会移除 origin,需要重新添加:
git remote add origin git@github.com:GitHub-Name/repositorie-name.git
🚀 步骤 4:强制推送到 GitHub
⚠️ 警告:这将覆盖远程仓库的历史,请确认无其他协作者或已备份。
git push --force --all
🔁 步骤 5:恢复本地分支与远程分支的跟踪关系
✅ 方法 1:使用 VS Code
打开左侧 Source Control 面板
点击 "发布分支" 按钮(Publish branch)
✅ 方法 2:使用 SourceTree
打开 SourceTree ➝ 选择本地分支
右键 ➝ 设置跟踪远程分支 ➝ 选择 origin/main 或 origin/master
⚠️ 注意事项(必须处理)
项目操作建议 未提交改动 提交或 stash 后再操作 暂存中的改动 清理或提交 .git/logs 等历史缓存 filter-repo 会自动清理,不建议中途操作不完整
✅ 最终效果验证
git log --format='%an <%ae>' | sort | uniq -c
输出应为:
次数 Git 用户名 <邮箱A@163.com>
并在 GitHub 上恢复正确的 contribution 显示。
🛠 Git 批量邮箱统一工具脚本(含自动推送)
需求:如果你有多个仓库需要做一样的操作,可以使用自动化脚本批量处理,比如遍历 ~/学习项目/ 下所有仓库。
适用场景:批量统一多个 Git 仓库的历史提交邮箱和作者名,自动强推到 GitHub,解决 contributions 丢失问题。
📦 脚本功能
✅ 扫描指定目录下的所有 Git 仓库
✅ 检测是否包含旧邮箱/旧用户名
✅ 自动生成
.mailmap
文件进行邮箱替换✅ 使用
git filter-repo
重写历史✅ 自动添加远程仓库
origin
✅ 自动强推所有分支
✅ 支持控制是否推送(开关配置)
📂 配置说明(脚本顶部)
BASE_DIR="$HOME/学习项目" # 本地项目集合目录
GITHUB_USER="GitHub-Name" # GitHub 用户名
GITHUB_NAMESPACE="GitHub-Name" # GitHub 用户名或组织名
NEW_NAME="Git用户名" # 统一后作者名
NEW_EMAIL="邮箱A@163.com" # 统一后邮箱
OLD_EMAILS=("邮箱B@qq.com" "94172257+GitHub-Name@users.noreply.github.com") # 所有旧邮箱
OLD_NAMES=("DESKTOP-F0C5U3P\hfs") # 所有旧用户名(Windows 会这样)
AUTO_PUSH=true # 是否自动推送到远程
🧑💻 使用方法
① 将以下脚本保存为 fix-git-history.sh
#!/bin/bash
# ========== 配置项 ==========
BASE_DIR="$HOME/学习项目"
GITHUB_USER="Hefengshun"
GITHUB_NAMESPACE="Hefengshun"
NEW_NAME="何风顺"
NEW_EMAIL="he15518191739@163.com"
OLD_EMAILS=("511385442@qq.com" "94172257+Hefengshun@users.noreply.github.com")
OLD_NAMES=("DESKTOP-F0C5U3P\hfs")
AUTO_PUSH=true
# ============================
log_info() {
echo -e "\033[1;34m[INFO]\033[0m $1"
}
log_warn() {
echo -e "\033[1;33m[WARN]\033[0m $1"
}
log_done() {
echo -e "\033[1;32m[DONE]\033[0m $1"
}
process_repo() {
repo_path="$1"
repo_name=$(basename "$repo_path")
echo -e "\n=============================="
log_info "正在处理仓库:$repo_name"
cd "$repo_path" || return
if [ ! -d ".git" ]; then
log_warn "跳过:不是 Git 仓库"
return
fi
match=false
for email in "${OLD_EMAILS[@]}"; do
if git log --format='%ae' | grep -q "$email"; then
match=true
fi
done
for name in "${OLD_NAMES[@]}"; do
if git log --format='%an' | grep -q "$name"; then
match=true
fi
done
if [ "$match" = false ]; then
log_info "无旧邮箱记录,跳过"
return
fi
log_info "创建 .mailmap..."
echo "$NEW_NAME <$NEW_EMAIL> <${OLD_EMAILS[0]}>" > .mailmap
for email in "${OLD_EMAILS[@]:1}"; do
echo "$NEW_NAME <$NEW_EMAIL> <$email>" >> .mailmap
done
for name in "${OLD_NAMES[@]}"; do
echo "$NEW_NAME <$NEW_EMAIL> $name <$NEW_EMAIL>" >> .mailmap
done
log_info "正在重写历史..."
git filter-repo --use-mailmap --force
echo "📄 当前提交记录作者邮箱:"
git log --format='%an <%ae>' | sort | uniq -c
if [ "$AUTO_PUSH" = true ]; then
git_remote_url="git@github.com:$GITHUB_NAMESPACE/$repo_name.git"
git remote remove origin 2>/dev/null
git remote add origin "$git_remote_url"
log_info "设置远程仓库:$git_remote_url"
git push --force --all
log_done "推送成功:$repo_name"
else
log_warn "未推送,请手动执行:git push --force --all"
fi
echo -e "=============================="
}
log_info "开始扫描目录:$BASE_DIR"
for repo in "$BASE_DIR"/*; do
if [ -d "$repo" ]; then
process_repo "$repo"
fi
done
log_done "所有仓库处理完成!"
# 清理 .mailmap 文件
rm -f .mailmap
② 赋予执行权限
chmod +x ~/fix-git-history.sh
③ 运行脚本
~/fix-git-history.sh
📌 注意事项
脚本会删除并重建
.git/logs
和远程绑定,请 提前备份重要仓库强推会覆盖 GitHub 上历史,请确保你是唯一协作者,或所有人同意此操作
提交前请处理所有 未提交的变更与暂存区
✅ 最终验证
git log --format='%an <%ae>' | sort | uniq -c
输出应为:
N Git用户名 <邮箱A@163.com>
说明你已经成功统一所有提交记录 🥳