Git Hooks 配合
Husky
实现 merge 时分支检查。
安装 Husky
文档地址:Husky - Git hooks
使用 pnpm
安装成功后,package.json
会多出一条 script
prepare
脚本会在每次执行完pnpm install
或者npm install
之后自动执行
Git Merge
merge
可能存在多种情况,参考 Git Merge | Atlassian Git Tutorial
fast-forward merge
这种情况是当前分支和要合并进来的分支,分支历史没有分叉,要合并的分支是基于当前分支前进的,且当前分支从要合并的分支新建后,再也没有发生过改动。
例如我们从 main
分支中新建了 dev
分支,然后在dev
分支中做开发,main
分支没有做过任何改动,dev
分支开发完成后merge
到main
分支
这种情况下我们使用 git reset HEAD^ --hard
的时候,整个分支会退回到 dev1-commit-3
no fast-forward merge
fast-forward merge
是我们在git merge
时默认使用的方式,我们可以通过在merge的时候添加 --no-ff
参数来关闭 fast-forward
模式,在提交的时候会自动创建一个 merge
的 commit
信息,然后合并到main
分支
会新增一个历史节点,其直接父节点指向要合并的两个分支,看两张示意图
这种情况下我们使用 get reset HEAD^ --hard
的时候,整个分支会回退到 dev2-commit-2
squash
通过 --squash
可以将一些不必要的 commit
进行压缩,合并的时候可以将 dev
分支的历史commit
进行合并,合并为一个commit
当我们 get merge --squash
之后,此时文件已经合并了,但是不移动 HEAD,不提交,需要再进行一次 额外的 commit
来“总结”一下,完成了最终的合并
这三种参数的示意图总结如下
merge conflict
当我们合并的两个分支存在冲突的时候,此时需要先解决完冲突,然你在重新 add
和 commit
GIt Merge Hooks
当我们 git merge
时可能会触发四个钩子,根据 merge
时的情况不同(上面四种),会执行不同的钩子
合并情况/触发钩子 | pre-merge-commit | prepare-commit-msg | commit-msg | post-merge |
---|---|---|---|---|
fast-forward | 不触发 | 不粗发 | 不触发 | 触发 |
no fast-forward | 触发 | 触发 | 触发 | 触发 |
merge conflict解决冲突后 add & commit | 不触发 | 触发 | 触发 | 不触发 |
最终根据合并情况,使用到的钩子情况如下
fast-forward
和n fast-forward
情况下:使用post-merge
钩子merge connflict
:存在冲突时使用prepard-commit-msg
所以我们如果要对 git merge
进行检测,最终需要用到 post-merge
和 prepare-commit-msg
两个钩子
获取当前分支
通过 Node execSync
执行上面的命令,可以获得当前所在的分支名
获取合并进来的分支名
post-merge
post-merge
钩子触发时,merge
操作已经结束了,已经成功的完成了合并,git reglog
已经完成了更新,所以此时可以通过 reglog
中的日志信息来获取合并进来的 分支名,该钩子会在 fast-forward
和 no fast-forward
的情况下触发。
git reflog -1
返回的日志格式
根据日志信息,可以通过正则匹配的方式提取分支名
prepare-commit-msg
当 merge 存在冲突代码时,会触发该钩子,未解决冲突之前,reflog
不会更新,所以不能通过 reflog
信息来获取合并进来的分支。
不过在合并冲突阶段,.git/MERGE_HEAD
文件中会保留合并进来分支的 hash
,我们可以通过读取该文件获取赌赢的内容,再通过 git name-rev [hash]
命令获取对应的分支名。
以下是通过NodeJs
来判断是否是在合并中,也可以像上面代码那样用sh
判断
另外需要注意的时,因为我们使用了两个钩子post-merge
和prepare-commit-merge
,当no fast-forward
情况下时,这两个钩子都会执行,merge conflict
冲突模式下,只会触发 prepare-commit-merge
,不会触发 post-merge
,所以我们需要判断当前是否是需要解决冲突的情况,如果是存在冲突的情况,才去执行 prepare-commit-msg
钩子。
可以通过检测 .git/MERGE_MSG
文件是否存在,以及其中的内容来判断当前是否处于解决冲突中
Husky 会自动退出当前终端进程
如果我们想在commit
或者merge
的时候通过readline
来提示用户输入一些内容,会失效,例如如下代码
会直接跳过用户输入阶段,Mac 上的解决办法是 添加
window 下使用一下代码有效
参考:
- How to get Husky pre-commit to await for user prompt input? · Issue #1129 · typicode/husky · GitHub
- git - How do I prompt the user from within a commit-msg hook? - Stack Overflow
- Why “No such device or address” when open /dev/tty in the first process? - Unix & Linux Stack Exchange
- git - sh shell script on windows causes error: /dev/tty: No such device or address - Stack Overflow
不是可执行文件的错误
如果在执行文件修改 commit 之后报错下面的错误,是因为 sh 脚本没有被识别为可执行文件,Git 不会同步文件的修改,问题表现为代码同步到了其他分支,但是并不会同步到 .git/hooks
文件中,因此会导致钩子失效
执行 hmod +x .husky/prepare-commit-msg
之后再次 commit 就好了
Git hooks 一览
Hook | 时机 | 说明 |
---|---|---|
pre-applypatch | git am 执行前 | |
applypatch-msg | git am 执行前 | |
post-applypatch | git am 执行后 | 不影响git am 的结果 |
pre-commit | git commit 执行前 | 可以用git commit --no-verify 绕过 |
commit-msg | git commit 执行前 | 可以用git commit --no-verify 绕过 |
post-commit | git commit 执行后 | 不影响git commit 的结果 |
pre-merge-commit | git merge 执行前 | 可以用git merge —no-verify绕过 |
prepare-commit-msg | git commit 执行后,编辑器打开之前 | — |
pre-rebase | git rebase 执行前 | |
post-checkout | git checkout 或 git switch执行后 | 如果不使用—no-checkout参数,则在git clone 之后也会执行 |
post-merge | git commit 执行后 | 再执行git pull时也会被调用 |
pre-push | git push执行前 | |
pre-receive | git-receive-pack执行前 | |
update | ||
post-receive | git-receive-pack执行后 | 不影响git-receive-pack的结果 |
post-update | 当git-receive-pack对git push做出反应并更新仓库中的引用时 | |
push-to-checkout | 当git-receive-pack对git push做出反应并更新仓库中的引用时,以及当推送试图更新当前被签出的分支,且 receive.denyCurrentBranch配置被设置为 updateInstead时 | |
pre-auto-gc | git fc —auto执行前 | |
post-rewrite | 执行git commit —amend或git rebase时 | |
sendemail-validate | git send-email执行前 | |
fsmonitor-watchman | 配置core.fsmonitor被设置为 .git/hooks/fsmonitor-watchman或.git/hooks/fsmonitor-watchmanv2时 | |
p4-pre-submit | git-p4 submit 执行前 | 可以用git-p4 submit —no-verify绕过 |
p4-prepare-changelist | git-p4 submit 执行后,编辑器启动前 | 可以用git-p4 submit --no-verify 绕过 |
p4-changelist | git-p4 submit 执行并编辑完changelist message 后 | 可以用git-p4 submit --no-verify 绕过 |
p4-post-changelist | git-p4 submit 执行后 | |
post-index-change | 索引被写入到read-cache.c do_write_locked_index 后 |
参考
- Git Hooks 阻止合并特定分支 | PJ’S BLOG
- Git Merge | Atlassian Git Tutorial
- git merge和git merge —no-ff的区别 | TY·Loafer
- Husky - Git hooks
- bash - Git : Determine if branch is in a merge conflict state - Stack Overflow
- Git hook to prevent merging staging branch into master branch · GitHub
- Prevent merge from a specific branch using Git Hooks