この記事は最初に公開されました: https://github.com/bigo-frontend/blog/ フォローと再投稿を歓迎します。
導入
プロジェクト開発中には、開発ブランチ、テスト ブランチ、トランク ブランチなどが存在します。通常、テスト ブランチは他のブランチにマージできませんが、誤ってマージしたり (手が震えたり)、知らないうちに新しいものを追加したりする可能性があり、後でオンラインになったときに発見されます (または見つからなかった場合)。 、テストブランチを直接置くだけです。ブランチのコードはオンラインになります)、結果は大なり小なりあり、ロールバックは面倒です。
マージフェーズ中に違法なブランチのマージを直接禁止することはできますか? 答えは「はい」です。次の問題を解くだけです。
- 合併されているのでしょうか?
- 現在の支店の名前は何ですか?
- マージ先のブランチの名前は何ですか?
- 現在のブランチとマージ対象のブランチが条件を満たしているかどうか (たとえば、マージ対象のブランチをテスト ブランチにすることはできません)
予備知識
gitフック
githooks は、簡単に言えば、git コマンドの実行中にトリガーされるフック関数 (スクリプト プログラム) です。特定の git コマンドによってどのようなフックがトリガーされるかがわかっていれば、それに応じた処理を行うことができます。たとえば、送信情報が仕様 ( commitlint など) に準拠しているかどうかを確認したり、マージを防止したりするために使用できます。この記事で説明する特定のブランチについて説明します。
gitマージ
注文:git merge <branch>
合併の可能性は3通りある
- fast-forward merge : マージするとき、現在のブランチとマージ先のブランチはブランチ履歴に分岐がありません [簡単に理解すると、マージされるブランチは現在のブランチに基づいており、現在のブランチはそれ以来一度も発生していません。新しいブランチが作成されました変更]。次の方法で
git merge --no-ff <branch>
2 番目のマージ ケースに変更できます。
- no fast-forward merge : 直接の親がマージする 2 つのブランチを指す新しい履歴ノードを追加します。
- マージ競合: マージ競合が発生しています。現時点では、競合を解決してから、再度追加してコミットする必要があります。
画像の出典: https://www.atlassian.com/git/tutorials/using-branches/git-merge
事前説明ここのプロジェクトは、 huskyを使用してgit フックを管理する
フロントエンド プロジェクトです。
よくある質問
合併されてるのかな
githooksを参照すると、マージ ステージで次のフックがトリガーされる可能性があることがわかります(この理由は、マージには多くの状況があり、それぞれの状況でトリガーされるフックが一貫していないためであると考えられます)。
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 競合を解決した後に追加してコミットする |
❌ | ✅ | ✅ | ❌ |
merge conflict
初期状態から中間状態までは中間状態となり(当前分支 | MERGING)
、マージ関連のフックはトリガーされません。競合が解決され、add&commit が開始されると、対応するフックがトリガーされます。
マージの状況に応じて、使用されるフックは次のとおりです。
fast-forward merge
およびno fast-forward merge
:post-merge
ロジック処理にフックを使用するmerge conflict
:prepare-commit-msg
論理処理に使用 [commit-msg
フックではマージされたブランチ名が取得できないため、のみ使用可能prepare-commit-msg
]
現在のブランチ名を取得します
git rev-parse --abbrev-ref HEAD
参照: 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
マージされたブランチ情報は次の方法で取得できます。
最初の 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
通常のマッチングにより対応するブランチ名を抽出できます。コードは次のとおりです。
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
マージされたブランチのハッシュが保持されます。
トリガーされるとprepare-commit-msg
、ファイルを読み取って対応するコンテンツを取得し、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
最初の質問「マージされているかどうか」では、最終的に 2 つのフックを使用してprepare-commit-msg
対応するインターセプト処理を行っていますが、no fast-forward merge
の場合、この 2 つのフックがトリガーされる、つまりインターセプト処理が繰り返し実行されます。この時点で判断が必要となり、フック内のインターセプト ロジックは に
ある場合にのみmerge conflict
実行されます。prepare-commit-msg
各マージ状況でインターセプト ロジックが 1 回だけトリガーされることが保証されます。
.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
議論するメッセージを残してくださる皆様を歓迎します。スムーズな仕事と幸せな生活をお祈りしています。
私は bigo のフロントエンドです。次号でお会いしましょう。