章節 ▾ 第二版

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-scm.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 輸出),在此之後新增更多詳細資訊,並在存在問題或 Bug 跟蹤器門票號時引用它們。

要告訴 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(交換前景和背景)。

外部合併和 diff 工具

儘管 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

現在你可以設定你的配置檔案來使用自定義的合併解決和 diff 工具。這需要一系列自定義設定:merge.tool 來告訴 Git 使用哪種策略,mergetool.<tool>.cmd 來指定如何執行命令,mergetool.<tool>.trustExitCode 來告訴 Git 該程式的退出程式碼是否表示合併解決成功,以及 diff.external 來告訴 Git 執行哪個命令進行 diff。所以,你可以執行四個配置命令:

$ 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 將會啟動 P4Merge,而不是在命令列中顯示 diff 輸出,其介面看起來像這樣:

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 進行 diff,而只想將其用於合併解決,並且 kdiff3 命令在你的 PATH 中,那麼你可以執行:

$ git config --global merge.tool kdiff3

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

格式和空格

格式和空格問題是許多開發人員在協作(尤其是跨平臺協作)時遇到的更令人沮喪和微妙的問題。由於編輯器會默默地引入它們,並且如果你的檔案觸碰到 Windows 系統,它們的行尾可能會被替換,因此補丁或其他協作工作很容易引入細微的空格更改。Git 有一些配置選項可以幫助解決這些問題。

core.autocrlf

如果你在 Windows 上程式設計並與非 Windows 使用者(反之亦然)合作,你可能會在某個時候遇到行尾問題。這是因為 Windows 在其檔案中使用回車符和換行符來表示新行,而 macOS 和 Linux 系統僅使用換行符。這是一個微妙但極其惱人的跨平臺工作事實;許多 Windows 上的編輯器會默默地將現有的 LF 風格行尾替換為 CRLF,或者在使用者按下回車鍵時插入兩個行尾字元。

Git 可以透過在將檔案新增到索引時自動轉換 CRLF 行尾為 LF,反之亦然,在將程式碼檢出到你的檔案系統時,來處理這個問題。你可以使用 core.autocrlf 設定來啟用此功能。如果你在 Windows 機器上,將其設定為 true——這會在你檢出程式碼時將 LF 結尾轉換為 CRLF。

$ git config --global core.autocrlf true

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

$ 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,它查詢行首製表符前的空格。

預設停用的三項是 indent-with-non-tab,它查詢以空格而不是製表符開頭的行(由 tabwidth 選項控制);tab-in-indent,它監視行縮排部分中的製表符;以及 cr-at-eol,它告訴 Git 行尾的回車符是可以的。

你可以透過將 core.whitespace 設定為你想要開啟或關閉的值(用逗號分隔)來告訴 Git 你想要啟用哪些。你可以透過在選項名稱前加上 - 來停用一個選項,或者透過將其從設定字串中完全省略來使用預設值。例如,如果你想要除 space-before-tab 之外的所有設定,你可以這樣做(其中 trailing-spaceblank-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 會在你執行 git diff 命令時檢測這些問題,並嘗試為它們著色,以便你可能在提交之前修復它們。它還將使用這些值來幫助你應用補丁,例如使用 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 了已經推送過的提交然後再次嘗試推送,或者以其他方式嘗試將一個提交推送到遠端分支,而該遠端分支不包含當前指向的提交,你將被拒絕。這通常是一個好的策略;但在 rebase 的情況下,你可能認為你知道你在做什麼,並且可以使用 -f 標誌來強制更新遠端分支。

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

$ git config --system receive.denyNonFastForwards true

另一種方法是透過伺服器端接收鉤子,我們將在稍後介紹。該方法允許你做更復雜的事情,例如拒絕特定使用者進行非快進式推送。

receive.denyDeletes

denyNonFastForwards 策略的一個變通方法是讓使用者刪除分支然後重新推送。為了避免這種情況,將 receive.denyDeletes 設定為 true:

$ git config --system receive.denyDeletes true

這將拒絕刪除任何分支或標籤——沒有使用者可以這樣做。要刪除遠端分支,你必須手動從伺服器中刪除 ref 檔案。正如你將在 Git 強制策略示例 中學到的那樣,還有更有趣的基於使用者的方法可以透過 ACL 來實現。