Git 配置文件
包括三种配置文件:
.gitconfig.gitattributes.gitignore
.gitconfig
.gitignore 是 Git 的忽略规则配置文件,Git 会在如下位置依次进行检查(优先级由高到低):
- 第一层是仓库级,在仓库目录下,文件为
.git/config - 第二层是用户级,在用户主目录下,文件为
~/.gitconfig(主要修改的是用户级配置) - 第三层是系统级,在安装目录下,安装时的选项会保存在这里
Git也可以使用符合XDG规范的配置文件:
~/.config/git/config。
1 | [user] |
.gitattributes
.gitattributes 是关于文件格式的配置,这个配置文件主要解决这些问题:
把哪些文件视作文本文件,哪些视作二进制文件,这涉及到 git 的差异比较和合并,
以及换行符的处理(对文本文件可以指定换行符,对二进制文件则不存在换行符的概念)。
这个文件通常不需要提前准备,等到仓库中出现了对应文件,导致 git 报错时添加对应条目即可。
这里提供一个参考的 .gitattributes 文件,含义见注释
1 | # 对所有 .bat, .cmd 文件强制使用 CRLF |
除此之外,Git LFS 的相关配置也会记录在 .gitattributes 文件中。
更多细节参考 官方文档。
.gitignore
.gitignore 是 Git 关于排除文件的规则配置,Git 会在如下位置依次进行检查(优先级由高到低):
- 从命令行中读取可用的忽略规则
- 当前目录定义的
.gitignore规则 - 父级目录定义的
.gitignore规则,依次递推向上查找,直到仓库根目录 $GIT_DIR/info/exclude文件中定义的规则core.excludesfile中定义的全局规则
下面整理一下基本的语法,更完整的语法规则参考官方文档。
- 空行被直接忽略
- 以#开头的行视为注释,不要让注释出现在规则之后,以下语法可能有错误
1 | *.log # 所有日志文件 |
- 其余每行表示一个 pattern,行尾的空格被忽略
!意味着取反,即原本被匹配上的文件被忽略,加上!后这些文件则被包含进来,但是这里需要保证它的各级父目录没有被忽略,否则仍然无法包含- pattern 严格区分大小写
- pattern 含有特殊字符例如
#,!时需要使用转义,例如\!,\# - pattern 视
/为路径分隔符,是否含有/对匹配规则影响很大
在 pattern 不含有 / 时,执行如下的简单规则:(匹配当前位置和所有子文件夹的所有文件和文件夹)
- 星号(
*)匹配零个或多个任意字符,不包括/; - 问号(
?)只匹配一个任意字符,不包括/; - [
abc]匹配任何一个列在方括号中的字符(要么匹配一个 a,要么匹配一个 b,要么匹配一个 c); - 如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [
0-9] 表示匹配所有 0 到 9 的数字);
在简单规则下,我们不需要考虑路径问题,只需要关注文件名和文件夹名自身的匹配,因为规则会遍历所有子文件夹。
简单规则示例:
1 | # 忽略所有以 ~ 结尾的文件(通常是临时文件) |
在 pattern 含有 / 时,匹配规则会考虑路径问题,更加复杂:
- 如果在 pattern 的开头或者中部出现
/,则只会匹配相对于 .gitignore 文件所在位置的相对路径,例如/doc/test和doc/test效果一样 - 如果在 pattern 的结尾出现
/,则只会匹配到文件夹,不会匹配任何文件
使用 ** 和 / 结合还有如下的特殊规则,以满足复杂规则下的需求:
- 例如
**/foo可以匹配任何位置的 foo 文件或文件夹,效果和foo一样 - 例如
**/foo/bar可以匹配任意位置下的 bar 文件或文件夹,但是要求它的父文件夹为 foo - 例如
/**匹配当前位置下的所有项,abc/**匹配当前位置的子文件夹 abc 下的所有项 - 例如
a/**/b可以匹配任意个中间目录,还可以没有中间目录,包括a/b,a/x/b和a/x/y/b等 - 在其它情形,即
**不直接和/相邻时,效果同*
复杂规则示例
1 | # 忽略build目录自身(也忽略了它的所有子项) |
我们可以先忽略一般项,再添加特殊项
1 | # 忽略所有 .txt 文件 |
注意这里 build/* 和 build/ 效果是不一样的,前者才支持重新添加 build 中的某些文件。
对于当前目录下以点开头的文件(通常为配置脚本)我们希望默认包含,对于以点开头的文件夹则希望默认排除,可以使用如下设置
1 | /.*/ |
.gitignore 模板
在 Python 项目中 .gitignore 可以参考
1 | __pycache__/ |
在 LaTeX 项目中的 .gitignore 可以参考
1 | .aux/ |
默认不添加 pdf,但是排除了几种例外情况:tikz 生成的 pdf,以及手动指定保留的 pdf(命名含有
-keep或者放在.pdfkeep子文件夹下)
在 C++ 项目中的 .gitignore 可以参考
1 | .cache/ |
在 MATLAB 项目中的 .gitignore 可以参考
1 | *.asv |
补充
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 | *.ipynb filter=nbstripout |
还有一部分配置写入 .git/config 文件,内容如下
1 | [filter "nbstripout"] |
如果直接运行 nbstripout --install,则会将配置都写入到 .git/ 目录下的配置文件中,不利于在多个本地仓库中保持一致。也可以执行 --uninstall 撤销配置。
nbstripout 的工作原理为:通过 Git filter 对 .ipynb 文件执行两种操作:
- clean:提交时清除 notebook 中的输出和元数据
- smudge:checkout 时不做任何恢复(默认为 cat,即保持文件原样)
注意:
- git 纳入版本控制的只有清理后的 ipynb 文件,清理的内容无法通过 git 还原。
- git 在比较工作区和暂存区差异时,自动对工作区中的文件使用 Git filter 进行处理,然后进行比较,这不会影响工作区中的文件,工作目录下实际的 ipynb 文件的输出和元数据不会被修改。
- 如果当前仓库已有未清理的 ipynb 文件,需要进行额外的处理,因为 filter 只会处理通过
git add添加的文件。
修改版本历史中的个人信息
有时我们需要在保留版本历史的前提下,修改其中涉及到的个人信息(user.name,user.email),下面提供一个底层命令参考。
1 | git filter-branch --env-filter ' |
这种修改会导致提交历史被重写,但是版本历史中的实质内容不变,只是由于 name 和 email 的修改会导致哈希值改变,需要强制推送。
注意,在 GitHub 网站界面直接创建的提交通常会带有 GitHub 生成的 GPG 签名,因此在网页上会显示 Verified,上述重写机制在遇到这类提交时可能会出现无法签名,因此会重写对应的 commit。
