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/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 推送到遠端版...