这里考虑的是使用 git 进行版本控制的跨平台项目,主要考虑普通文本以及 C++项目中需要的配置文件。 下文包括这些配置文件的基本介绍以及模板。

.editorconfig

通过 .editconfig,我们可以实现跨平台,跨 IDE 的文本统一控制,常见的设置包括 tab,文本编码,换行符等。很多代码编辑器和IDE都支持 .editconfig:VS 直接支持,VScode 需要使用官方插件来提供支持。

某些文件对格式是非常敏感的,我们需要对此进行专门设置:

  • Makefile 必须使用 tab,不能使用空格替代!
  • bat 脚本,pwsh 脚本以及 VS 配置文件这些与windows平台深度绑定的,建议使用 CRLF 换行符,否则可能导致异常
  • markdown 文件允许结尾的空格
  • 对数据文件不允许更改换行符
  • 对 build 文件夹,out 文件夹和 data 文件夹的内容撤销所有设置

为了达到上述要求,对应的.editconfig 文件示例如下

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# fenglielie@qq.com
# .editorconfig template
# 2023-09-14

root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

# must use tab
[Makefile]
indent_style = tab

# must use crlf
[*.{bat,cmd,cmd.*}]
end_of_line = crlf

[*.{ps1,ps1.*}]
end_of_line = crlf

[*.{sln,vcproj,vcxproj,props,csproj,fsproj,vbproj,dbproj,shproj}*]
end_of_line = crlf

[*.md]
trim_trailing_whitespace = false

# data files
[*.{dat,csv,log}]
end_of_line = unset

# unset all
[{**build/**,**out/**,**data/**}]
charset = unset
end_of_line = unset
indent_style = unset
indent_size = unset
insert_final_newline = unset
trim_trailing_whitespace = unset

说明:

  • 使用root = true表明这是最终的 .editconfig 文件,否则还可能尝试从父目录中寻找,默认会递归向上查找,即使存放位置在项目文件夹之外,可以在 windows 的每个盘符根目录下放置一份即可
  • 这里首先使用[*]匹配所有文件进行设置,后面对具体的文件进行单独设置
  • #开头的为注释行,空行和注释行都会被忽略
  • 两个规则冲突时,后文的匹配规则会覆盖前文中的规则

.gitattributes

我们希望使用这个配置文件解决什么问题呢:主要是 git 把哪些文件视作文本文件,哪些视作二进制文件,这涉及到 git 的差异比较和合并,以及换行符的处理,参考官方文档

这里提供一个参考的.gitattributes 文件,主要是在 Git 中适配前面的.editconfig 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
# 对所有 .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

.clang-tidy

clang-tidy 可以提供 C++的静态语法检查,包括很多具体规则的检查,可以使用如下的注释去关闭特定位置的特定检查

1
2
3
4
5
6
7
8
9
10
11
12
13
关闭当前行的所有检查 // NOLINT
关闭当前行的特定检查 // NOLINT(xxx)

关闭下一行的 // NOLINTNEXTLINE

关闭一部分区域的
// NOLINTBEGIN(check-name)
...
// NOLINTEND(check-name)

// NOLINTBEGIN
...
// NOLINTEND

clang-tidy 模板如下

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# fenglielie@qq.com
# clang-tidy template
# 2023-09-14

Checks: 'bugprone-*,
-bugprone-easily-swappable-parameters,
cert-*,
-cert-env33-c,
-cert-dcl50-cpp,
-cert-err33-c,
clang-analyzer-*,
cppcoreguidelines-*,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-owning-memory,
google-*,
-google-objc-*,
-google-readability-todo,
-google-readability-casting,
hicpp-*,
-hicpp-vararg,
llvm-*,
misc-*,
-misc-non-private-member-variables-in-classes,
-misc-unused-parameters,
-cppcoreguidelines-non-private-member-variables-in-classes,
modernize-*,
-modernize-loop-convert,
-modernize-use-nodiscard,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
-cppcoreguidelines-avoid-c-arrays,
-hicpp-avoid-c-arrays,
mpi-*,
performance-*,
-performance-unnecessary-value-param,
readability-*,
readability-identifier-naming,
-readability-braces-around-statements,
-hicpp-braces-around-statements,
-google-readability-braces-around-statements,
-readability-identifier-length,
-readability-redundant-control-flow,
-readability-magic-numbers,
-cppcoreguidelines-avoid-magic-numbers,
-readability-const-return-type,
fuchsia-default-arguments-declarations
'



CheckOptions:
- {key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE}
- {key: readability-identifier-naming.NamespaceCase, value: lower_case}
- {key: readability-identifier-naming.ClassCase, value: CamelCase}
- {key: readability-identifier-naming.StructCase, value: CamelCase}
- {key: readability-identifier-naming.EnumCase, value: CamelCase}
- {key: readability-identifier-naming.TypedefCase, value: CamelCase}
- {key: readability-identifier-naming.TypeAliasCase, value: CamelCase}
- {key: readability-identifier-naming.TemplateParameterCase, value: CamelCase}
- {key: readability-identifier-naming.ValueTemplateParameterCase, value: CamelCase}
- {key: readability-identifier-naming.ParameterCase, value: aNy_CasE}
- {key: readability-identifier-naming.MethodCase, value: lower_case}
- {key: readability-identifier-naming.PublicMethodCase, value: aNy_CasE}
- {key: readability-identifier-naming.MemberCase, value: lower_case}
- {key: readability-identifier-naming.PublicMemberCase, value: aNy_CasE}
- {key: readability-identifier-naming.PrivateMemberPrefix, value: m_}
- {key: readability-identifier-naming.FunctionCase, value: aNy_CasE}
- {key: readability-identifier-naming.VariableCase, value: aNy_CasE}
- {key: readability-identifier-naming.ConstexprVariableCase, value: aNy_CasE}
- {key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor, value: true}
- {key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions, value: true}
- {key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted, value: true}
- {key: hicpp-special-member-functions.AllowSoleDefaultDtor, value: true}
- {key: hicpp-special-member-functions.AllowMissingMoveFunctions, value: true}
- {key: hicpp-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted, value: true}
- {key: readability-function-cognitive-complexity.IgnoreMacros, value: true}
- {key: readability-function-cognitive-complexity.Threshold, value: 40}
- {key: cppcoreguidelines-avoid-do-while.IgnoreMacros, value: true}
- {key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals, value: true}

.clang-format

clang-format 可以提供 C++的语法格式化。

clang-format 模板如下

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
---
# fenglielie@qq.com
# clang-format template
# 2023-09-14

# 参考 https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# 可以用注释取消代码片段的自动格式化
# // clang-format off
# ...
# // clang-format on

# 下文中被注释掉的设置可能是默认设置或者被舍弃的选项

# 语言
Language: Cpp
# 基于LLVM风格进行微调
BasedOnStyle: LLVM
# 访问说明符(public、private等)的偏移
AccessModifierOffset: -4
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
# 例如
# someLongFunction(argument1,
# argument2);
# AlignAfterOpenBracket: Align
# 连续赋值时,对齐所有等号
# AlignConsecutiveAssignments: Consecutive
# 连续声明时,对齐所有声明的变量名
# AlignConsecutiveDeclarations: Consecutive
# 对齐逃脱换行(使用反斜杠换行)的反斜杠
# AlignEscapedNewlines: Right
# 水平对齐二元和三元表达式的操作数
# AlignOperands: Align
# 对齐连续的尾随的注释
# AlignTrailingComments: true

# 允许函数声明的所有参数在放在下一行
# AllowAllParametersOfDeclarationOnNextLine: true
# 允许短的块放在同一行
AllowShortBlocksOnASingleLine: Always
# 允许短的case标签放在同一行
AllowShortCaseLabelsOnASingleLine: true
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
# AllowShortFunctionsOnASingleLine: All
# 允许短的if语句保持在同一行,除非没有else分支
AllowShortIfStatementsOnASingleLine: WithoutElse
# 允许lambda表达式在同一行
# AllowShortLambdasOnASingleLine: All
# 允许短的循环保持在同一行
AllowShortLoopsOnASingleLine: true

# 函数是否在定义返回类型后换行
# AlwaysBreakAfterReturnType: None
# 总是在多行string字面量前换行
# AlwaysBreakBeforeMultilineStrings: false
# 是否在template声明后换行
AlwaysBreakTemplateDeclarations: true
# false表示函数实参要么都在同一行,要么都各自一行
# BinPackArguments: true
# false表示所有形参要么都在同一行,要么都各自一行
# BinPackParameters: true


# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
BreakBeforeBinaryOperators: NonAssignment
# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
# Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
# Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom(自定义)
# 注:这里认为语句块也属于函数
BreakBeforeBraces: Custom
# 大括号换行自定义规则,只有当BreakBeforeBraces设置为Custom时才有效
BraceWrapping:
# class定义后面
AfterClass: false
# 控制语句后面
AfterControlStatement: Never
# enum定义后面
AfterEnum: false
# 函数定义后面
AfterFunction: false
# 命名空间定义后面
AfterNamespace: false
# struct定义后面
AfterStruct: false
# union定义后面
AfterUnion: false
AfterExternBlock: false
# catch之前
BeforeCatch: true
# else之前
BeforeElse: true
# 大括号
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: true


# 在构造函数的初始化列表的逗号前换行
# BreakConstructorInitializersBeforeComma: false
# BreakConstructorInitializers: BeforeColon
# 每行字符的限制,0表示没有限制
# ColumnLimit: 80
# 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变
# CommentPragmas:'^ IWYU pragma:'
# 嵌套的命名空间的namespace语句是否合并到一行
# CompactNamespaces: false
# 构造函数的初始化列表的缩进宽度
# ConstructorInitializerIndentWidth: 4
# 延续的行的缩进宽度
# ContinuationIndentWidth: 4
# 去除C++11的列表初始化的大括号{后和}前的空格
# Cpp11BracedListStyle: true
# 基于当前文件中最常用的指针和引用的对齐方式进行处理
# DerivePointerAlignment: false
# 对于public/private之类的非空部分,自动加入空行进行分隔
# EmptyLineBeforeAccessModifier: LogicalBlock
# 在命名空间的}后面自动添加注释 // namespace XXX
# FixNamespaceComments: true
# 缩进宽度
IndentWidth: 4
# 函数返回类型之后立刻换行时,是否缩进函数声明或函数定义的函数名
# IndentWrappedFunctionNames: false
# 保留在块开始处的空行,这里不允许块开头有空行
KeepEmptyLinesAtTheStartOfBlocks: false
# 开始一个块的宏的正则表达式
# MacroBlockBegin: ''
# 结束一个块的宏的正则表达式
# MacroBlockEnd: ''
# 连续空行的最大数量
# MaxEmptyLinesToKeep: 1
# 命名空间的缩进: None, Inner(只缩进嵌套的命名空间中的内容), All
# NamespaceIndentation: None
# 视作命名空间的宏列表,类似的还有视作if的,视作foreach的宏
# 例如
# NamespaceIndentation:
# - MACRO1
# - MACRO2
# NamespaceMacros
# 在call(后对函数调用换行的penalty
# PenaltyBreakBeforeFirstCallParameter: 19
# 在一个注释中引入换行的penalty
# PenaltyBreakComment: 300
# 第一次在<<前换行的penalty
# PenaltyBreakFirstLessLess: 120
# 在一个字符串字面量中引入换行的penalty
# PenaltyBreakString: 1000
# 对于每个在行字符数限制之外的字符的penalty
# PenaltyExcessCharacter: 1000000
# 将函数的返回类型放到它自己的行的penalty
# PenaltyReturnTypeOnItsOwnLine: 60

# 指针和引用的对齐: Left, Right, Middle
# PointerAlignment: Right
# 引用的对齐(参照指针)
# ReferenceAlignment: Pointer
# 允许重新排版注释,保证行的长度
# ReflowComments: true
# 在函数之间,类和结构体之间自动使用空行分隔
SeparateDefinitionBlocks: Always
# 大小写敏感地排序include
# SortIncludes: CaseSensitive

# 在C风格类型转换后添加空格
# SpaceAfterCStyleCast: false
# 在template关键词之后紧接着加空格
# template <int> void foo();
# SpaceAfterTemplateKeyword: true
# 在赋值运算符例如+=和=之前添加空格
# a += 5;
# SpaceBeforeAssignmentOperators:true
# 开圆括号之前添加一个空格: Never, ControlStatements, Always
# SpaceBeforeParens: ControlStatements
# 在空的Block中添加空格
# SpaceInEmptyBlock: false
# 在空的圆括号中添加空格
# SpaceInEmptyParentheses: false
# 在尾随的注释前添加的空格数(只适用于//)
SpacesBeforeTrailingComments: 2
# 在尖括号的<后和>前添加空格
# SpacesInAngles: Never
# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
# SpacesInContainerLiterals: true
# 在C风格类型转换的括号中添加空格
# SpacesInCStyleCastParentheses: false
# 在圆括号的(后和)前添加空格
# SpacesInParentheses: false
# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
# SpacesInSquareBrackets: false

# tab宽度
TabWidth: 4

# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
UseTab: Never
---

.clangd

clangd有时候比较蠢,我们可以通过在项目根目录添加.clang文件来提示它,例如它很可能在windows上找不到正确的标准库头文件,而是会跳转到MSVC的标准库头文件,我们可以添加--include-directory=来指定。

例如

.clangd
1
2
3
4
5
6
7
8
CompileFlags:
Add:
- '-Wall'
- '-Wextra'
- '-Wshadow'
- '-Wno-unused-parameter'
- '-std=c++17'
- '--include-directory=/path/to/compiler/include'

有时候需要使用C语言项目,但是默认的配置是针对C++的,对于C语言目前使用下面的配置

.clangd
1
2
3
4
5
6
7
CompileFlags:
Remove: [-std=*]
Add: [-xc, -Wall]
Compiler: clang
Diagnostics:
ClangTidy:
Remove: readability-identifier-naming

除此之外,clangd主要会基于compile_commands.json文件进行代码提示,CMake项目可以用特定选项在build/中自动生成compile_commands.json

.gitconfig

Git 的用户级配置参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[user]
name = xxxxxxxxxx
email = xxxxxxxxxx@qq.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
[alias]
trace = log --oneline --all --graph
now = status -s -b

.gitignore

语法规则参考官方文档

基本语法

  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
# 忽略所有以 ~ 结尾的文件(通常是临时文件)
*~

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

# 忽略日志文件
*.log

# 忽略备份文件
*.bak

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

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

在 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
3
4
5
6
# 忽略一切
*

# 把想要的文件或文件夹重新包含进来
!src/**
!README.md

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

1
2
/.*/
!/.*

规则优先级

Git 检查忽略规则的时候有多个来源,它的优先级如下(由高到低):

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

模板

Github 官方提供了很多模板,参考gitignore 模板

下面给自己准备几个模板吧,涉及的主要是 C++ 和 LaTeX。

C/C++ 项目模板

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# [[C++]]
# Compiled Object files
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.a
*.lib

# Executables
*.exe
*.out

# Build directory and results
build/
[Dd]ebug/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Bb]in/
[Ll]ib/
[Ll]og/

# [[Other]]

# temp/log/backup
*~
*.tmp
*.log
*.bak
*.backup

# document
*.pdf

LaTeX 项目模板(这里选择白名单模式,因为LaTeX项目可能产生很多杂项文件)

1
2
3
4
5
6
7
8
9
10
*

!.gitignore
!.gitattributes
!.editorconfig

!*.tex*
!*.bib*
![Ii]mage*/
![Ff]igure*/