使用git钩子防止合并分支
git是一款实用的版本管理工具,我们通过git init初始化一个git仓库,git会在当前目录为我们生成一个.git/目录,用来管理我们的版本文件信息。
在这个目录中有一个二级目录.git/hooks/,它里面存放了一些git执行的钩子脚本,在git运行的不同时期,执行不同的钩子。我们可以通过编写一些钩子脚本控制它的工作流程,比如在代码提交时进行邮件通知、代码格式检验等。
本文介绍的是一种通过编写钩子防止分支合并的案例,场景是在开发中有一些远超前于当前分支的分支(比如beta分支),如不慎将其合入开发分支(feature分支),然后还提交了,会给项目带来不必要的风险。
考虑日常工作情况,先使用git add, git commit提交代码,然后git merge合并分支。git merge进行的是一种三方合并,git分析了当前版本(以下简称F版本)、待合并版本(以下简称M版本)和它们的第一个共同祖先(也即分裂出它们两者的那个版本)这三者的差异,然后进行判断。如果M包含F的所有修改,则默认采用fast forward合并模式,直接把F所在分支的指针向前移动到M所在分支,合并结束,且不会执行任何钩子。
另一种情况是F和M形成了分裂(M不能包含F,因为修改了不同文件或者修改了相同的文件且造成冲突),在解决了所有可能发生的冲突后,git合并F和M的修改,创建一个全新版本(图2)。值得注意的是,如果在merge操作时带上-no-ff参数,则会强制按照这种方式合产生新版本。
第一种情况:

第二种情况:

在merge过程中会依次触发prepare-commit-msg和commit-msg钩子;如果有冲突,则在此之前,解决冲突并commit后触发pre-commit钩子,具体流程见下图。

git merge xxx三方合并是否快进合并结束是否冲突解决冲突后commit执行钩子pre-commit执行钩子prepare-commit-msg编辑提交合并信息执行钩子commit-msg合并结束yesnoyesno
下面来看钩子的编写,在.git/hooks文件夹下存放着一些git自带的钩子范例,且都以.sample为文件后缀。如果想让它们发挥作用,直接去掉这个后缀即可。钩子使用bash语法,在脚本中exit一个非零值即可中断git的行为。
比如说,把prepare-commit-msg.sample文件更名为prepare-commit-msg,并重写代码:
#!/bin/sh
echo "hook: prepare-commit-msg"
exit 1
那么通过正常git流程无法提交代码了,因为不管是commit操作还是merge操作(非快进),prepare-commit-msg钩子都要执行,而它exit 1表明是非正常终止,git就直接放弃了后续操作。
再来说下解决前文问题的思路:
我们在prepare-commit-msg钩子中实现控制,预设一个黑名单,对于所有git merge xxx操作,读取xxx对应的分支信息,与黑名单进行匹配,如果匹配命中,则终止操作。
需要思考的子问题:
- 钩子应该能自动检测当前操作是
merge还是commit(它们都会触发commit-msg) - 需要一个检测机制,精确匹配当前merge操作的分支是否在黑名单中
问题一是容易处理的,钩子执行时在全局已经给了我们几个参数,可以通过$x语法获取到,比如说对于prepare-commit-msg钩子:
#!/bin/sh
echo "hook: prepare-commit-msg"
# 钩子的路径
# .git/hooks/prepare-commit-msg
echo $0
# 提交信息文件路径(这是一个临时文件)
# merge操作 .git/MERGE_MSG ; commit操作 .git/COMMIT_EDITMSG
echo $1
# 操作类型
# merge操作 merge ; commit操作 message
echo $2
exit 1
$1参数是提交信息文件路径,merge操作的文件路径为".git/MERGE_MSG",我们自然可以判断如果这个文件存在,则是merge操作。当然也可以直接用$2操作类型判断,但commit-msg钩子中没有$2参数。
再来看问题二,怎样才能精确匹配分支?首先要找到待合并的分支是啥,在merge操作过程中,git会在.git目录生成MERGE_HEAD, MERGE_MODE, MERGE_MSG三个文件,分别存放的是待合并分支的sha-1值,合并模式和合并信息,读取MERGE_HEAD文件即可获取分支信息,nice。
接下来,要获取黑名单中分支的sha-1值,它们保存在.git/refs/heads这个目录里,我们遍历这个目录读取对应分支名的文件,即可拿到sha-1值
接下来就是匹配操作,就不赘述了。代码如下:
#!/bin/sh
BLACKLIST=("beta" "gamma")
forbid_list=()
if [[ -e .git/MERGE_HEAD ]]; then
heads=`ls .git/refs/heads`
for bl in "${BLACKLIST[@]}"; do
if [[ -n `echo ${heads} | grep -oP "(^| )${bl} "` ]]; then
forbid_list+=(`cat .git/refs/heads/${bl}`)
fi
done
merge_head=`cat .git/MERGE_HEAD`
for br in "${forbid_list[@]}"; do
if [[ ${merge_head} == ${br} ]]; then
echo -e "\033[41;37m 合并了黑名单中的分支 \033[0m\n\r"
echo -e "\033[41;37m 请使用 git merge --abort 命令终止合并 \033[0m"
exit 1
fi
done
fi
最后一点也是最重要的,前面提到过fastward合并模式无法触发任何钩子,所以必须使用强制产生新版本的--no-ff模式,钩子才能发挥作用。推荐做法是使用git别名: git config --global alias.mg 'merge --no-ff', 以后合并操作使用git mg xxx, 保证钩子执行的同时还能少按几次键。
使用git钩子防止合并分支的更多相关文章
- git创建与合并分支
创建与合并分支 在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支.截止到目前,只有一条时间线,在Git里,这个分支叫主分 支,即master分支.HEAD严格来 ...
- Git(创建与合并分支)
在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支.截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支.HEAD严格来说不是指向提交,而 ...
- 使用git创建与合并分支
一.概述 学会使用git命令对项目进行创建分支,并在创建结束后合并到主分支上. 问:为什么要创建分支? 答:在原来的分支上创建一个自己的分支进行开发,在开发完毕后一次性合并到原先的分支,这样既保证安全 ...
- 【Git版本控制】Git的merge合并分支命令
1.实例 git checkout master git merge dev merge合并分支只对当前分支master产生影响,被合并的分支dev不受影响. 假设你有两个分支,“stable” 和 ...
- Git创建与合并分支,撤销修改
git回滚到指定版本并推送到远程分支(撤销已提交的修改,并已push) git reset --hard <commit ID号> git push -f git回滚到上一个版本并推送到远 ...
- 五、git创建及合并分支
1. 创建并切换到dev分支 git checkout -b dev // git checkout命令加上-b参数表示创建并切换,相当于以下两条命令 git branch dev git check ...
- Git合并分支出现的冲突解决
人生不如意之事十有八九,合并分支往往也不是一帆风顺的. 我们准备新的分支newbranch. LV@LV-PC MINGW32 /c/gitskill (master)$ git checkout - ...
- GIT 分支管理:创建与合并分支、解决合并冲突
分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN. 如果两个平行宇宙互不干扰,那对现在的你也没啥影响.不过,在某个时间点,两个平行宇宙合并 ...
- Git学习——创建与合并分支
分支概念 当前我们所在的分支是master(主分支),可以通过创建分支: git branch <branch_name> 创建完成后,可以查看当前的分支状态: git branch 当前 ...
- Git 解决合并分支时的冲突
参考链接:https://www.liaoxuefeng.com/wiki/896043488029600/900004111093344 创建分支时,新分支的文件内容建立在原分支的基础上,我们称这时 ...
随机推荐
- 51电子-STC89C51开发板:程序烧录(刷写) 到 IC 设置
全部内容,请点击: 51电子-STC89C51开发板:<目录> --------------------------- 正文开始 --------------------------- ...
- MyBatis_09(逆向工程)
MyBatis的逆向工程 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表.Hibernate是支持正向工程的 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源: J ...
- Vue 事件监听
事件监听 v-on 使用v-on进行事件绑定监听,回调函数写在methods中.可以使用@的这种简写形式来代替v-on,当事件源无参数传递时,可省略括号. 语法如下所示: <button @:事 ...
- css样式 div垂直水平居中对齐
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- docker镜像打包成tar包,上传到另一台服务器
需要打包的服务器操作: 一.需要打包的服务器执行以下操作 docker save -o node.tar.gz calico/node # docker save -o 要打镜像包的名称 镜像 ...
- django项目初创建报错TypeError: unsupported operand type(s) for /: 'str' and 'str'
解决办法: 'DIRS': [os.path.join(BASE_DIR, 'templates')],
- shell, 进程, 变量, 命令, 作业控制
资料来源 (1) UNIX shell范例精解 1.什么是shell? shell的作用? (1) shell是一种特殊的程序,是用户与UNIX/Linux系统内核之间的接口; (2) shell用于 ...
- springboot集成Thumbnailator压缩图片
一.参考大神博客 1.https://blog.51cto.com/u_11269274/5118649 2.https://blog.csdn.net/weixin_44722978/article ...
- CUDA Arch 代码
- 【Anaconda】Jupyter 中添加 Anaconda 环境
两种方法: 1. 安装 nb_conda_kernels,将所有 conda 环境同步至 Jupyter Notebook,参考『Jupyter notebook选择conda环境 - 简书』. 2. ...