章节 ▾ 第二版

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 找到这些参考资料。

注意

对于高级用例,你可能想在上述文档中查找“条件包含”。

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 来实现。