章节 ▾ 第二版

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.cn/docs/git-config 上找到此参考资料。

注意

对于高级用例,您可能需要查看上面提到的文档中的“Conditional includes”。

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 工具进行差异查看和合并冲突解决。

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 命令在您的路径中,那么您可以运行

$ git config --global merge.tool kdiff3

如果您运行此命令而不是设置 extMergeextDiff 文件,Git 将使用 KDiff3 进行合并解决,并使用正常的 Git 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

如果您在使用 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,它查找行开头制表符前的空格。

默认禁用但可以启用的三个是 indent-with-non-tab,它查找以空格而不是制表符开头的行(由 tabwidth 选项控制);tab-in-indent,它监视行缩进部分中的制表符;以及 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

如果您重写已经推送的提交,然后尝试再次推送,或者以其他方式尝试将提交推送到不包含远程分支当前指向的提交的远程分支,您将被拒绝。这通常是好的策略;但在重写的情况下,您可能会认为您知道自己在做什么,并且可以使用 -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 强制策略中学习到。