版本管理与合作¶
Pending Review
Git 使用技巧¶
本地配置¶
配置文件
Git 的配置文件一般存放于 ~/.gitconfig
或 ~/.config/git/config
中, 可以使用 git config --global --edit
快速打开配置文件, 你可以直接拷贝下面的内容。
下文中会讲解部分配置的作用及注意事项。
缩进问题
下面的文档中,.gitconfig
的配置文件均使用 4 个空格而不是 Tab 进行缩进。
常用别名¶
[alias]
aliases = !git config --get-regexp alias | sed -re 's/alias\\.(\\S*)\\s(.*)$/\\1 = \\2/g'
ci = commit
co = checkout
st = status
lg = log --graph --date=relative --pretty=tformat:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ad)%Creset'
oops = commit --amend --no-edit
reword = commit --amend
push-with-lease = push --force-with-lease
uncommit = reset --soft HEAD~1
常用配置¶
[color]
ui = auto
[color "branch"]
upstream = green
remote = red
[core]
editor = nvim
excludesfile = ~/.gitignore_global
[commit]
template = ~/.gitmessage
[pull]
ff = only
[push]
default = upstream
followTags = true
[tag]
sort = version:refname
我们也将上述的 .gitconfig
正确使用 Tab 缩进的版本放在 这里. 使用如下命令可以快速将我们提供的模板放入你的配置文件中:
gitignore¶
GitHub 在 这里 提供了一些常见的 .gitignore
文件,对于较为复杂的项目,也可以使用gitignore.io 生成。
仅本地的 gitignore
本地的 .git/info/exclude
起到与 .gitignore
相同的作用,但是不会被提交到版本库中,适用于以下的情况:
- 项目不允许修改
.gitignore
- 目录名称本身包含敏感信息
- 不希望
.gitignore
参与到版本控制中
详细的文档可以参考这里。
Global gitignore¶
对于一些常见的文件类型,可以在全局配置文件中指定:
Git Hook¶
对于一些重复性的工作(例如格式化代码、检查代码风格等),可以使用 Git Hook 来自动化。
一个叫较为成熟的框架是 pre-commit,它支持多种语言和工具,例如 black
、flake8
、eslint
等,这里 提供了一些常用的 hook.
如果只需要在 commit 后运行一段脚本
可以按照如下方法进行配置:
Git Submodule¶
Submodule 可以用来添加外部项目,例如向一个 C++ 项目中添加 Eigen:
如果已经 clone 到了子目录 src/eigen
下,可以通过如下方法添加:
git rm --cached -f src/eigen # if you've already added it to the index
git submodule add <url_of_eigen> src/eigen
Rebase 与 Merge¶
一般来说,我们希望保持项目有线性的提交历史,这样可以更容易地追溯问题,因此推荐使用 rebase
来合并分支。
git checkout -b feature
git commit -a --allow-empty -m "feat: aaaaaa"
git checkout master
git commit -a --allow-empty -m "feat: bbbbbb"
git checkout feature
git rebase master
通过如下设置,可以使得 rebase
成为默认行为:
Bisect¶
在调试问题时,有时会出现这样的情况:某个 bug 在旧版本没有出现,但是在新版本出现了,或者某个问题在旧版本存在,新版本不存在,同时需要搞清楚具体是哪一个 commit 导致/修复了对应的问题。一个一个 commit 编译测试显然工作量实在太大,此时 git bisect
就可以起到很大的帮助。在使用 bisect 时,需要提供一个 "good"(旧版本)commit 和一个 "bad"(新版本)commit:
之后 git bisect
就会帮助你做二分搜索,跳转到两者中间的 commit 以供测试,之后提供 git bisect good
或者 git bisect bad
引导 git 搜索,最终找到对应的 commit。
good or bad?
有时候我们会需要确认哪个 commit 修复(而不是导致)了问题,但是 git bisect
默认 good 需要早于 bad,直接 git bisect start
的话很容易误操作。请阅读 git-bisect(1) 了解应该如何处理此类情况。
Commit Message Convention¶
对于多人协作的项目,良好的 commit message 是非常重要的。胡乱使用诸如 update
、fix
、change
等无意义的 Commit Message,会使得项目的历史记录变得难以理解,也会给后续的维护带来困难。
Conventional Commits
一种常见的 Commit Message 格式是 Conventional Commits,它的格式是:
其中:
type
是 commit 的类型,可以是feat
、fix
、docs
、style
、refactor
、perf
、test
、build
、ci
、chore
等。scope
是 commit 的作用域description
是 commit 的简要描述body
是 commit 的详细描述,通常会引用 issue、解释修改的原因等footer
通常用于引用 issue、关闭 issue 等,例如Closes #123
,也可以用于指定 breaking change 等
值得注意的是,以上规范仅仅只是推荐,实际使用时可以根据项目的实际情况进行调整,例如本文档所存放的仓库是一个文档类的项目,一般情况下可以直接省略掉type
, 可用文档相对目录来替代,例如修改本文的 Commit Message 一般就写成 dev/git: fix typo
.
Commit Message 模板
即便不严格遵循上述规范,设置一个 commit message 模板也是非常有用的,例如,可以将 这里的例子 添加到 ~/.gitmessage
:
添加后不要忘记在首行添加空行,这样 git commit
时无需新建一行。
GitHub 使用技巧¶
GitHub CLI¶
GitHub CLI 是 GitHub 官方提供的命令行工具,可以用于管理 GitHub 仓库、Issue、Pull Request 等。
例如,给 ustclug/Linux201-docs
修 bug 的流程可以简化为:
其他常用的 GitHub CLI 命令包括:
gh repo view --web
:在浏览器中打开当前仓库gh issue list
:列出当前仓库的 Issuegh run watch
:查看当前仓库的 GitHub Actions 运行状态
gh run watch
默认情况下 gh run watch
需要手动选择关注的 GitHub workflow, 如果只想关注最新的 workflow 可以将如下函数添加到 ~/.bashrc
或 ~/.zshrc
:
watch_latest_run() {
# Fetch the latest run ID using gh and jq
local latest_run_id=$(gh run list --limit 1 --json databaseId --jq '.[0].databaseId')
if [ -z "$latest_run_id" ]; then
echo "No runs found."
return 1
fi
# Pass the latest run ID to gh run watch
gh run watch "$latest_run_id"
}
之后可以直接使用 watch_latest_run
命令即可。
GPG 签名¶
SSH Key 只用来验证 push 环节的身份,而 GPG Key 则用来验证 Commit 的真实性。
GitHub 对 GPG Key 的文档描述很详细,我们将其列在这里:
请注意备份 GPG Key, 并额外向其他 Key Server 发布 GPG Key, 以防止 GPG Key 丢失:
gpg --list-secret-keys --keyid-format LONG
gpg --armor --export <GPG Key ID> | tee gpg.key
gpg --keyserver keyserver.ubuntu.com --send-keys <GPG Key ID>
gpg --keyserver pgp.mit.edu --send-keys <GPG Key ID>
过期的 GPG Key
过期的 GPG Key 是可以更新的, 参考 这个 StackOverflow 回答. 在 GitHub 上 rotate 只需要删除旧的 GPG Key, 然后重新添加新的 GPG Key 即可. 值得注意的是过期的 GPG Key 签名的 commit 依然会显示成 Verified, 因此不要轻易删除过期的 GPG Key.
Issue¶
下面关于 Markdown 的特性并不限于 Issue,也适用于 Pull Request 等。
GitHub 链接¶
我们以 ustclug/mirrorrequest#213 为例:
- 在 Issue 中,可以使用
#
来引用其他 Issue / PR,例如#133
-
可以通过
user/repo#issue_number
的方式引用其他仓库的 Issue / PR,例如tuna/issues#341
-
当一个 PR 包含如下关键字,并且按照上述方法连接到一个 Issue 时,合并这个 PR 会关闭对应的 issue:
-
-
可以通过 GitHub Web 上 Copy permalink 的方式获取代码的链接,例如打开 ustclug/mirrorrequest/README.md 后,可以选择某一行,点击左侧的菜单,选择 Copy permalink, 即可获得诸如 https://github.com/ustclug/mirrorrequest/blob/f23dd1f1cbe81f01e4f878ac11ee064b6c7d70ec/README.md?plain=1#L1 这样的链接。
- 这样的链接可以在 Issue 中直接粘贴,会被以代码框的形式渲染到 Issue 中,方便其他人迅速了解问题。
- 点击后也可以直接复制地址栏中的 URL,这样的链接总是指向某个 branch 或者 tag 的,而不是特定 commit,但这样的作法可能会导致链接失效。
GitHub Flavor Markdown¶
正如其他编辑器对 Markdown 的支持一样,GitHub 支持一个 Markdown 方言(超集)的写法,称为 GitHub Flavor Markdown。
我们在这里介绍一些你可能感兴趣的 feature:
-
Mermaid 是一种简单且强大的关系图/流程图语法,例如可以通过如下方式创建一个简单的关系图:
graph LR; A-->B; A-->C; B-->D; C-->D;
-
GitHub 通过 MathJax 支持 LaTeX 公式,可以通过
$ \frac 12 $
的形式创建行内公式,$$ \frac 12 $$
的形式创建块级公式。 -
在 Issue 正文中创建一个任务列表,例如:
此时可以将这个 Issue 转化为一个任务列表,方便追踪任务的进度,同时
#123
会被标记为Tracked by #xxx
。
Issue 模板¶
对于大型项目,使用 Issue template 可以使得 Issue 更加规范化,例如,可以在 .github/ISSUE_TEMPLATE/bug_report.yml
中添加如下内容:
name: Bug Report
about: Create a report to help us improve
labels:
- bug
body:
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
placeholder: I'm always frustrated when...
validations:
required: true
可以参考 ustclug/mirrorrequest/.../01-mirror-request.yml, GitHub 文档.
Pull Request¶
对于维护者(Maintainer)来说,Pull Request 更像是 Merge Request, 他们会检查代码、测试代码、review 代码,然后将代码合并到主分支中。
GitHub 在 这里 介绍了合并 PR 的方式,简单来说跟本地 merge / rebase 没有太大区别。
- PR 中仅有一个 commit 时,推荐使用 Rebase 合并 PR
- 当 PR 中包含多次 commit,但实际上应当合并为一个时(例如经过 Review 后),推荐使用 Squash 合并 PR
- 多次 commit 来提交新 feature 时,推荐使用 Merge 合并 PR
维护者有时会需要将 PR checkout 到本地以测试。可以使用 GitHub CLI 的 gh pr checkout
命令快速完成,也可以采用手工方式:使用 git fetch origin pull/PR_NUMBER/head:BRANCH_NAME
的形式将编号为 PR_NUMBER
的 PR 对应的 head 同步到本地的 BRANCH_NAME
分支,之后 git checkout
即可。维护者可以在这个新分支中同步贡献者的新修改,如果 PR 设置为 "Allow edits from maintainers",那么维护者也可以直接写入贡献者的 PR。
GitHub Actions¶
GitHub Actions 是 GitHub 提供的 CI/CD 服务,可以用于自动化构建、测试、部署等。
GitHub Actions Pricing
对于 Public 仓库,GitHub 提供了免费的服务,对于 Private 仓库,GitHub 提供了 2000 分钟的免费服务。
关于计费问题,可以参考这里。
对于 GitHub Actions 的写法,只需要关心如下几件事情:
- 何时应当触发 Action:例如
on: push, pull_request
- 如何在 GitHub Actions 上搭建环境(例如安装依赖、配置环境变量等)
- 产物的存放位置:例如
artifacts
、release
如果涉及到 Secret Key,还应当注意安全问题(限制触发条件、产物等)。
GitHub 在 这里 提供了详细的文档。
Other CI/CD systems¶
以下是一些其他常用的 CI/CD 提供商,它们提供了类似的服务:
考虑到 GitHub Actions 的免费额度,以及与 GitHub 的无缝集成等,已经足够满足大多数项目的需求,因此我们在这里不再详细介绍其他 CI/CD 系统。