本文以Gitlab来讲解Git使用以及持续集成, 持续集成采用的是Gitlab CI. 其他代码托管网站,如Github, 都有自己或第三方的持续集成方案, 如Travis CI等.
1 注册Git账号并添加SSH密钥
首先在gitlab.com注册账号
1.1 生成个人ssh密钥
如果不存在~/.ssh/id_rsa.pub,那么首先要生成你的用户的ssh密钥.如果已经有ssh密钥了,可以跳过生成密钥步骤.
1.1.1 打开终端,输入以下命令生成密钥
创建ssh密钥,并使用邮箱地址作为标签
ssh-keygen -t rsa -b 4096 -C "你的邮箱地址"
当出现"输入密钥保存地址的提示",按回车保存到默认地址
Enter a file in which to save the key (~/.ssh/id_rsa):[按回车]
以下输入密码保护密钥,也可以直接回车使用空密码
Enter passphrase (empty for no passphrase): [输入密码]
Enter same passphrase again: [再次输入密码]
查看密钥的公钥
cat ~/.ssh/id_rsa.pub
示例输出结果如下,,复制输出,待会添加到Gitlab网站.
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDYw1z5WqltnMBnPzlTV7Jvxv5hV1fzC6E49rUCIe1s7cINPNcGPPep+qjFH1OibxdvD7g86vPWSn5OYCsWb2WoVTwmiSKhoJm2+qOqjXE+4liqULAbag4uFNGQdIkXWdDJomFihxGy6xHkWmUx9ILvph07j+ai4VZWlb1OHnvegPU1umXprWG9cFmNKAzexLwTma9FJSkBTaH4KSUcyO+Hm7MRq5xTfD1w1TcHI83L2x9iFuOE2yUHNs9ijfOwlRr2WwRQmL8K8U+K9rkPndosvMuvzjDScjOO1cXvoD1QbqK2w/ofYngC3mqChzOiOJqQcRPwHakOz5L3gUe5ZHvjl0BJ/icgFYX/9sS8UT9B2v6irdO2ZSab3KKDqq6w1wrKJLQqhh2vu9kxu29qzEegXZ/M4DFP5wAqn0T+GxFp0rvVvcIqao0r8BrWrgOzdcTN6YsdtXT7He2Q3xVCtkMjA9sTrqU+fKxHiPb9GAtHV6txfetX2RaBUGwnXwCHoU1uZM9fgYnyjs2mq/dSLOpzR1jsmKZHcCQ9nMpV7/sph0dji91c37JEwGG7+6D4Mkm74FF1yhYdShFIe2VhFUJYYiXjTgJzKqd3XYmw4fJRIU2PNsSYyolFnVuhGNBDu97BDoEjxaCoBJf8BqqTCPpQCI5piAt6drCoHAOSDEZ3mw== [email protected]
1.2 加入ssh密钥
1.2.1 登陆后点击头像中的Settings进入个人设置
1.2.2 点击SSH Keys一栏
1.2.3 添加ssh密钥
就这样, 添加密钥就完成了,后续就可以通过ssh方式访问git服务器了.
2 添加Git项目
2.1 点击添加项目按钮
2.2 输入项目信息
在选择项目权限的时候有几种选择
Private - 私有, 权限必须明确授予给用户
Internal - 内部, 项目能够被登陆的用户访问
Pubblic - 公开, 项目能够被公开访问
一般设置成私有或者内部
点击 Create project进行项目创建.
2.3 初始化项目
在项目创建好后会有提示指导你进行初始化.如下为可能需要做的步骤.以下小节并不是按顺序做的,需要根据选择使用. 建议每个项目根目录有README的文档说明项目.
2.3.1 Git全局设置
该步骤只需要在每台机器上做一次, 用来设置用户信息. 已经执行过的可以跳过
git config --global user.name "你的名字"
git config --global user.email "邮箱,推荐使用注册的邮箱"
如果执行以上命令时提示git命令不存在,请安装git后重试.
2.3.2 创建新的仓库
git clone 项目地址,如([email protected]:zhang.zhiyi/test.git)
cd test
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master
2.3.3 已经有文件了,添加到仓库
cd 已有文件夹
git init
git remote add origin 项目地址,如([email protected]:zhang.zhiyi/test.git)
git add .
git commit -m "Initial commit"
git push -u origin master
2.3.4 已有Git仓库
cd 已有仓库文件夹
git remote add origin 项目地址,如([email protected]:zhang.zhiyi/test.git)
git push -u origin --all
git push -u origin --tags
3. Git基本使用
以下为一些常用的git命令, 需要详细的使用方法,请参考Git官方文档和其他网上教程.
3.1 git clone克隆代码
git clone [email protected]:zhang.zhiyi/test.git
使用以上命令克隆代码,克隆成功在本地会生成项目文件夹.
3.2 git pull拉取代码
cd test
git pull
使用以上命令拉取最新代码,注意在使用本命令之前需要本地分支是干净的,如果不是,需要使用git stash暂存修改,或者使用git clean删除修改(不推荐).
3.3 git add增加文件
默认的文件是不会自动加到git仓库中的,需要使用git add命令加入到仓库中,没有加入的视为未跟踪文件,不会被commit提交.
如需要添加README.md文件
git add README.md
3.4 git status查看状态
当需要查看当前的文件状态的时候,使用git status,可以看到当前那些文件被修改,添加,删除等.
git status
若输出如下,则说明是干净的(不代表与服务器已经同步).
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
3.5 git commit提交代码
当使用git add
添加好需要提交的代码时,就可以使用git commit
进行代码提交了.
使用命令git commit
进行提交,会自动打开编辑器,需要输入提交的信息,一般使用一行主题就可以.对于较复杂的commit,可以加入正文,一般格式如下
一行主题,不宜过长
[空行]
正文,说明有什么更改,可以较详细
需要注意, 主题我们一般使用祈使句,如"Fix xxx","Add xxx","Implement xxx","修复什么","增加什么","实现什么","删除什么"等.
3.5.1 git commit -am 一次性提交
对于确定是全部修改的文件是需要提交的,并且提交信息不多时,可以用git commit -am一次性提交代码.如
git commit -am "Add README.md"
3.6 git push推送代码到服务器
当我们进行提交后,我们可以提交这些commits到服务器
如推送到origin地址的master分支,使用
git push origin master
如果执行了地址跟踪(使用了-u选项),如
git push -u origin
那么可以省略掉origin,直接执行
git push master
如果把分支名省掉,则是推送当前分支
git push
很经常, 如果服务器的分支进度跟你的不一致, 会发生冲突,这时候就需要拉取服务器上的代码后进行冲突处理.冲突处理在后面小节中讲到
3.7 git checkout检出代码
主要有以下用途
3.7.1 切换分支
执行完后会切换到目标分支,需要当前分支是干净的.如果本地上有修改,请先执行git stash操作暂存修改或者,否则将不允许切换分支.
git checkout dev
3.7.2 丢弃某个文件更改(恢复文件到分支未修改状况)
git checkout -- 文件路径
3.8 git branch新建分支
在想要新建分支的分支上执行
git branch 分支名
执行后会有分支名的名字创建好,通过以下命令可以查看现有的分支
git branch -a
删除分支使用-d或-D选项,-D选项强制删除.
git branch -d 要删除的分支名
或 git branch -D 要删除的分支名
3.9 git merge合并代码
merge操作主要用在这种情况,你新建的一个分支想和原有分支的新更改进行合并.比如你从master分支出一个新分支dev,然后master分支得到了更新,这时候你可以通过合并master的代码获取到这些更新.
首先你需要切换到另一分支
git checkout dev
然后执行git merge
git merge master
这样就会把master分支上的新更改同步到当前分支, 一般可以自动合并,但是如果有双方都修改了的代码,无法自动合并时,需要手动解决冲突, 后面小节会讲到.
3.10 git rebase重新切换基础分支
rebase操作主要用在这种情况:你新建了dev分支,在上面做了修改,然后master分支又更新了,然后你需要提交一个合并请求到master分支,由于你在dev分支上做了修改(未提交), 合并的时候基础分支master已经发生了改变,这时候会合并失败,所以要用rebase.
rebase原理是把你的dev分支修改先从当前分支上移除, 然后同步当前分支到master分支的更新. 之后在把你的更改一个个加回去dev分支.
git checkout dev
git rebase master
从此进入rebase状态,退出rebase执行git rebase --abort
,开始合并代码执行以下命令
git merge master
如果成功合并,执行git rebase --continue
直到所有修改都成功恢复.
git rebase --continue
rebase和merge的区别是rebase是把修改加到当前分支原分叉节点后,merge是把修改加到当前分支当前节点后.并且rebase是一步步接回去的, 每一步有冲突比较容易发现, merge是一步操作,一旦有冲突会比较多. 如果是在自己的分支上, 需要合入其他分支的更新,则使用merge, 如果是自己的修改没有提交, 则直接使用rebase即可.
3.11 git log查看日志
查看commit日志
git log
3.12 git 解决冲突
当出现冲突后,git会提示哪些文件有冲突,如test.txt在pull的时候有冲突
git pull
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.
这时候我们打开冲突文件test.txt,找到有左右箭头的部分
第一行
<<<<<<< HEAD
第二行
=======
修改过的第二行
>>>>>>> 6853e5ff961e684d3a6c02d4d06183b5ff330dcc
第三行
这时候在=======上面是当前分支的内容,下面是你修改的内容,你需要把这些部分修改成无冲突状态. 无冲突状态就是把你要的内容保留下来, 假如要保留修改的第二行, 那么修改成以下样子.
第一行
修改过的第二行
第三行
注意要把<<<<<<<,=======,>>>>>>>的整行也删掉.不然会被提交上去
在冲突解决后, 使用命令git add
把无冲突的文件加回去
git add test.txt
这样test.txt就无冲突了.
还有其他冲突解决方法,比如提交合并请求的时候可以直接使用网页上的提示解决冲突.
3.13 风格指南
未详述的风格,请参考Git 风格指南
3.13.1 分支命名
一般遵循命名惯例,如master, dev等. 多个单词的一般用短横线分割.当需要开发新功能或者修复某个bug的时候,推荐"类型/具体描述"的方式命名, 类型我们一般使用单个单词. 如新功能我们有以下命名示例
feature/support-rpm
feature/fast-boot
feature/download-prompt
对于修复bug有以下命名示例
fix/page-not-open
fix/cannot-boot
具体还有其他命名示例可以参考git branch naming best practices
3.14 .gitignore文件
该文件说明了那些文件应该被忽略,被忽略的文件将不被追踪,在提交的时候忽略.经常把一些临时文件,如.o文件,编译目标等加入到这个文件中. .gitignore模板
4 Git与持续集成
有了git那么方便的工具后,可以让持续集成的实现更方便. 本文的持续集成主要包含以下步骤:自动编译,测试,部署.并且本文采用Gitlab CI这一工具来实现实现持续集成.
由于持续集成是一个流程, 本文在原本持续集成的基础上, 再添加了平时开发项目应有的流程. 所以下面以项目开发流程来描述持续集成.
4.1 实施编码规范
首先需要在团队里面,采用标准的代码规范. 代码规范中应规定命名规范,开发规范,文本编码等. C/C++的代码规范可以参考Qt/C/C++推荐代码规范,未说明的或者其他语言请参考Google 代码规范,建议采用官方的代码规范.如Python,则参考Python官方的规范.
4.2 项目开发
4.2.1 项目初始化设置
建议加入一些警告选项到编译选项中,比如C/C++工程使用Makefile和GCC进行构建时,建议有以下编译选项.
-W -Wall -Wextra -Wformat=2 -Wshadow -Wcast-qual -Wwrite-strings -Wmissing-include-dirs -Wunused-parameter -Wstrict-overflow=4 -Wfloat-equal
C++还可以使用以下更多选项
-Wshadow -Wcast-qual -Wwrite-strings
其他选项可以参考GCC官方文档和其他教程.
对于内核开发,应该准备一个打开了大多数调试选项的开发版内核,如开启死锁检测,魔术键支持等,并在虚拟机中运行该内核,避免影响主机系统.
4.2.1 编写代码
建议在统一代码规范的前提下使用自己熟悉的IDE或编辑器编写代码,
建议建立新分支存放自己的代码,每个新分支代表一个新功能或者一个bug修复方法.
代码编写的时候可用一些代码自动格式化工具确保符合团队代码风格,如C/C++可以使用Clang-format工具,具体实施参考Qt/C/C++推荐代码规范和网上教程,其他语言也有相关的工具或者IDE插件来格式化代码.
4.2.2 静态代码检查
当代码编写完成之后,应该使用一些代码静态检查工具,C/C++有Cppcheck,Clang的scan-build工具,Visual Studio的自带静态代码检查工具.具体的使用方式请查询相关文档. 这些工具可以检查代码中一些编写人员在编码过程中犯的一些错误,如可能的空指针引用,内存泄漏,未初始化的变量,未使用的函数等.
4.2.3 动态代码检查
对于一些较复杂的bug, 需要使用代码动态检查工具才能检查出来. 对于C/C++工程, 推荐的动态代码检查工具有GCC Sanitizer,Valgrind等. GCC Sanitizer是在编译选项中加入sanitizer选项,使编译出来的代码在运行时能够检查, 会降低软件性能, 所以一般在debug编译中使用.
GCC Sanitizer可以支持内存泄漏,越界,线程数据竞争等众多的错误,平时开发建议加入. 用法请看GCC Manual 3.11 Program Instrumentation Options
Valgrind由于比较敏感和较难自动化,一般手动调用,Valgrind也能支持很多错误检测,但是平时用起来有较多的误报,需要自己判断一下.
4.2.4 编写单元测试
当一个功能点开发完成后,应该编写单元测试. 单元测试应该覆盖关键函数和绝大多数路径, 建议了解一些软件测试的方法. 对于Qt/C/C++工程,可以使用如Google Test或者Qt Test的测试框架来实现单元测试. 其他语言也有相应的单元测试框架,在此不赘述.
4.2.5 代码提交
当软件功能完成, 并且编译,静态检查,动态检查,单元测试都通过时可以提交到主分支. 如果是提交到自己的开发分支,可以不一定每项测试在每次提交都要通过,但是最终提交需要都通过.每次commit建议一个完善的部分进行提交, 比如实现了某个功能的某部分,就提交. 建议小量多次提交, 后续容易恢复和查找问题.
当功能点完成之后,可以向主分支发起合入请求. 在提交合入请求之前请检查测试都通过,并且符合代码规范,同时没有合并冲突.
发起合入请求后,把合入请求分配给另一个同事,其他同事如果发现有问题,填写评论后给回提出合入请求的同事。问题都解决后分配给有合入权限的同事。
4.3 CI(Continuous integration,持续集成)部署
对于4.2节中的一些检查部分,如果需要每次人工执行,必然会出现遗漏. 并且对于能够自动化的东西,尽量要自动化,这样才能提高开发效率.
在此,以Gitlab CI为例,实现项目的自动编译,代码检查,测试和部署.
4.3.1 Gitlab Runner部署
Gitlab runner就是实际执行.gitlab-ci.yml中的命令的实体. 可以是Docker镜像,VirtualBox虚拟机,也可以是真实的机器的Shell. 一般使用Docker镜像进行构建,因为Docker镜像每次重新运行环境是一样的,使用真实机器的Shell需要项目保证环境的正确性.
一般Gitlab Runner的部署由管理员进行部署,部署好后就能够被各个用户进行使用,如果有特殊需要,需要自己的Gitlab runner,那么请参考部署自己的Gitlab CI Runner 进行部署.
要查看一个项目能够使用的Runner,点开项目的Settings->Pipelines,绿色图标的就是在线的可用Runner.
如果需要使用特定的Runner来执行命令,通过.gitlab-ci.yml中的tag或image字段来指定.
4.3.2 编写.gitlab-ci.yml
Gitlab CI需要有一个.gitlab-ci.yml文件放在仓库根目录下对CI进行配置.
.gitlab-ci.yml文件采用的是yml格式,具体语法参考.gitlab-ci.yml的编写,项目模板参考.gitlab-ci.yml模板.
当.gitlab-ci.yml文件编写好后,放到仓库根目录并提交上去就可以, 如果没有错误, Gitlab CI就会执行文件中编写的步骤. 每次代码提交都会触发Gitlab CI的运行, 执行每个步骤, 如果某些修改,如只修改文档,不会影响项目代码正确性的话,可以跳过CI的执行.要跳过CI的执行,在commt的message里加入"[skip ci]"(不分大小写)
一开始编写yml文件时,可能会遇到一些问题,导致yml文件无效,建议在提交之前使用CI lint工具检查一下yml文件的正确性, 该工具可以在gitlab项目的Pipeline->Jobs->CI lint中找到.
在.gitlab-ci.yml编写好编译,测试,部署的步骤并提交后,CI的配置就算是完成了.
参考资料与其他建议阅读材料:
Git
- git ssh配置和使用
- Git教程
项目开发
- 语义化版本
- Google 开源项目风格指南
- Linux下doxygen的使用
- Cppcheck 用法
- Scan-build用法
- GCC Manual 3.11 Program Instrumentation Options
- The Valgrind Quick Start Guide
- Google Test
- Qt Test
Gitlab CI部署
- 使用Gitlab进行持续集成
- CI(持续集成)入门,在gitlab上配置CI
- .gitlab-ci.yml的编写
- .gitlab-ci.yml模板
- 部署自己的Gitlab CI Runner
以上为Gitlab CI的部署和使用,如果使用其他产品,如Github,也有很多类似服务,如Travis CI等
Docker
- Docker入门
- Kubernets入门,学习使用Docker建立服务集群