使用git鉤子防止合併分支

2021-09-29 18:11:43 字數 3586 閱讀 5350

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-msgcommit-msg鉤子;如果有衝突,則在此之前,解決衝突並commit後觸發pre-commit鉤子,具體流程見下圖。

下面來看鉤子的編寫,在.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 ***操作,讀取***對應的分支資訊,與黑名單進行匹配,如果匹配命中,則終止操作。

需要思考的子問題:

鉤子應該能自動檢測當前操作是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

$2exit 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

"$";

doif

[[ -n `

echo $

|grep -op "(^| )$ "` ]

];then

forbid_list+=(`

cat .git/refs/heads/$`)

fidone

merge_head=

`cat .git/merge_head`

for br in

"$";

doif[[

$==$]

];then

echo -e "\033[41;37m 合併了黑名單中的分支 \033[0m\n\r"

echo -e "\033[41;37m 請使用 git merge --abort 命令終止合併 \033[0m"

exit 1

fidone

fi

最後一點也是最重要的,前面提到過fastward合併模式無法觸發任何鉤子,所以必須使用強制產生新版本的--no-ff模式,鉤子才能發揮作用。推薦做法是使用git別名:git config --global alias.mg 'merge --no-ff', 以後合併操作使用git mg ***, 保證鉤子執行的同時還能少按幾次鍵。

Git 使用Rebase合併分支

在開發過程中,可能會出現多個 commit 所涉及的邏輯都是同乙個功能模組,此時,會導致 log tree 非常的混亂,不美觀,因此,我們可以將多個 commit 進行合併,變成一條,這樣,也使得 log tree 會更加的簡潔。首先,先確定的是,我們合併 commit 使用的是 rebase 命令...

git合併分支

應該是基本知識的,但是之前工作很少用develop分支,用的時候也不會負責合併和發布新版本,所以就一直沒有接觸這塊,做自己小東西一點一點嘗試吧,也不敢亂來,怕一不小心把自己 搞沒了.需求 我在github有乙個master分支,本地有乙個develop分支,目前做的修改都在develop上,現在準備...

git合併分支

工作中很多情況下都是並行開發,後開發的模組上線時需要合併先開發完成的 這就用到了git的多分支合併。這裡以分支dev5.0.1 dev5.0.2和主幹master進行講解。合併思路是先將dev5.0.1合併到master,在dev5.0.2合併master 的 最後把 dev5.0.2 推送到遠端版...