在pre-push钩子中同步
这个方案在推送前先拉取上游最新代码,确保本地代码基于最新版本。

创建 pre-push 钩子
# 在 .git/hooks/ 目录下创建 pre-push 文件 touch .git/hooks/pre-push chmod +x .git/hooks/pre-push
pre-push 脚本内容
#!/bin/bash
# 获取当前分支名
current_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
# 只对 master/main 分支执行
if [ "$current_branch" = "master" ] || [ "$current_branch" = "main" ]; then
echo "🔄 正在同步上游最新代码..."
# 保存当前状态
git stash --include-untracked 2>/dev/null || true
# 拉取最新代码
git fetch upstream
git rebase upstream/"$current_branch"
if [ $? -ne 0 ]; then
echo "❌ 合并冲突,已停止推送"
git stash pop 2>/dev/null || true
exit 1
fi
# 恢复暂存的工作
git stash pop 2>/dev/null || true
echo "✅ 上游代码同步完成"
fi
exit 0
在post-checkout钩子中同步
切换分支时自动同步,适合多分支协作场景。
创建 post-checkout 钩子
touch .git/hooks/post-checkout chmod +x .git/hooks/post-checkout
post-checkout 脚本内容
#!/bin/bash
# 参数说明: $1=旧HEAD, $2=新HEAD, $3=是否分支切换(1=是, 0=否)
if [ "$3" = "1" ]; then
new_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
echo "🌿 切换到分支: $new_branch"
echo "🔄 正在同步上游代码..."
# 确保有上游分支配置
if git rev-parse --abbrev-ref @{upstream} >/dev/null 2>&1; then
git fetch upstream
git rebase upstream/"$new_branch"
if [ $? -ne 0 ]; then
echo "⚠️ 同步失败,请手动处理"
exit 1
fi
echo "✅ 上游代码同步完成"
else
echo "ℹ️ 未配置上游分支,跳过同步"
fi
fi
exit 0
在post-merge钩子中同步
合并操作完成后自动同步到上游分叉点。
创建 post-merge 钩子
touch .git/hooks/post-merge chmod +x .git/hooks/post-merge
post-merge 脚本内容
#!/bin/bash
current_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
# 跳过从上游拉取后的自动合并
if [ -f .git/MERGE_HEAD ]; then
exit 0
fi
echo "🔄 合并完成,检查上游更新..."
# 获取上游最新分支指向
upstream_ref="upstream/$current_branch"
if git rev-parse --verify "$upstream_ref" >/dev/null 2>&1; then
# 检查是否有新的提交
behind=$(git rev-list --count HEAD.."$upstream_ref" 2>/dev/null)
if [ "$behind" -gt 0 ]; then
echo "ℹ️ 上游有 $behind 个新提交"
read -p "是否现在同步? (y/N): " response
if [ "$response" = "y" ] || [ "$response" = "Y" ]; then
git rebase "$upstream_ref"
echo "✅ 同步完成"
fi
else
echo "✅ 已是最新"
fi
fi
exit 0
高级版本:多场景智能同步脚本
这是一个更完整的解决方案,可适配多个钩子:
#!/bin/bash
# ===== 配置 =====
UPSTREAM_REMOTE="upstream"
ALLOW_DIRTY_WORK_TREE=true # 是否允许在修改工作区时同步
AUTO_STASH=true # 是否自动暂存修改
INTERACTIVE_MODE=false # 交互模式
# ===== 核心函数 =====
sync_upstream() {
local branch=${1:-$(git symbolic-ref --short HEAD 2>/dev/null)}
local source=${2:-"pre-push"}
# 跳过保护分支列表
local protected_branches="develop|test|staging"
if echo "$branch" | grep -Eq "^($protected_branches)$"; then
echo "ℹ️ 保护分支 $branch,跳过自动同步"
return 0
fi
# 检查远程是否可访问
if ! git remote get-url "$UPSTREAM_REMOTE" >/dev/null 2>&1; then
echo "⚠️ 未配置上游远程: $UPSTREAM_REMOTE"
return 0
fi
echo "=== 🚀 开始上游同步 ($source) ==="
echo "分支: $branch"
# 保存当前工作目录状态
local stashed=false
if [ "$AUTO_STASH" = true ] && ! git diff-index --quiet HEAD -- 2>/dev/null; then
echo "📦 暂存当前修改..."
git stash push -m "auto-sync-$(date +%s)"
stashed=true
fi
# 拉取最新代码
echo "📥 从 $UPSTREAM_REMOTE 获取更新..."
if ! git fetch "$UPSTREAM_REMOTE" "$branch"; then
echo "❌ 拉取失败"
[ "$stashed" = true ] && git stash pop
return 1
fi
# 检查是否有新提交
local behind_count=$(git rev-list --count HEAD.."$UPSTREAM_REMOTE/$branch" 2>/dev/null)
if [ "$behind_count" -eq 0 ]; then
echo "✅ 已是最新"
[ "$stashed" = true ] && git stash pop
return 0
fi
echo "📊 上游有 $behind_count 个新提交"
# 执行合并策略:优先 rebase
if git rebase "$UPSTREAM_REMOTE/$branch"; then
echo "✅ 基于上游变基成功"
else
echo "❌ 变基失败,尝试合并..."
git rebase --abort 2>/dev/null
if git merge "$UPSTREAM_REMOTE/$branch"; then
echo "✅ 合并成功"
else
echo "❌ 合并失败,存在冲突"
echo "⚠️ 请手动解决冲突后继续"
[ "$stashed" = true ] && git stash drop 2>/dev/null
return 1
fi
fi
# 恢复暂存
[ "$stashed" = true ] && git stash pop
echo "=== ✅ 同步完成 ==="
return 0
}
# ===== 主逻辑 =====
main() {
case "$1" in
"pre-push")
sync_upstream "$(git symbolic-ref --short HEAD)" "pre-push"
;;
"post-checkout")
if [ "$3" = "1" ]; then
sync_upstream "$(git symbolic-ref --short HEAD)" "post-checkout"
fi
;;
"post-merge")
if [ ! -f .git/MERGE_HEAD ]; then
sync_upstream "$(git symbolic-ref --short HEAD)" "post-merge"
fi
;;
*)
echo "Usage: $0 {pre-push|post-checkout|post-merge}"
exit 1
;;
esac
}
# 执行
main "$@"
安装和配置说明
安装钩子
#!/bin/bash
# 一键安装脚本 install-git-hooks.sh
HOOKS_DIR=".git/hooks"
SCRIPTS_DIR="scripts/git-hooks"
# 需要的钩子
HOOKS=("pre-push" "post-checkout" "post-merge")
for hook in "${HOOKS[@]}"; do
if [ -f "$SCRIPTS_DIR/$hook" ]; then
# 备份已有钩子
if [ -f "$HOOKS_DIR/$hook" ]; then
cp "$HOOKS_DIR/$hook" "$HOOKS_DIR/$hook.bak"
fi
# 安装新钩子
cp "$SCRIPTS_DIR/$hook" "$HOOKS_DIR/$hook"
chmod +x "$HOOKS_DIR/$hook"
echo "✅ 安装 $hook 钩子"
else
echo "⚠️ 未找到 $SCRIPTS_DIR/$hook"
fi
done
echo "🎉 钩子安装完成"
使用方法
- 手动执行测试:
# 测试 pre-push 钩子 bash .git/hooks/pre-push
手动触发同步
git push --dry-run
2. **临时跳过钩子**:
```bash
# 方法1:使用 --no-verify 参数
git push --no-verify
# 方法2:设置环境变量
SKIP_GIT_HOOKS=1 git push
配置上游仓库
确保已配置好上游远程仓库:
# 添加上游仓库 git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git # 或设置已有远程为上游 git remote rename origin upstream # 检查配置 git remote -v
注意事项
- 冲突处理:自动同步可能导致冲突,建议在干净的仓库上操作
- 性能影响:频繁的 fetch/rebase 会增加网络开销
- 团队协作:与团队成员沟通,确保不会干扰他人工作
- 保护分支:对重要分支建议只采用手动同步
- 日志记录:建议在脚本中添加日志功能:
exec 1>>"$HOME/.git-hooks.log" 2>&1 echo "[$(date)] 开始同步 $current_branch"
需要根据你的具体工作流程调整这些脚本,建议先在测试仓库中验证后再正式使用。