通过Pre-Receive Hook测试git push上的推送冲突

时间:2016-04-27 14:31:55

标签: git push hook pre

我正在BitBucket上进行Pre-Receive Hook,它应该确认分支中的所有推送都是与父分支一起进行的。

我的意思是,在时间演变中,我们有几个分支创作:

Branch creation during time

使用上面的示例os 3分支,Dev,Feature1和我的Local,我想在将Local推送到远程/ originins / Feature1之前,使用最近的推送本地代码从最新的Feature1进行git合并。通过这种方式,我可以确认,无论是谁进行推送,都在使用最新版本的feature1,并且不会发生冲突。 如果有任何冲突,我会返回1,以避免推动!并强制开发人员在推送代码之前从Feature中提取。

这是我在Pre-Receive Hook上的脚本。

while read from_ref to_ref ref_name; do
    echo "Ref update:"
        echo " Old value: $from_ref"
        echo " New value: $to_ref"
        echo " Ref name:  $ref_name"
        echo " Diff:"
        git clone --progress -v $GIT_URL $CLONE_DIR1
        cd $CLONE_DIR1
        git checkout -b test remotes/origin/Feature1
        git merge --no-commit -m "Merging feature with local on-push code" $ref_name
        (....)
done

我尝试过使用ref_name,to_ref,但没有成功。

任何人都可以帮助我?

如何访问最近推送的代码,并通过父代码与此代码合并?

非常感谢, 努诺

2 个答案:

答案 0 :(得分:1)

这似乎是一件非常奇怪的事情,它可能注定要失败。它肯定会很复杂,您将需要根据更新的ref更改测试行为以及这些更新是否添加合并提交。

也就是说,预接收和更新挂钩有一些特殊的规则,如果你遵守它们,你会得到更多:

  1. 请勿离开当前目录chdircd。或者,如果你这样做,请确保chdir回来,但通常不难确保必须在另一个目录中运行的操作作为一个单独的进程运行:子shell,或另一个脚本。
  2. 在尝试需要使用其他存储库的git命令之前,从环境中删除$GIT_DIR。原因是钩子在顶级目录中运行,$GIT_DIR设置为.git(非裸仓库)或.(裸仓库)。
  3. 将这两者放在一起,您可以将所有验证程序代码移动到单独的脚本中并执行以下操作:

    exitstatus=0
    while read from_ref to_ref ref_name; do
        ... maybe some setup code here to see if $ref_name
            is being created or destroyed ...'
    
        case "$ref_name" in
        ... add cases as needed to choose action based on ref ...
            if (unset GIT_DIR; /path/to/check_script arg1 arg2 ...); then
                echo "push being rejected because ..."
                exitstatus=1
            fi
        ...
        esac
        ...
    done
    exit $exitstatus
    

    这里还有一个很大的问题。如果挂钩脚本退出0 ,您希望check_script能够访问任何可以从$ref_name 访问的提交,以便提议允许更新。该更新尚未发生$ref_name仍然指向旧的SHA-1 $from_ref。同时,$to_ref 中的新SHA-1可能没有任何指向它的名称(尽管它仍存在于底层存储库中)。

    除其他外,如果$to_ref指向 new 提交(通常情况),那么此时通过正常的git操作生成的任何克隆都不会包含这些提交,所以你将无法使用它们。

    有两种明显的方法可以解决这个问题:

    • 创建一个指向$to_ref的新(临时)引用。然后,您可以在克隆中看到建议的提交。
    • 不要使用克隆。以其他方式复制存储库,或直接使用原始存储库本身,例如,作为"替换",或者通过创建临时工作树目录并指向$GIT_WORK_TREE,或者使用某些git 2.6+中出现的新git worktree功能。 (如果选择手动临时工作树方法,请务必考虑正常的共享$GIT_INDEX_FILE。)

    还要记住检查删除提交分支的强制推送,甚至一次性删除 - 添加 - 添加其他所有内容。

答案 1 :(得分:1)

更新: 这个问题已经解决了。

最终的代码是:

#!/bin/bash
DIR=xpto/external-hooks/code_review
CLONE_DIR=$DIR/test_conflict_push-$(date +%s)
GIT_URL=myGitUrl
exitStatus=0

read oldrev newrev refname
feature_branch=${refname##refs/heads/}
echo "Feature branch-> $feature_branch"
#Clone feature branch from remote repo to be update via merged.
git clone --progress -v $GIT_URL $CLONE_DIR
currentDir=$PWD
cd $CLONE_DIR
#create branch named 'latest' to put new and modify files
git checkout -b latest remotes/origin/$feature_branch
#go back to PWD otherwise cant make git diff
cd $currentDir
# Get the file names, without directory, of the files that have been modified
# between the new revision and the old revision
echo "Getting files"
files=`git diff --name-only ${oldrev} ${newrev}`
echo "Files -> $files"
# Get a list of all objects in the new revision
echo "Getting objects"
objects=`git ls-tree --full-name -r ${newrev}`
echo "objects -> $objects"
# Iterate over each of these files
for file in ${files}; do
    # Search for the file name in the list of all objects
    object=`echo -e "${objects}" | egrep "(\s)${file}\$" | awk '{ print $3 }'`
    # If it's not present, then continue to the the next itteration
    if [ -z ${object} ]; 
    then
        continue;
    fi
    # Otherwise, create all the necessary sub directories in the new temp directory
    mkdir -p "${CLONE_DIR}/`dirname ${file}`" &>/dev/null
    # and output the object content into it's original file name
    git cat-file blob ${object} > ${CLONE_DIR}/${file}
done;
echo "Ready for start merging."
cd $CLONE_DIR
#add new files to branch
echo $(git add .)
#commit added and modify files to branch
echo $(git commit -a -m "Merge latest to original feature")
#get generated commit id
echo $(git log -1)
#create branch named 'merged' to merge above commited files
echo $(git checkout -b merged remotes/origin/$feature_branch)
#merge only occurs for madded and modify files!
echo "Merging committed files to 'merged' branch with from 'latest' branch."
mergeResult=$(git merge --no-commit latest)
echo "Merge Result -> $mergeResult"
##to lower case
if [[ "${mergeResult,,}" == *"conflict"* ]]
then
echo "Merge contains conflicts."
echo "Update your $feature_branch branch!"
exitStatus=1
else
echo "Merge don't contains conflict."
echo "Push to $feature_branch can proceed."
exitStatus=0
fi
#remove temporary branches
echo $(git checkout master)
echo $(git branch -D latest)
echo $(git branch -D merged)
#delete temporary clone dir
rm -rf $CLONE_DIR
exit $exitStatus

非常感谢。