We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
项目开发时,有开发分支,测试分支,主干分支等。一般不能把测试分支合并到其他分支里,然而可能一不小心(手抖)合并了,甚至在不知情的情况下还加了新的东西,后面上线时才发现(或者没发现,直接把测试分支的代码带到了线上),后果可大可小,回滚时也麻烦。
那能不能在合并阶段直接禁止合并非法分支呢?答案是可以的。只要解决了下面问题即可。
git hooks,简单来说就是在执行 git 命令的过程中会触发的钩子函数(脚本程序)。只要知道特定 git 命令会触发什么 hooks,就可以做对应处理,比如可以用来检查提交信息是否符合规范(如 commitlint),以及本文即将要讨论的阻止合并某些分支。
命令: git merge <branch>
git merge <branch>
合并可能有 3 种情况
git merge --no-ff <branch>
图片来自:https://www.atlassian.com/git/tutorials/using-branches/git-merge
前置说明 这里的项目是前端项目,使用 husky 管理 git hooks
通过查阅 git hooks 可知,merge 阶段可能会触发以下钩子【之所以说可能是因为 merge 有多种情况,每种情况触发的钩子不太一致】:
pre-merge-commit
prepare-commit-msg
commit-msg
post-merge
fast-forward merge
no fast-forward merge
merge conflict
merge conflict 会有中间态 (当前分支 | MERGING),从初始态到中间态,不会触发 merge 相关的钩子。当解决完冲突后,开始 add&commit 时,才会触发对应钩子
(当前分支 | MERGING)
根据合并情况,使用到的钩子如下:
git rev-parse --abbrev-ref HEAD
REF: https://stackoverflow.com/questions/6245570/how-to-get-the-current-branch-name-in-git
在此钩子处理 no fast-forward merge 和 fast-forward merge 合并。
post-merge 钩子触发时,分支已经合并了,并且 reflog 也更新了,所以可以通过 git reflog 获取到合并进来的分支信息
git reflog
前 2 种合并情况,git reflog -1 返回的日志格式如下
git reflog -1
e7cb874 HEAD@{0}: merge feat/no-fast-forward: Merge made by the 'recursive' strategy.
724446f HEAD@{0}: merge feat/fast-forward: Fast-forward
可以通过正则匹配提取对应的分支名,代码如下
const { execSync } = require('child_process'); function getMergeBranch() { // 从 reflog 提取合并进来的分支名 function getBranchNameFromReflog(reflogMessage) { const reg = /@\{\d+\}: merge (.*):/; return reg.exec(reflogMessage)[1]; } const reflogMessage = execSync('git reflog -1', { encoding: 'utf8' }); const mergedBranchName = getBranchNameFromReflog(reflogMessage); return mergedBranchName; }
在此钩子处理合并冲突的情况。
因冲突未解决,reflog 也不会更新,因此无法通过 reflog 获取到合并进来的分支。
不过在合并冲突阶段,.git/MERGE_HEAD 中会保留合并进来分支的 hash。 在 prepare-commit-msg 触发时,可以通过读取该文件获取对应的内容,再通过 git name-rev [hash] 命令获取对应的分支名
.git/MERGE_HEAD
git name-rev [hash]
const { execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); // 从 .git/MERGE_HEAD (sha) 提取合并进来的分支名 function getMergeBranch() { try { const mergeHeadPath = path.resolve(process.cwd(), '.git/MERGE_HEAD'); const mergeHeadSha = fs.readFileSync(mergeHeadPath, { encoding: 'utf8' }); const mergeBranchInfo = execSync(`git name-rev ${mergeHeadSha}`); return / (.*?)\n/.exec(mergeBranchInfo)[1]; } catch (err) { return ''; } }
这个根据各自场景处理就行了。比如在合并错误分支后,进行提示,让操作者自行决定是否回滚等。
const { execSync } = require('child_process'); const readline = require('readline'); function showConfirm(currentBranch, mergeBranch, inConflict) { log(`检测到非法合并: ${mergeBranch} ==into==> ${currentBranch}`); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question(`是否撤销本次合并?(y/n) `, (answer) => { if (answer === 'y') { log('撤销合并中...'); if (inConflict) { log(`exec: git merge --abort`); execSync('git merge --abort'); log('已撤销合并 done'); rl.close(); process.exit(-1); } else { log(`exec: git reset --merge HEAD@{1}`); execSync('git reset --merge HEAD@{1}'); log('已撤销合并 done'); rl.close(); process.exit(0); } } else { rl.close(); process.exit(0); } }); };
在第一个问题【是否在合并中】,最终采用了 post-merge 和 prepare-commit-msg 2 个钩子来做对应的拦截处理,但这 2 个钩子在no fast-forward merge的情况下都会被触发到,也就是会重复执行拦截处理。 此时需要进行判断,当且仅当在 merge conflict 才去执行 prepare-commit-msg 钩子中的拦截逻辑。保证每种合并情况只会触发一次拦截逻辑。
思路是检测 .git/MERGE_MSG 文件是否存在,以及其中的内容是否是冲突信息。
.git/MERGE_MSG
const { execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); function isMergingConflict() { // 是否合并中 const mergeMsgPath = path.resolve(process.cwd(), '.git/MERGE_MSG'); const isMerging = fs.existsSync(mergeMsgPath); if (!isMerging) { return false; } try { const mergeMsg = fs.readFileSync(mergeMsgPath, { encoding: 'utf8' }); return /\n# Conflicts:\n/.test(mergeMsg); // 如果是冲突则能匹配上 } catch (err) {} return false; }
https://git-scm.com/docs/githooks https://www.atlassian.com/git/tutorials/using-branches/git-merge
The text was updated successfully, but these errors were encountered:
No branches or pull requests
引言
项目开发时,有开发分支,测试分支,主干分支等。一般不能把测试分支合并到其他分支里,然而可能一不小心(手抖)合并了,甚至在不知情的情况下还加了新的东西,后面上线时才发现(或者没发现,直接把测试分支的代码带到了线上),后果可大可小,回滚时也麻烦。
那能不能在合并阶段直接禁止合并非法分支呢?答案是可以的。只要解决了下面问题即可。
前置知识
git hooks
git hooks,简单来说就是在执行 git 命令的过程中会触发的钩子函数(脚本程序)。只要知道特定 git 命令会触发什么 hooks,就可以做对应处理,比如可以用来检查提交信息是否符合规范(如 commitlint),以及本文即将要讨论的阻止合并某些分支。
git 合并
命令:
git merge <branch>
合并可能有 3 种情况
git merge --no-ff <branch>
变为第 2 种合并情况问题解答
是否在合并中
通过查阅 git hooks 可知,merge 阶段可能会触发以下钩子【之所以说可能是因为 merge 有多种情况,每种情况触发的钩子不太一致】:
pre-merge-commit
prepare-commit-msg
commit-msg
post-merge
pre-merge-commit
prepare-commit-msg
commit-msg
post-merge
fast-forward merge
no fast-forward merge
merge conflict
解决完冲突后 add & commit根据合并情况,使用到的钩子如下:
fast-forward merge
和no fast-forward merge
: 使用post-merge
钩子进行逻辑处理merge conflict
: 使用prepare-commit-msg
进行逻辑处理【因为commit-msg
钩子无法获取到合并进来的分支名,故只能使用prepare-commit-msg
】获取当前分支名
获取合并进来的分支名
post-merge
在此钩子处理
no fast-forward merge
和fast-forward merge
合并。post-merge
钩子触发时,分支已经合并了,并且 reflog 也更新了,所以可以通过git reflog
获取到合并进来的分支信息前 2 种合并情况,
git reflog -1
返回的日志格式如下no fast-forward merge
:e7cb874 HEAD@{0}: merge feat/no-fast-forward: Merge made by the 'recursive' strategy.
fast-forward merge
:724446f HEAD@{0}: merge feat/fast-forward: Fast-forward
可以通过正则匹配提取对应的分支名,代码如下
prepare-commit-msg
在此钩子处理合并冲突的情况。
因冲突未解决,reflog 也不会更新,因此无法通过 reflog 获取到合并进来的分支。
不过在合并冲突阶段,
.git/MERGE_HEAD
中会保留合并进来分支的 hash。在
prepare-commit-msg
触发时,可以通过读取该文件获取对应的内容,再通过git name-rev [hash]
命令获取对应的分支名合并分支是否符合要求
这个根据各自场景处理就行了。比如在合并错误分支后,进行提示,让操作者自行决定是否回滚等。
其他问题
在第一个问题【是否在合并中】,最终采用了
post-merge
和prepare-commit-msg
2 个钩子来做对应的拦截处理,但这 2 个钩子在no fast-forward merge
的情况下都会被触发到,也就是会重复执行拦截处理。此时需要进行判断,当且仅当在
merge conflict
才去执行prepare-commit-msg
钩子中的拦截逻辑。保证每种合并情况只会触发一次拦截逻辑。思路是检测
.git/MERGE_MSG
文件是否存在,以及其中的内容是否是冲突信息。总结
参考
The text was updated successfully, but these errors were encountered: