Git钩子脚本如何同步上游最新代码

wen 实用脚本 1

pre-push钩子中同步

这个方案在推送前先拉取上游最新代码,确保本地代码基于最新版本。

Git钩子脚本如何同步上游最新代码

创建 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 "🎉 钩子安装完成"

使用方法

  1. 手动执行测试
    # 测试 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

注意事项

  1. 冲突处理:自动同步可能导致冲突,建议在干净的仓库上操作
  2. 性能影响:频繁的 fetch/rebase 会增加网络开销
  3. 团队协作:与团队成员沟通,确保不会干扰他人工作
  4. 保护分支:对重要分支建议只采用手动同步
  5. 日志记录:建议在脚本中添加日志功能:
    exec 1>>"$HOME/.git-hooks.log" 2>&1
    echo "[$(date)] 开始同步 $current_branch"

需要根据你的具体工作流程调整这些脚本,建议先在测试仓库中验证后再正式使用。

上一篇Git钩子脚本如何清理本地已合并分支

下一篇当前分类已是最新一篇

抱歉,评论功能暂时关闭!