包括三种配置文件:

  • .gitconfig
  • .gitattributes
  • .gitignore

.gitconfig

.gitignore是Git的忽略规则配置文件,Git 会在如下位置依次进行检查(优先级由高到低):

  1. 第一层是仓库级,在仓库目录下,文件为.git/config
  2. 第二层是用户级,在用户主目录下,文件为~/.gitconfig(主要修改的是用户级配置)
  3. 第三层是系统级,在安装目录下,安装时的选项会保存在这里

Git也可以使用符合XDG规范的配置文件:~/.config/git/config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[user]
name = xxx
email = xxx@xx.com
[init]
# 默认主分支名称
defaultBranch = main
[core]
# 默认编辑器
editor = vim
# 确保git正确显示中文信息
quotepath = false
# 在提交时将所有文本的换行符转换为LF,检出时不转换
autocrlf = input
# 检查文本的换行符,对于混合换行符的文件不允许提交
safecrlf = true
[i18n]
# 确保提交信息utf-8编码
commitEncoding = utf-8
[gui]
# 确保gui使用utf-8编码
encoding = utf-8

.gitattributes

.gitattributes是关于文件格式的配置,这个配置文件主要解决这些问题: 把哪些文件视作文本文件,哪些视作二进制文件,这涉及到 git 的差异比较和合并, 以及换行符的处理(对文本文件可以指定换行符,对二进制文件则不存在换行符的概念)。

这里提供一个参考的.gitattributes 文件,含义见注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 对所有 .bat, .cmd 文件强制使用 CRLF
*.bat text eol=crlf
*.cmd text eol=crlf

# 对所有 .ps1 文件强制使用 CRLF
*.ps1 text eol=crlf
*.psd1 text eol=crlf
*.psm1 text eol=crlf

# 对所有 Visual Studio 解决方案和项目文件强制使用 CRLF
*.sln text eol=crlf
*.vcproj text eol=crlf
*.vcxproj text eol=crlf

# 将 Mathematica 的 .nb 文件视为二进制文件
*.nb binary

更多细节参考官方文档

.gitignore

.gitignore是Git关于排除文件的规则配置,Git 会在如下位置依次进行检查(优先级由高到低):

  1. 从命令行中读取可用的忽略规则
  2. 当前目录定义的.gitignore 规则
  3. 父级目录定义的.gitignore 规则,依次递推向上查找,直到仓库根目录
  4. $GIT_DIR/info/exclude文件中定义的规则
  5. core.excludesfile中定义的全局规则

下面整理一下基本的语法,更完整的语法规则参考官方文档

  1. 空行被直接忽略
  2. 以#开头的行视为注释,不要让注释出现在规则之后,以下语法可能有错误
1
*.log # 所有日志文件
  1. 其余每行表示一个 pattern,行尾的空格被忽略
  2. !意味着取反,即原本被匹配上的文件被忽略,加上!后这些文件则被包含进来,但是这里需要保证它的各级父目录没有被忽略,否则仍然无法包含
  3. pattern 严格区分大小写
  4. pattern 含有特殊字符例如#!时需要使用转义,例如\!\#
  5. pattern 视/为路径分隔符,是否含有/对匹配规则影响很大

在 pattern 不含有/时,执行如下的简单规则:(匹配当前位置和所有子文件夹的所有文件和文件夹)

  • 星号(*)匹配零个或多个任意字符,不包括/
  • 问号(?)只匹配一个任意字符,不包括/
  • [abc]匹配任何一个列在方括号中的字符(要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);
  • 如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字);

在简单规则下,我们不需要考虑路径问题,只需要关注文件名和文件夹名自身的匹配,因为规则会遍历所有子文件夹。

简单规则示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 忽略所有以 ~ 结尾的文件(通常是临时文件)
*~

# 忽略以 .tmp 结尾的临时文件
*.tmp

# 忽略日志文件
*.log

# 忽略备份文件
*.bak

# 忽略 Visual Studio 生成的文件和目录
*.sln
*.vcproj
*.vcxproj
*.vcxproj.filters

# 忽略所有 .html 和 .htm 文件
*.[html,htm]

# 忽略Release和release目录(兼顾开头大小写)
[Rr]elease/

# 忽略所有 .txt 文件
*.txt
# 特别保留 specific.txt 文件
!specific.txt

在 pattern 含有/时,匹配规则会考虑路径问题,更加复杂:

  • 如果在 pattern 的开头或者中部出现/,则只会匹配相对于.gitignore 文件所在位置的相对路径,例如/doc/testdoc/test效果一样
  • 如果在 pattern 的结尾出现/,则只会匹配到文件夹,不会匹配任何文件

使用**/结合还有如下的特殊规则,以满足复杂规则下的需求:

  • 例如**/foo可以匹配任何位置的 foo 文件或文件夹,效果和foo一样
  • 例如**/foo/bar可以匹配任意位置下的 bar 文件或文件夹,但是要求它的父文件夹为 foo
  • 例如/**匹配当前位置下的所有项,abc/**匹配当前位置的子文件夹 abc 下的所有项
  • 例如a/**/b可以匹配任意个中间目录,还可以没有中间目录,包括a/ba/x/ba/x/y/b
  • 在其它情形,即**不直接和/相邻时,效果同*

复杂规则示例

1
2
3
4
5
6
7
8
9
10
11
12
# 忽略build目录自身(也忽略了它的所有子项)
# build可能是当前目录下或者子目录中的build文件夹
build/

# 忽略doc目录下的所有.pdf文件,但不包括它的子目录下的.pdf文件
doc/*.pdf

# 忽略dox目录下的所有.pdf文件,以及它的子目录下的.pdf文件
doc/**/*.pdf

# 忽略所有 .log 文件,包括嵌套目录下的,效果与*.log一样
**/*.log

我们可以先忽略一般项,再添加特殊项

1
2
3
4
5
6
7
8
9
10
11
12
# 忽略所有 .txt 文件
*.txt

# 特别保留 specific.txt 文件
!specific.txt

# 忽略当前位置的build目录下的所有内容,
# 但是不忽略build目录自身,保留这个目录,使得其中的文件可以被特别保留
build/*

# 保留build目录下的指定文件
!build/config.xml

注意这里build/*build/效果是不一样的,前者才支持重新添加 build 中的某些文件。

对于当前目录下以点开头的文件(通常为配置脚本)我们希望默认包含,对于以点开头的文件夹则希望默认排除,可以使用如下设置

1
2
/.*/
!/.*

.gitignore模板

在 Python 项目中 .gitignore 可以参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
__pycache__/
*.py[cod]
*.so
*.dll
*.dylib

.ipynb_checkpoints/

env/
venv/
*.env
*.venv

build/
dist/
*.egg-info/

*.npy
*.npz
*.pt
*.pth

*.log
*.out
*.tmp

在 LaTeX 项目中的 .gitignore 可以参考

1
2
3
4
5
6
7
8
9
10
.aux/

*.pdf
*.synctex.gz
*.synctex.gz.sum.synctex

*.zip
*.tar.gz

indent.log

在 C++ 项目中的 .gitignore 可以参考

1
2
3
4
.cache/
bin/
build*/
lib/

在 MATLAB 项目中的 .gitignore 可以参考

1
2
3
4
*.asv
*.log
*.mat
.ref/

补充

ipynb 文件的额外处理

有时我们需要在 git 仓库中存储 ipynb 文件,但是 ipynb 文件实质是一个 json 文件,除了代码之外,还存储了输入内容,以及运行次数等元数据等不适合存储的信息,可以结合 nbstripout 这个工具自动在 git 添加时对 ipynb 文件进行清理。

安装

1
conda install -c conda-forge nbstripout

这个工具可以直接在命令行使用,例如清理文件中的输出和元数据

1
nbstripout test.ipynb

更普遍的做法是结合 Git 使用,配置如下(仅对当前仓库生效)

1
nbstripout --install --attributes .gitattributes

这里的具体行为是将一部分配置写入到当前仓库的.gitattributes文件中,并添加到版本控制中,内容如下

1
2
3
*.ipynb filter=nbstripout
*.zpln filter=nbstripout
*.ipynb diff=ipynb

还有一部分配置写入 .git/config 文件,内容如下

1
2
3
4
5
6
[filter "nbstripout"]
clean = /path/to/python -m nbstripout
smudge = cat
required = true
[diff "ipynb"]
textconv = /path/to/python -m nbstripout -t

如果直接运行nbstripout --install,则会将配置都写入到 .git/ 目录下的配置文件中,不利于在多个本地仓库中保持一致。也可以执行 --uninstall 撤销配置。

nbstripout 的工作原理为:通过 Git filter 对 .ipynb 文件执行两种操作:

  • clean:提交时清除 notebook 中的输出和元数据
  • smudge:checkout 时不做任何恢复(默认为 cat,即保持文件原样)

注意:

  • git 纳入版本控制的只有清理后的 ipynb 文件,清理的内容无法通过 git 还原。
  • git 在比较工作区和暂存区差异时,自动对工作区中的文件使用 Git filter 进行处理,然后进行比较,这不会影响工作区中的文件,工作目录下实际的 ipynb 文件的输出和元数据不会被修改。
  • 如果当前仓库已有未清理的 ipynb 文件,需要进行额外的处理,因为 filter 只会处理通过 git add 添加的文件。