- 原文地址:Protect our Git Repos, Stop Foxtrots Now!
- 原文作者:Sylvie Davies
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:LeviDing
- 校對者:薛定諤的貓,luisliuchao
舞者們正準備跳狐步舞。
首先,什麼是“狐步舞”式的合併?
“狐步舞”式的合併是 git commit
的一個特別不好的具體順序。如同在戶外看到的“狐步舞”,這種commits序列像這個樣子:
但在公開場合很少會見到“狐步舞”。它們隱藏在樹冠之間,樹枝之間。我稱它們“狐步舞”式,是因為他們交叉的樣子,他們看起來像同步舞蹈的舞步順序:
還有一些人也提到“狐步舞”式的合併,但它們從來沒有直接說出它的名字。例如,Junio C. Hamano 的部落格有有趣的 --first-parent
,還有有趣的非快進方式(Non-Fast-Forward)。David Lowe 的 nestoria.com 有關於保持一致的線性歷史記錄的文章。此外還有一大堆人告訴你要避免使用 git pull
,而是使用 git pull –rebase
。為什麼?主要是為了避免一般的合併和提交時的錯誤,此外還可以避免出現該死的“狐步舞”式的提交。
“狐步舞”式的合併真的很不好嗎?是的。
它們顯然不如僧帽水母那樣糟糕。但是“狐步舞”式的合併也是不好的,你不希望你的 git 倉庫裡有它們的身影。
“狐步舞”式的合併為什麼不好?
“狐步舞”式的合併不好,因為它會改變 origin/master
分支的“第一父級”的地位。
合併提交記錄的父級是有序的。第一個父級是 HEAD
。第二個父級是用 git merge
命令提交的。
你可以像下面這樣想:
git checkout 1st-parent
git merge 2nd-parent
複製程式碼
如果你是 octopus 的說客:
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
,讓你的遠端倉庫接受它時,你的歷史記錄看起來像這樣:
對於已經混入了“狐步舞”式的合併的 git 專案應該怎麼做?
啥招都沒有,隨它們去吧。除非你重寫 master 分支的歷史而惹怒其他人,那麼就去這麼瘋吧。
事實上,不要這樣做。
如何防止未來“狐步舞”式的合併出現在我的 git 專案中?
這有幾個方法。我最喜歡的的方式是下面的四步:
-
為你的團隊安裝 Atlassian Bitbucket 伺服器。
-
安裝我為 Bitbucket 伺服器寫的外掛,名字叫“Bit Booster Commit Graph and More”。 你可以在下面的連結中找到他們:marketplace.atlassian.com/plugins/com…marketplace.atlassian.com/plugins/com…
-
在你所有專案中,都點選 “Protect First Parent Hook” 上的 “Enabled” 按鈕,也就是“啟用”按鈕:
- 你可以在試用許可結束前免費使用31天。(感覺它好用的話,可以在試用期後進行購買)。
這是我最喜歡的方式,因為它杜絕了“狐步舞”的出現。每當有一個“狐步舞”式的合併被阻擋時,它會列印一隻牛:
$ 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
:
- 撤銷你之前的合併,使你的 origin/master 分支成為第一父級:
- 在“狐步舞”式的合併後建立第二個合併並提交,以恢復 origin/master 的第一父級的地位。
但請不要使用上面的第三種方法,因為最後的結果被稱為“僧帽水母”式的合併,這種合併甚至比“狐步舞”式的合併更糟糕。
總結
在最後,其實“狐步舞”式的合併也像其他的合併那樣。兩個(或多個)提交到一起融合成一個新的記錄節點。就你的程式碼庫而言,沒有任何區別。無論 commit A 合併到 commit B 中還是反過來 commit B 合併到 commit A,從程式碼的角度來看最終結果是相同的。
但是,當涉及到你的倉庫的歷史記錄時,以及有效地使用 git 工具集時,“狐步舞”式的合併會有一定的破壞性。通過設定相應的策略來防止其出現,可以使你倉庫的歷史記錄更加清晰明瞭,並減少了需要記住的 git 命令選項的範圍。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、React、前端、後端、產品、設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃。