git 提交规范
一个commit包含如下几个信息:
- commit message - 提交的内容相关描述
- author & committer - 作者及提交者
- changed files - 修改的文件
- hash & parent - 提交内容的hash及在提交树上的位置
Commit Message
提交消息描述的是当前提交的功能相关信息,一般可以包括header,body,footer,
<header>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
其中header是必须的。Angular官方建议的格式如下
<type>(<scope>): <short summary>
│ │ │
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
│ │
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core...
│
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test
表明本次提交的类型,一般有如下几种:
- build: 涉及构建相关的改动
- ci: 持续集成相关的改动
- docs: 文档
- feat: 新功能
- fix: bug修复
- perf: 性能相关改动
- refactor: 重构相关(非bug、非新功能)
- test: 测试相关,包括新增测试或者更改已有测试
表示改动影响的范围。在Angular中,某个提交可能涉及的范围有表单处理、动画处理等。 在实际工作中可以视项目而定。
最后,
规范的提交信息可以使用工具对内容进行解析,自动化生成文档或者发布日志。在一些大型的开源项目中, 版本的更新文档,接口的更新及兼容性影响,纯粹靠人工整理是很费时费力的,用统一的规范能够极大自动化 这部分工作。当然不同的项目对提交信息的要求和格式标准也不一样,开源项目或者公司项目对提交信息的 要求也有差异,一般需要遵从所在项目的约定。较为成熟的开源项目一般可以在README文档中找到如何贡献, 或者有单独的CONTRIBUTING.md文档,对代码风格、提交方式等进行约定。
自动化校验commit message
有了提交信息的规范,如何确保开发者对规范进行遵守呢?我们可以使用Git提供的Git Hooks功能对提交 的信息进行校验。本文不对Git Hooks的细节做过多介绍,仅做基础的说明,具体细节可以参考 官方文档 或Atlassian的文档。
在新初始化的git项目内,我们可以在.git/hooks文件夹中找到官方提供的样例:
ls -l .git/hooks
total 120
-rwxr-xr-x 1 tomo staff 478B Nov 11 20:44 applypatch-msg.sample
-rwxr-xr-x 1 tomo staff 896B Nov 11 20:44 commit-msg.sample
-rwxr-xr-x 1 tomo staff 4.5K Nov 11 20:44 fsmonitor-watchman.sample
-rwxr-xr-x 1 tomo staff 189B Nov 11 20:44 post-update.sample
-rwxr-xr-x 1 tomo staff 424B Nov 11 20:44 pre-applypatch.sample
-rwxr-xr-x 1 tomo staff 1.6K Nov 11 20:44 pre-commit.sample
-rwxr-xr-x 1 tomo staff 416B Nov 11 20:44 pre-merge-commit.sample
-rwxr-xr-x 1 tomo staff 1.3K Nov 11 20:44 pre-push.sample
-rwxr-xr-x 1 tomo staff 4.8K Nov 11 20:44 pre-rebase.sample
-rwxr-xr-x 1 tomo staff 544B Nov 11 20:44 pre-receive.sample
-rwxr-xr-x 1 tomo staff 1.5K Nov 11 20:44 prepare-commit-msg.sample
-rwxr-xr-x 1 tomo staff 2.7K Nov 11 20:44 push-to-checkout.sample
-rwxr-xr-x 1 tomo staff 3.6K Nov 11 20:44 update.sample
涉及提交相关的是下面四个:
- pre-commit - 在Git生成commit对象前执行
- prepare-commit-msg - 在pre-commit后执行,用以生成默认的提交信息,脚本接收三个参数:
包含提交信息的临时文件名
提交的类型,如message, template, merge, squash
相关提交的SHA1,仅在有-c, -C或–amend参数时提供该参数 - commit-msg - 在开发者编写提交信息后执行,仅有临时文件名一个参数
- post-commit - 在commit-msg后立马执行,更多做通知用
我们可以用prepare-commit-msg对提交信息规范做说明,并用commit-msg对规范的执行 进行检查,脚本的非0的返回会中断本次提交。
如我们想应用简单的类似Angular的
下面是prepare-commit-msg的示例:
#!/usr/bin/env python
import sys, os, re
from subprocess import check_output
# Collect the parameters
commit_msg_filepath = sys.argv[1]
if len(sys.argv) > 2:
commit_type = sys.argv[2]
else:
commit_type = ''
if len(sys.argv) > 3:
commit_hash = sys.argv[3]
else:
commit_hash = ''
print("prepare-commit-msg: File: %s\nType: %s\nHash: %s" % (commit_msg_filepath, commit_type, commit_hash))
msg_spec = '''# Please use follow format
# <type>(<scope>): <short summary>
# │ │ │
# │ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
# │ │
# │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core
# │
# └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test'''
with open(commit_msg_filepath, 'r+') as f:
f.write("\n" + msg_spec)
sys.exit(0) # return non-zero will abort current commit
下面是简单的commit-msg示例:
#!/usr/bin/env python
import sys, os, re
# Collect the parameters
commit_msg_filepath = sys.argv[1]
print("commit-msg: File: %s" % commit_msg_filepath)
header_pattern = re.compile(r'^(?P<type>\w+)(\((?P<scope>\w+)\))?: .+$')
commit_types = 'build|ci|docs|feat|fix|perf|refactor|test'.split('|')
commit_scopes = 'animations|bazel|benchpress|common|compiler|compiler-cli|core'.split('|')
with open(commit_msg_filepath, 'r') as f:
commit_msg_header = f.readline().rstrip('\n') # header line
print('<header>: %s' % commit_msg_header)
match = header_pattern.match(commit_msg_header)
if not match:
print('commit message does not meet spec')
sys.exit(1)
commit_type = match.group('type')
commit_scope = match.group('scope')
if commit_type not in commit_types:
print('invalid <type>')
sys.exit(1)
if commit_scope and commit_scope not in commit_scopes: # scope is optional
print('invalid <scope>')
sys.exit(1)
sys.exit(0)
想使用相关的Git Hooks,可以在目录.git/hooks创建对应的文件,文件名为prepare-commit-msg 及commit-msg,并赋予可执行权限。这样在我们进行git commit操作时,对应的脚本就会执行。 下图是相关执行示意图,其中不合规范的提交会被中断。
具体执行过程可以参考在线执行过程
Git的提交不会包含.git目录,所以对应的hooks的改动并不会被提交到仓库中。我们可以在仓库根目录 创建.githooks文件夹并将我们实现的代码放到该目录中,通过更改配置或者软连接的方式进行引用:
# use config
git config core.hooksPath .githooks
# OR use soft link
ln -sf .githooks/* .git/hooks
当然这些都是客户端的校验,开发者可以完全忽视这样的一些Git Hooks的配置并引入不合规范的提交, 这种情况下我们可以使用服务端校验进行处理,或者引入一些CI工具或使用GitHub Action进行校验。
Author & Committer
Git中,Author表示原始纂写该提交的作者,Committer表示应用该提交的人,如合并Pull Request 的项目管理员。如果是个人开发者或只使用单个Git平台服务(如GitHub、BitBucket等),我们一般 不需要对作者进行特别的配置。但如果使用多个Git平台或者有公司内部要求,我们可能需要针对不同的仓库 设置不同的用户及邮箱,比如全局可以设置个人的GitHub账号,企业内部仓库设置企业邮箱等。
# 全局默认配置
git config --global user.email "<github email>"
git config --global user.name "<github username>"
# 企业内部仓库
git config user.email "<enterprise email>"
git config user.name "<real name>"
Changed files
我们所有的提交,核心的其实我们提交的文件。不同的提交涉及的文件可多可少,一般遵循以下一些原则:
提交前使用git diff查看文件的改动,使用git add添加期望进入提交的文件, 使用git status查看文件状态,最终使用git commit进行提交
单次提交仅提交相关的改动,例如修复两个不同的bug应该使用两次独立的提交
鼓励经常性的提交,这样可以更快的分享实现的功能,并且减少代码丢失的风险
在主分支或者协作的功能分支不能提交半成品,提交之前需要进过测试
编译输出,日志,中间产物等,不要引入到提交中,使用.gitignore进行相关文件的排除,不同语言 或者操作系统有一些通用的排除配置,参考github/gitignore
密码、授权凭证、密钥等,不要提交。如AWS的certificate.csv文件或内容, GCP的Service Account文件等,泄露到公开仓库会导致资源被不法分子使用,造成损失。同时由于Git的特性, 想从历史提交中移除这类文件会较为困难,参考GitHub官方相关文档及描述
对于配置文件(如数据库连接信息等),一般使用配置模板,个人维护本地文件,且该文件在.gitignore 中配置。或者使用git update-index --[no-]assume-unchanged 来忽略某些文件的改动
其他一些常用命令(请在明确知道其含义后使用)
- git reset - 移除被添加的文件(提交之前),reset命令的其他可以查看帮助文档
- git clean -f - 移除较多的未被追踪的中间文件
- git checkout - 回退对某个文件的改动(提交之前)
Hash & Parent
一般情况,commit hash及父节点信息我们不需要额外关注,但在特定场景下我们可能需要对commit 进行修复或者其他处理。在这样的场景下,我们需要理解整个git的提交链,每个提交对应的父节点,分支 间的共同祖先,以及本地与远端的差异,尤其涉及rebase相关的操作时。同时我们需要在整个提交中 遵循项目使用的工作流模型,使用对应工作流模型中建议的操作(常见的工作流模型参考 Atlassian文档)。
下面是一些实际开发过程中涉及的场景:
在自身的开发分支,某个功能涉及多个提交,在正式合并至主分支前对相关的提交进行整理,可以使用 git rebase -i 命令,对提交进行合并、废弃、修改提交信息等处理。需要注意的是如果提交 已经发布到远端,需要使用git push -f进行覆盖(仅限个人开发分支)。下面是一个简单的例子及相关 命令描述,常见的命令有pick, reword, fixup, drop等。
$ git rebase -i 8717c71fc
reword 27e67629b feat: some feature first commit
fixup 7a3f0cd25 feat: some feature second commit
fixup d9a9d7f04 feat: some feature third commit
# Rebase 8717c71fc..d9a9d7f04 onto 8717c71fc (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
# commit's log message, unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
- 在一些Git工作流模型中,使用git pull --rebase对本地提交进行更新
- 原则上禁止对主分支等进行git push -f操作,涉及需要回退的,使用git revert
- 涉及多分枝代码同步,可以使用git cherry-pick命令
Github / Gitee 使用说明
Github是一个使用Git作为版本管理工具的代码托管平台,由于其用户量大并且氛围友好,又经常被大家戏称为“全球最大同性交流平台”。事实上,依赖于Git的强大协作能力,Github是开源软件发展的主战场之一。如果没有注册过Github,可以先登陆http://github.com注册账号
Github上几乎所有的事情都是围绕着仓库展开的。我们首先来学习如何新建一个仓库并满足自己的开发需求。
一个GitHub仓库实质上是一个包含了你的项目所有文件的文件夹。 .git文件夹也包含在其中用于版本控制。
登陆GitHub主页,点击右上角+号即可创建一个仓库,如下图:
点击后会跳转到新建仓库的表单,
注意:
- github上的仓库一般都会包含readme文件,该readme文件会在项目页面进行展示
- .gitignore文件可以用来忽略工作区的私有文件(例如本地配置、缓存文件、node_modules等)
点击绿色的code按钮,选择相应的协议即可拿到该项目的地址,在本地只需要clone下来就可以进行开发了,开发完成后push到原仓库即可
仓库界面介绍
我们以vscode项目进行介绍
访问https://github.com/microsoft/vscode 即可看到如下的界面
整个页面的信息量很大,我们主要介绍几个:
页面的左上角:
Star:Star类似于朋友圈的点赞,给项目star代表了你对项目的认可
所以尽量不要像朋友圈集赞一样要求身边朋友刷star
Fork:Fork操作实际上是创建一个仓库的副本,并将仓库的upstream指向原仓库
小问题:为什么要fork呢?为什么不直接push呢?
fork方便了多人协作
Watch:Watch操作可以向你的邮箱中推送该仓库的推送信息
提示:如果不是特别重要的项目,不要随意点Watch,否则很容易邮箱被轰炸
Issues:Issues在Github官方文档中被翻译为议题,作用是针对仓库的内容进行讨论(例如bug反馈/新功能推荐)
提示:Issues不等同于评论区,Issues板块应该专注于解决问题,不要在Issues中发一些和项目无关的内容,这样可能会消耗很多maintainer的精力
Pull Requests:Pull Requests,简称PR,是github中将修改过的代码分支合并到目标分支的操作。前面git的学习中,我们都知道commit是git的最小工作单元,在github的仓库中,PR是主要的工作单元。很多同学刚刚接触GitHub时,对于Pull Requests很不理解:什么是拉取请求?在gitlab中,pr的操作叫做Merge Request, 实际上大家可以把PR理解为“我修改好了你的代码,现在请求你把代码拉回主仓库中”
Action:Github Action 是GitHub推出的自动化构建工具,感兴趣的同学可以阅读文档
Projects:针对某一仓库的项目板(看板)
Wiki: 存放一些介绍性的内容
Security:与安全相关,这里不做介绍
Insight:里面包含里项目的一些数据,包括代码贡献的时间分布图,每个人的贡献量等metric
discussion:vscode仓库中并没有开启discussion功能
提交issue
我们以vscode为例展示提交issue
进入Issues选项卡,可以看到vscode项目已经准备了一些模版,我们点击Bug report
提示:在提bug时,请尽可能详细的描述出bug发生的步骤以及所运行的环境(https://stackoverflow.com/help/minimal-reproducible-example )。一般来说,Issue是参与项目贡献的起点,一个高质量的Issue也会让maintainer更愿意交流、处理。
如果你是项目的maintainer,也可以通过右侧对该issue进行更详细的设置,这里不详细介绍了。
提交PR
如果你fork了一份项目代码并做了修改,并且希望将修改的代码合并进上游仓库中,就可以提交PR
上图为vscode的pr界面,点击New pull request即可新建pr。具体的PR流程这里也不详细讲解了。
注意:不是所有的PR都会被合并,所以在提交PR前请先和maintainer进行沟通,并且在开发的过程中反馈进度,一种比较好的方式就是draft PR,如下图所示:
draft PR表示该PR还没有开发完,项目的maintainer不需要进行reveiw和merge,只需要简单看看代码是否符合预期。
小提示:在提交PR时,尽可能关联相关Issue,并说明你的代码解决了什么问题。
更新fork的项目-Fetch upstream
情景:用户2729956566的对项目进行了fork,提交的PR通过了,或者项目进行了其它更新。此时,我们fork的项目已不是最新版,因此需要更新我们fork的项目到最新版。
大概步骤 视频参考链接
# 查看远程仓库有几个分支
git remote -v
# 将仓库的原始地址加进去
git remote add upstream git@github.com:2951121599/repo_for_test_pr.git
# 再次查看远程仓库的分支 (会多上有仓库upstream)
git remote -v
# fetch将远程分支拉到本地 pull = fetch + merge (pull会做自动合并)
# 创建新分支 master/upstream
git fetch upstream
# 查看远程分支 若跟本地分支名一样 然后做一下合并
git branch -r
# 和原始仓库的远程保持一致 rebase不会做合并操作,将当前分支的修改复制并放在目标分支的最后一次 而merge会将两个分支合在一起
# 因此没做贡献用rebase就够了
git rebase upstream/master
# 推送
git push
探索Github
对于大多数程序员来说,Github的一个重要用途就是学习别人的代码,看自己的任务有没有已经写好的轮子可以用。因此如何高效的探索Github也是很重要的,这里给大家介绍几种探索GitHub的小技巧
Explore
点击GitHub最上方的Explore或输入https://github.com/explore 即可进入。Explore板块不仅可以根据你的兴趣进行项目的推荐,而且Trending榜展示了当前综合热度最高的项目。关注Trending可以随时掌握整个Github的最新动向
补充 https://kamranahmed.info/githunt/ 也是一个追踪热门项目的网站
快捷键
GitHub网站拥有一系列快捷键,你可以通过快捷键来完成你想要完成的动作,例如ctrl/command+k会调起一个类似于powertoy一样的搜索框,在这里你可以直接进行搜索。
类似的快捷键有很多,完整的快捷键见文档https://docs.github.com/cn/get-started/using-github/keyboard-shortcuts
高级搜索
高效的搜索方式可以节约你很多时间,例如下面代码可以帮助你找到Github中star量超过10000的项目
stars:>10000
其他搜索技巧可以参考上图红框中的链接
常见搜索的快速备忘单
基本搜索
搜索 | 使用…查找存储库 |
---|---|
cat stars:>100 | 查找星数超过 100 的 cat 存储库。 |
user:2951121599 | 从用户 2951121599 获取所有存储库。 |
qucik_sort extension:py | 在带有py扩展名的代码中查找所有qucik_sort的实例。 |
NOT cat | 排除所有包含 cat 的结果。 |
存储库搜索
查看你在 GitHub 上可以访问的项目,你也可以过滤结果
搜索 | 使用…查找存储库 |
---|---|
python forks:>200 | 查找所有大于 200 个forks的 Python 存储库。 |
awesome-python fork:true | 包括 awesome-python 的分支。 |
awesome-python fork:only | 仅返回 awesome-python 的分支。 |
代码搜索
代码搜索会查看托管在 GitHub 上的文件。您还可以过滤结果:
搜索 | 使用…查找存储库 |
---|---|
chunk repo:2951121599/Utils | 从存储库 2951121599/Utils 中查找所有 chunk 的code实例。 |
pandas user:2951121599 | 从所有公共 2951121599 存储库中查找对 pandas 的引用。 |
examples path:/docs/ | 在路径 /docs/ 中查找所有examples 。 |
问题搜索
问题搜索查看 GitHub 上的 issues and pull requests(问题和拉取请求)。您还可以过滤结果:
搜索 | 发现问题… |
---|---|
cat is:open | 查找未解决的猫问题。 |
strange comments:>42 | 超过 42 条评论的问题。 |
hard label:bug | 标记为错误的难题。 |
author:2951121599 | 所有问题均由 2951121599 撰写。 |
mentions:tpope | 所有提到 tpope 的问题。 |
assignee:2951121599 | 分配给 2951121599 的所有问题。 |
CodeSpace
在你的仓库界面,输入英文状态下的 .
,即可进入该项目的web editor,这实质上是一个云端的vscode,方便用户查找编辑代码。很可惜现在CodeSpace还不能支持在线运行代码,一些简单的修改可以配合Action使用
Copilot
Copilot是Github通过公开代码训练的一个强大的代码补全工具,现在还在内测阶段,有感兴趣的同学可以在https://copilot.github.com/ 申请,这里不过多介绍了
Readme-profile
用户主页也是探索Github很好的地方.
左边展示了你参与过的项目,右边展示了Github Explore推荐的项目,中间展示了你Follow的用户最近的动态通常来说,你Follow的用户越多,主页动态越精彩。
补充资料:思否今年做过一个中国开源爱好者榜单,有兴趣的同学可以看下https://github.com/OpenSourceWin/hacking-force
Github API
Github对针对开发者提供了一系列API,详情见https://docs.github.com/en/developers 。通过API可以对数据采集分析,探索更微观的GitHub。也有开源项目专门做这件事情,例如open-digger开源项目(https://github.com/X-lab2017/open-digger ),感兴趣的同学去自己探索下,这里也不多讲了。
国内其他代码托管平台简介
Gitee/Coding/jihulab
作为代码托管平台,Github由于网速等原因的限制,访问起来会很慢,可以使用开源项目dotnetcore/FastGithub: github加速神器,只需下载安装运行,即可提高网站访问速度。
这时可以采用国内的代码托管平台,这里我们只介绍下Gitee
网址:https://gitee.com/
Gitee整体的功能与github相差不大,这里就不多介绍了,等待大家的探索。
下面我们讲一下,如何通过Gitee 克隆Github上的项目
在Gitee创建仓库时,点击右上角点击导入
即可导入其他平台项目,并享受高速的克隆速度。非常方便~
本章内容就介绍到这里了,下面是一些补充资料:
补充资料一:一些Git相关的开源仓库
Progit2:https://github.com/progit/progit2
git-cheat-sheet:https://github.com/arslanbilal/git-cheat-sheet
githug–一个ruby编写的git练习游戏:https://github.com/Gazler/githug
gitignore模版:https://github.com/github/gitignore
git-extras:https://github.com/tj/git-extras
git-recipes:https://github.com/geeeeeeeeek/git-recipes
补充资料二:GitHub高赞项目推荐
awesome系列:
主仓库https://github.com/sindresorhus/awesome
周刊系列:
https://github.com/GrowingGit/GitHub-Chinese-Top-Charts
https://github.com/ruanyf/weekly
https://github.com/GitHubDaily/GitHubDaily
资源集合系列:
https://github.com/papers-we-love/papers-we-love
https://github.com/public-apis/public-apis
https://github.com/danistefanovic/build-your-own-x
https://github.com/GorvGoyl/Clone-Wars
https://github.com/TheAlgorithms