Git
章節 ▾ 第二版

8.1 自訂 Git - Git 設定

到目前為止,我們已經介紹了 Git 的運作方式和使用方法的基本概念,並且介紹了 Git 提供的一些工具,以協助您輕鬆有效地使用它。在本章中,我們將介紹幾個重要的組態設定和鉤子系統,說明如何讓 Git 以更客製化的方式運作。使用這些工具,可以輕鬆地讓 Git 按照您、您的公司或您的團隊所需要的方式工作。

Git 設定

如您在起步中簡短讀到的內容,您可以使用 git config 命令指定 Git 設定。您做的第一件事之一就是設定您的姓名和電子郵件地址

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

現在您將學習一些更有趣的選項,您可以透過這種方式設定這些選項來自訂您的 Git 用法。

首先,快速回顧一下:Git 使用一系列設定檔來確定您可能需要的非預設行為。Git 首先尋找這些值的位置是在系統範圍的 [path]/etc/gitconfig 檔案中,該檔案包含適用於系統上每個使用者及其所有儲存庫的設定。如果您將 --system 選項傳遞給 git config,它會專門從這個檔案讀取和寫入。

Git 尋找的下一個位置是 ~/.gitconfig(或 ~/.config/git/config)檔案,該檔案是特定於每個使用者的。您可以透過傳遞 --global 選項,使 Git 從此檔案讀取和寫入。

最後,Git 會在您目前正在使用的任何儲存庫的 Git 目錄(.git/config)中的設定檔中尋找設定值。這些值是特定於該單一儲存庫的,並且表示將 --local 選項傳遞給 git config。如果您沒有指定您想要使用的層級,則這是預設值。

每個「層級」(系統、全域、本機)都會覆寫前一個層級中的值,因此例如,.git/config 中的值會優先於 [path]/etc/gitconfig 中的值。

注意

Git 的設定檔是純文字格式,因此您也可以透過手動編輯檔案並插入正確的語法來設定這些值。不過,通常執行 git config 命令會比較容易。

基本用戶端設定

Git 辨識的設定選項分為兩類:用戶端和伺服器端。大多數選項是用戶端 — 設定您的個人工作偏好設定。支援許多、許多組態選項,但其中很大一部分僅在某些邊緣情況下有用;我們在這裡只介紹最常見和有用的選項。如果您想查看您的 Git 版本辨識的所有選項的清單,您可以執行

$ man git-config

此命令會非常詳細地列出所有可用的選項。您也可以在 https://git.dev.org.tw/docs/git-config 找到此參考資料。

注意

對於進階使用案例,您可能需要在上述文件中查找「條件包含」。

core.editor

預設情況下,Git 會使用您透過其中一個 shell 環境變數 VISUALEDITOR 設定為預設文字編輯器的任何內容,否則會回退到 vi 編輯器來建立和編輯您的提交和標籤訊息。若要將該預設值變更為其他值,您可以使用 core.editor 設定

$ git config --global core.editor emacs

現在,無論您的預設 shell 編輯器設定為何,Git 都會啟動 Emacs 來編輯訊息。

commit.template

如果您將此設定為您系統上某個檔案的路徑,Git 會在您提交時使用該檔案作為預設的初始訊息。建立自訂提交範本的價值在於,您可以使用它來提醒自己(或其他人)在建立提交訊息時應遵守的正確格式和樣式。

例如,考慮一個位於 ~/.gitmessage.txt 的範本檔案,其內容如下所示

Subject line (try to keep under 50 characters)

Multi-line description of commit,
feel free to be detailed.

[Ticket: X]

請注意,這個提交範本如何提醒提交者保持主旨行簡短(為了 git log --oneline 輸出的簡潔),並在下方新增更多細節,以及在存在時參考問題或錯誤追蹤器的票號。

若要告訴 Git 在您執行 git commit 時使用它作為編輯器中顯示的預設訊息,請設定 commit.template 組態值

$ git config --global commit.template ~/.gitmessage.txt
$ git commit

然後,當您提交時,您的編輯器會開啟類似如下的內容,作為您的預留位置提交訊息

Subject line (try to keep under 50 characters)

Multi-line description of commit,
feel free to be detailed.

[Ticket: X]
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   lib/test.rb
#
~
~
".git/COMMIT_EDITMSG" 14L, 297C

如果您的團隊有提交訊息政策,那麼在您的系統上放置該政策的範本,並設定 Git 預設使用它,可以幫助提高定期遵守該政策的可能性。

core.pager

此設定決定當 Git 分頁輸出(例如 logdiff)時所使用的分頁器。您可以將其設定為 more 或您喜歡的分頁器(預設為 less),或者您可以將其設定為空白字串以關閉它

$ git config --global core.pager ''

如果您執行該命令,Git 將列印所有命令的完整輸出,無論它們有多長。

user.signingkey

如果您正在建立簽署的註解標籤(如「簽署您的工作」中所討論的),將您的 GPG 簽署金鑰設定為組態設定會讓事情變得更容易。像這樣設定您的金鑰 ID

$ git config --global user.signingkey <gpg-key-id>

現在,您可以簽署標籤,而無需每次都使用 git tag 命令指定您的金鑰

$ git tag -s <tag-name>

core.excludesfile

您可以將模式放入專案的 .gitignore 檔案中,以讓 Git 不將它們視為未追蹤的檔案,或者在您對它們執行 git add 時不嘗試暫存它們,如「忽略檔案」中所討論的。

但有時您希望為所有您使用的儲存庫忽略某些檔案。如果您的電腦執行 macOS,您可能很熟悉 .DS_Store 檔案。如果您偏好的編輯器是 Emacs 或 Vim,您會知道以 ~.swp 結尾的檔案名稱。

此設定可讓您編寫一種全域 .gitignore 檔案。如果您建立一個包含以下內容的 ~/.gitignore_global 檔案

*~
.*.swp
.DS_Store

…並且您執行 git config --global core.excludesfile ~/.gitignore_global,Git 將永遠不會再為這些檔案打擾您。

help.autocorrect

如果您輸入錯誤的命令,它會顯示類似這樣的訊息

$ git chekcout master
git: 'chekcout' is not a git command. See 'git --help'.

The most similar command is
    checkout

Git 會盡力嘗試找出您的意思,但它仍然拒絕執行。如果您將 help.autocorrect 設定為 1,Git 實際上會為您執行此命令

$ git chekcout master
WARNING: You called a Git command named 'chekcout', which does not exist.
Continuing under the assumption that you meant 'checkout'
in 0.1 seconds automatically...

請注意「0.1 秒」這部分。help.autocorrect 實際上是一個整數,表示十分之一秒。因此,如果您將其設定為 50,Git 會給您 5 秒的時間來改變主意,然後再執行自動更正的命令。

Git 中的色彩

Git 完全支援彩色終端輸出,這極大地有助於快速且輕鬆地視覺解析命令輸出。許多選項可以幫助您將色彩設定為您的偏好。

color.ui

Git 會自動為其大部分輸出著色,但如果您不喜歡這種行為,則有一個主開關。若要關閉 Git 的所有彩色終端輸出,請執行此操作

$ git config --global color.ui false

預設設定為 auto,當輸出直接傳送到終端時會著色,但當輸出重新導向到管道或檔案時,會省略色彩控制碼。

您也可以將其設定為 always,以忽略終端和管道之間的差異。您很少會想要這樣做;在大多數情況下,如果您希望重新導向的輸出包含色彩碼,您可以改為將 --color 旗標傳遞給 Git 命令,以強制它使用色彩碼。預設設定幾乎總是您想要的。

color.*

如果您想更具體地了解哪些命令會著色以及如何著色,Git 提供了動詞特定的著色設定。這些設定的每個都可以設定為 truefalsealways

color.branch
color.diff
color.interactive
color.status

此外,如果您想要覆寫每個色彩,這些設定中的每個都有子設定,您可以使用這些子設定來設定輸出特定部分的特定色彩。例如,若要將 diff 輸出中的元資訊設定為藍色前景、黑色背景和粗體文字,您可以執行

$ git config --global color.diff.meta "blue black bold"

您可以將色彩設定為以下任何值:normalblackredgreenyellowbluemagentacyanwhite。如果您想要先前範例中的粗體之類的屬性,您可以從 bolddimul(底線)、blinkreverse(交換前景和背景)中選擇。

外部合併和差異工具

儘管 Git 有一個內部的 diff 實作,這就是我們在這本書中一直展示的內容,但您可以改為設定外部工具。您也可以設定圖形化的合併衝突解決工具,而不必手動解決衝突。我們將示範設定 Perforce Visual Merge Tool (P4Merge) 來執行您的 diff 和合併解決,因為它是一個不錯的圖形工具並且是免費的。

如果您想嘗試一下,P4Merge 適用於所有主要平台,因此您應該能夠做到。我們將在範例中使用在 macOS 和 Linux 系統上運作的路徑名稱;對於 Windows,您必須將 /usr/local/bin 變更為您環境中的可執行路徑。

首先,從 Perforce 下載 P4Merge。接下來,您將設定外部包裝腳本來執行您的命令。我們將使用可執行檔的 macOS 路徑;在其他系統中,它將是您的 p4merge 二進位檔案安裝的位置。設定一個名為 extMerge 的合併包裝腳本,它會使用所有提供的引數呼叫您的二進位檔案

$ cat /usr/local/bin/extMerge
#!/bin/sh
/Applications/p4merge.app/Contents/MacOS/p4merge $*

diff 包裝器會檢查是否提供了七個引數,並將其中兩個引數傳遞給您的合併腳本。預設情況下,Git 會將以下引數傳遞給 diff 程式

path old-file old-hex old-mode new-file new-hex new-mode

因為您只需要 old-filenew-file 引數,所以您可以使用包裝腳本來傳遞您需要的引數。

$ cat /usr/local/bin/extDiff
#!/bin/sh
[ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"

您還需要確保這些工具是可執行的

$ sudo chmod +x /usr/local/bin/extMerge
$ sudo chmod +x /usr/local/bin/extDiff

現在,您可以設定您的組態檔案以使用您的自訂合併解決和差異工具。這需要一些自訂設定:merge.tool 用於告訴 Git 要使用的策略、mergetool.<tool>.cmd 用於指定如何執行命令、mergetool.<tool>.trustExitCode 用於告訴 Git 該程式的結束代碼是否表示成功的合併解決,以及 diff.external 用於告訴 Git 要執行什麼命令來執行差異。因此,您可以執行四個組態命令

$ git config --global merge.tool extMerge
$ git config --global mergetool.extMerge.cmd \
  'extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"'
$ git config --global mergetool.extMerge.trustExitCode false
$ git config --global diff.external extDiff

或者您可以編輯您的 ~/.gitconfig 檔案以新增這些行

[merge]
  tool = extMerge
[mergetool "extMerge"]
  cmd = extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
  trustExitCode = false
[diff]
  external = extDiff

在完成所有這些設定後,如果您執行類似這樣的 diff 命令

$ git diff 32d1776b1^ 32d1776b1

Git 不會在命令列上取得 diff 輸出,而是會啟動 P4Merge,它看起來像這樣

P4Merge
圖 168. P4Merge

如果您嘗試合併兩個分支,並隨後發生合併衝突,您可以執行命令 git mergetool;它會啟動 P4Merge,讓您透過該 GUI 工具解決衝突。

這個包裝器設定的好處是您可以輕鬆地變更您的 diff 和合併工具。例如,若要將您的 extDiffextMerge 工具變更為改為執行 KDiff3 工具,您只需編輯您的 extMerge 檔案

$ cat /usr/local/bin/extMerge
#!/bin/sh
/Applications/kdiff3.app/Contents/MacOS/kdiff3 $*

現在,Git 將使用 KDiff3 工具進行 diff 檢視和合併衝突解決。

Git 預設設定為使用許多其他合併解決工具,而無需您設定 cmd 組態。若要查看它支援的工具列表,請嘗試此操作

$ git mergetool --tool-help
'git mergetool --tool=<tool>' may be set to one of the following:
        emerge
        gvimdiff
        gvimdiff2
        opendiff
        p4merge
        vimdiff
        vimdiff2

The following tools are valid, but not currently available:
        araxis
        bc3
        codecompare
        deltawalker
        diffmerge
        diffuse
        ecmerge
        kdiff3
        meld
        tkdiff
        tortoisemerge
        xxdiff

Some of the tools listed above only work in a windowed
environment. If run in a terminal-only session, they will fail.

如果您不想要將 KDiff3 用於差異,而是想要將它僅用於合併解決,並且 kdiff3 命令位於您的路徑中,則您可以執行

$ git config --global merge.tool kdiff3

如果您執行此命令而不是設定 extMergeextDiff 檔案,Git 將使用 KDiff3 進行合併解決,並使用一般的 Git diff 工具進行差異。

格式化和空白

格式化和空白問題是許多開發人員在協作時遇到的一些更令人沮喪且微妙的問題,尤其是在跨平台協作時。由於編輯器會悄悄地引入它們,因此修補程式或其他協作工作很容易引入微妙的空白變更,而且如果您的檔案曾接觸過 Windows 系統,它們的行尾可能會被取代。Git 有一些組態選項可以幫助解決這些問題。

core.autocrlf

如果您在 Windows 上編寫程式,並與不是在 Windows 上工作的人員協作(反之亦然),您可能會在某個時間點遇到行尾問題。這是因為 Windows 在其檔案中使用歸位字元和換行字元作為換行符號,而 macOS 和 Linux 系統僅使用換行字元。這是一個微妙但令人難以置信的跨平台工作煩惱事實;Windows 上的許多編輯器會悄悄地將現有的 LF 樣式行尾取代為 CRLF,或者在使用者按下 Enter 鍵時插入兩個行尾字元。

當您將檔案新增至索引時,Git 可以透過自動將 CRLF 行尾轉換為 LF 來處理此問題,反之,當它將程式碼簽出到您的檔案系統時也可以處理此問題。您可以使用 core.autocrlf 設定來開啟此功能。如果您使用的是 Windows 電腦,請將其設定為 true — 這會在您簽出程式碼時將 LF 結尾轉換為 CRLF

$ git config --global core.autocrlf true

如果您使用的是使用 LF 行尾的 Linux 或 macOS 系統,那麼您不希望 Git 在您簽出檔案時自動轉換它們;但是,如果意外引入了帶有 CRLF 結尾的檔案,您可能希望 Git 修正它。您可以告訴 Git 在提交時將 CRLF 轉換為 LF,但不進行反向轉換,方法是將 core.autocrlf 設定為 input

$ git config --global core.autocrlf input

此設定應該讓您在 Windows 簽出時使用 CRLF 結尾,但在 macOS 和 Linux 系統以及儲存庫中使用 LF 結尾。

如果您是正在執行僅限 Windows 專案的 Windows 程式設計師,那麼您可以關閉此功能,方法是將組態值設定為 false,從而在儲存庫中記錄歸位字元

$ git config --global core.autocrlf false

core.whitespace

Git 預設設定為偵測並修正一些空白問題。它可以尋找六個主要的空白問題 — 預設啟用三個,可以關閉;預設停用三個,但可以啟用。

預設啟用的三個是 blank-at-eol,它會尋找行尾的空格;blank-at-eof,它會注意檔案末尾的空白行;以及 space-before-tab,它會尋找位於行首 Tab 前面的空格。

預設停用但可以啟用的三個是 indent-with-non-tab,它會尋找以空格而不是 Tab 開頭的行(並由 tabwidth 選項控制);tab-in-indent,它會監看行縮排部分的 Tab;以及 cr-at-eol,它會告訴 Git 行尾的歸位字元是可以的。

您可以透過將 core.whitespace 設定為您想要開啟或關閉的值(以逗號分隔),來告訴 Git 您想要啟用哪些這些值。您可以透過在其名稱前面加上 - 來停用選項,或者透過將其保留在設定字串之外來使用預設值。例如,如果您希望除了 space-before-tab 之外的所有設定都已設定,您可以執行此操作(其中 trailing-space 是涵蓋 blank-at-eolblank-at-eof 的簡寫)

$ git config --global core.whitespace \
    trailing-space,-space-before-tab,indent-with-non-tab,tab-in-indent,cr-at-eol

或者您也可以只指定自訂部分

$ git config --global core.whitespace \
    -space-before-tab,indent-with-non-tab,tab-in-indent,cr-at-eol

當你執行 git diff 命令時,Git 會偵測這些問題,並嘗試以顏色標示它們,讓你在提交之前能夠修正。當你使用 git apply 套用補丁時,它也會使用這些值來幫助你。在套用補丁時,你可以要求 Git 在套用具有指定空白問題的補丁時發出警告。

$ git apply --whitespace=warn <patch>

或者,你可以讓 Git 嘗試在套用補丁之前自動修正這些問題。

$ git apply --whitespace=fix <patch>

這些選項也適用於 git rebase 命令。如果你提交了空白問題,但尚未推送到上游,你可以執行 git rebase --whitespace=fix,讓 Git 在重寫補丁時自動修正空白問題。

伺服器組態

Git 的伺服器端可用的組態選項並不多,但有一些有趣的選項你可能需要注意。

receive.fsckObjects

Git 能夠確保在推送過程中接收到的每個物件仍然與其 SHA-1 校驗和匹配,並指向有效的物件。但是,它預設不執行此操作;這是一個相當耗費資源的操作,可能會減慢操作速度,尤其是在大型儲存庫或推送時。如果你希望 Git 在每次推送時都檢查物件一致性,可以將 receive.fsckObjects 設定為 true 來強制執行。

$ git config --system receive.fsckObjects true

現在,Git 會在接受每次推送之前檢查你的儲存庫的完整性,以確保有問題(或惡意)的客戶端不會引入損壞的資料。

receive.denyNonFastForwards

如果你重新變基(rebase)已經推送的提交,然後嘗試再次推送,或者嘗試將一個提交推送到遠端分支,而該提交不包含遠端分支目前指向的提交,你將會被拒絕。這通常是好的策略;但在重新變基的情況下,你可能認為你知道自己在做什麼,並且可以使用推送命令的 -f 標誌強制更新遠端分支。

要告訴 Git 拒絕強制推送,請設定 receive.denyNonFastForwards

$ git config --system receive.denyNonFastForwards true

另一種方法是透過伺服器端接收掛鉤,我們稍後會介紹。這種方法可以讓你執行更複雜的操作,例如拒絕向特定用戶子集進行非快速轉發。

receive.denyDeletes

denyNonFastForwards 策略的一種替代方法是讓用戶刪除分支,然後使用新的參考將其推送回去。為避免這種情況,請將 receive.denyDeletes 設定為 true。

$ git config --system receive.denyDeletes true

這會拒絕任何分支或標籤的刪除 — 任何用戶都無法執行此操作。要刪除遠端分支,你必須手動從伺服器移除 ref 檔案。還有更多有趣的方法可以透過 ACL 在每個用戶的基礎上執行此操作,你將在一個 Git 強制策略的範例中學習到。

scroll-to-top