- 原文地址:Protect our Git Repos, Stop Foxtrots Now!
- 原文做者:Sylvie Davies
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:LeviDing
- 校對者:薛定諤的貓,luisliuchao
舞者們正準備跳狐步舞。html
「狐步舞」式的合併是 git commit
的一個特別很差的具體順序。如同在戶外看到的「狐步舞」,這種commits序列像這個樣子:前端
但在公開場合不多會見到「狐步舞」。它們隱藏在樹冠之間,樹枝之間。我稱它們「狐步舞」式,是由於他們交叉的樣子,他們看起來像同步舞蹈的舞步順序:react
還有一些人也提到「狐步舞」式的合併,但它們歷來沒有直接說出它的名字。例如,Junio C. Hamano 的博客有有趣的 --first-parent
,還有有趣的非快進方式(Non-Fast-Forward)。David Lowe 的 nestoria.com 有關於保持一致的線性歷史記錄的文章。此外還有一大堆人告訴你要避免使用 git pull
,而是使用 git pull –rebase
。爲何?主要是爲了不通常的合併和提交時的錯誤,此外還能夠避免出現該死的「狐步舞」式的提交。linux
「狐步舞」式的合併真的很很差嗎?是的。android
它們顯然不如僧帽水母那樣糟糕。可是「狐步舞」式的合併也是很差的,你不但願你的 git 倉庫裏有它們的身影。ios
「狐步舞」式的合併很差,由於它會改變 origin/master
分支的「第一父級」的地位。git
合併提交記錄的父級是有序的。第一個父級是 HEAD
。第二個父級是用 git merge
命令提交的。github
你能夠像下面這樣想:後端
git checkout 1st-parent
git merge 2nd-parent
複製代碼
若是你是 octopus 的說客:bash
git merge 2nd-parent 3rd-parent 4th-parent ... 8th-parent etc...
複製代碼
這意味着父級的記錄就像它聽起來同樣。當你提交新的代碼的時候,忽略第一個父級之外的父級,從而獲得一個新的代碼記錄。對於常規的 commit
(非 merge
),第一個父級是惟一的父級,而且對於 merge
來講,它是你在輸入 git merge
時所產生的記錄。這種父級概念是直接植入到 Git 裏的,而且在不少命令行中都有所體現,例如,git log –-first-parent
。
「狐步舞」式的合併問題在於,它使得 origin/master 由第一父級變成了第二父級。
除了 Git 在評估提交是否有資格進行 fast-forward
時,Git 並不關心父級的前後次序。
固然你很不但願這樣。你不但願「狐步舞」式的合併經過 fast-forward
的方式更新你的 origin/master,使得 origin/master 第一父級的地位不穩定。
看一下當「狐步舞」式的合併被 push
上去的時候會發生什麼:
可使用手指從 origin/master 開始沿着圖形往下,在每一個分叉的地方選擇左邊的分支,從而知道當前的第一父級的變動歷史。
問題是,最初的第一個父級提交次序(從 origin/master 開始)是這樣的:
B, A.
可是當「狐步舞」式的合併被 push
以後,父級的次序變成這樣了:
D, C, A.
這時,B 節點已從 origin/master 第一父級中消失,事實上,B在它的第二父級上。固然,不會有任何資料的丟失,而且 B 節點仍然是 origin/master 的一部分。
可是,這樣父級節點就會有錯綜複雜的關係。你是否知道,tilda
符號(例如 ~N
)指定從第 N 個提交的節點到第一個父節點間的路徑?
你有沒有想要看看你的分支上的每一個提交記錄之間的差別,可是使用 git log -p
顯然會漏掉一些信息,使用 git log -p -m
能獲取更多的信息嗎?
嘗試使用 git log -p -m –first-parent
吧。
你想過要還原一個合併的分支嗎?那你須要爲 git revert
提供 -m parent-number
選項,這時候你就很不但願本身提供的 parent-number
是錯的。
和我一塊兒工做的人,大多數都將第一個父級做爲真正的 master
分支。有意識或無心識地,人們將 git log –first-parent origin/master
視爲重要事物的順序。 至於任何其餘合併進來的分支?嗯,你應該知道他們會怎麼說:
可是「狐步舞」式的合併把這些都混在了一塊兒。請考慮下面的例子,其中 origin/master 分支的一系列的重要提交信息,與你本身的稍微不那麼重要的提交併行:
如今,你終於準備把你的工做併入到 master
中。你輸入 git pull
,或者可能你在一個主題分支上使用 git merge master
命令。那這樣發生了什麼?一個「狐步舞」式的合併就這麼出現了。
一切都沒有什麼大問題,除了當你鍵入 git push
,讓你的遠程倉庫接受它時,你的歷史記錄看起來像這樣:
啥招都沒有,隨它們去吧。除非你重寫 master 分支的歷史而惹怒其餘人,那麼就去這麼瘋吧。
事實上,不要這樣作。
這有幾個方法。我最喜歡的的方式是下面的四步:
爲你的團隊安裝 Atlassian Bitbucket 服務器。
安裝我爲 Bitbucket 服務器寫的插件,名字叫「Bit Booster Commit Graph and More」。 你能夠在下面的連接中找到他們:marketplace.atlassian.com/plugins/com…marketplace.atlassian.com/plugins/com…
在你全部項目中,都點擊 「Protect First Parent Hook」 上的 「Enabled」 按鈕,也就是「啓用」按鈕:
這是我最喜歡的方式,由於它杜絕了「狐步舞」的出現。每當有一個「狐步舞」式的合併被阻擋時,它會打印一隻牛:
$ git commit -m 'my commit'
$ git pull
$ git push
remote: _____________________________________________
remote: / \
remote: | Moo! Your bit-booster license has expired! |
remote: \ /
remote: ---------------------------------------------
remote: \ ^__^
remote: \ (oo)\_______
remote: (__)\ )\/\
remote: ||----w |
remote: || ||
remote:
remote: *** PUSH REJECTED BY Protect-First-Parent HOOK ***
remote:
remote: Merge [da75830d94f5] is not allowed. *Current* master
remote: must appear in the 'first-parent' position of the
remote: subsequent commit.
複製代碼
還有其餘的方法。你能夠禁止直接向 master 分支進行推送,並保證不在 fast-forward
的狀況下合併 pull-requests
。或者培訓你的員工使用 git pull –rebase
命令,而且永遠不要使用 git merge master
。而且一旦你培訓完你的員工,就不要再招聘其餘員工了。
若是你能夠直接訪問遠程倉庫,則能夠設置 pre-receive hook
。 如下的 bash
腳本能夠幫助你開始這項設置:
#/bin/bash
# Copyright (c) 2016 G. Sylvie Davies. http://bit-booster.com/
# Copyright (c) 2016 torek. http://stackoverflow.com/users/1256452/torek
# License: MIT license. https://opensource.org/licenses/MIT
while read oldrev newrev refname
do
if [ "$refname" = "refs/heads/master" ]; then
MATCH=`git log --first-parent --pretty='%H %P' $oldrev..$newrev |
grep $oldrev |
awk '{ print \$2 }'`
if [ "$oldrev" = "$MATCH" ]; then
exit 0
else
echo "*** PUSH REJECTED! FOXTROT MERGE BLOCKED!!! ***"
exit 1
fi
fi
done
複製代碼
push
上去。我該怎麼解決?假設你安裝了預先接收的鉤子,而且阻止你「狐步舞」式的合併。你下一步作什麼?你有三種可能的補救辦法:
rebase
:但請不要使用上面的第三種方法,由於最後的結果被稱爲「僧帽水母」式的合併,這種合併甚至比「狐步舞」式的合併更糟糕。
在最後,其實「狐步舞」式的合併也像其餘的合併那樣。兩個(或多個)提交到一塊兒融合成一個新的記錄節點。就你的代碼庫而言,沒有任何區別。不管 commit A 合併到 commit B 中仍是反過來 commit B 合併到 commit A,從代碼的角度來看最終結果是相同的。
可是,當涉及到你的倉庫的歷史記錄時,以及有效地使用 git 工具集時,「狐步舞」式的合併會有必定的破壞性。經過設置相應的策略來防止其出現,可使你倉庫的歷史記錄更加清晰明瞭,並減小了須要記住的 git 命令選項的範圍。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、React、前端、後端、產品、設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃。