如何规范地使用git——以ACM模板管理为例
Introduction
最近看到ACM群的学弟讲到用git的问题,回想起自己刚工作时还不会用git,连怎么维护代码都不知道,闹出了一些笑话。
git作为一个很好用的代码管理工具,还是要趁早学习,按最规范的git flow维护自己的代码。可惜SCUT软院项目管理课太水,对于代码的维护、发布、修复流程没有任何介绍,只能靠摸索前人的经验来学习。
这篇文章以ACM模板管理为例,介绍最简单的git flow,足以覆盖大部分工作场景。
在看这篇文章之前,你需要做好如下准备:
- 安装git并学习基本的使用方式(至少知道怎么clone/pull/push/commit/log)
- 创建github账号,并配置ssh-key
- 随便创建一个仓库,并使用ssh方式clone至本地,验证ssh-key可用性
Single Work Flow
在github新建一个仓库,存放你的ACM模板。这里命名为MyACMTemplate,并添加一份README文件,作为仓库的描述文件。
创建后长这样。
我们将这个仓库通过ssh的方式clone到本地,并使用编辑器打开这个仓库。
用vscode打开仓库后发现一干二净,用git branch
查看该仓库的分支,显示我们当前正使用main(以前叫master)分支。
我们规定:main分支不能直接用于开发,只能存放已测试并已发布的代码。这个仓库目前仅有main
分支,还不具备开发代码的条件。
我们需要创建一个存放已测试但未发布的代码分支,而这个分支将从main
分支创建出来。我们将这个分支命名为pre-release
。
通过git checkout
命令,在原有的main
分支创建pre-release
分支。并再次通过git branch
命令,查看当前仓库可用分支与所处分支。
现在,我们可以进行代码开发了。一个完整的开发流程如下:
- 从
pre-release
分支新建开发分支 - 在开发分支上贡献代码
- 测试自己的代码、确认正确性,并合并所有commit
- 将开发分支的代码改动合并至
pre-release
分支 - 从
pre-release
分支提起Pull Request
,将代码改动从pre-release
分支合并至main
分支 - 在
main
分支上创建git tag,更新仓库版本
我们走一遍完整流程。首先,我们在pre-release
分支创建新的开发分支。按照git分支命名规范,涉及到新功能开发的分支,命名以feat/
开头。考虑到大伙都很爱线段树,分支名取为feat/segment_tree
。
我们假装贡献一段线段树代码。
commit代码并推送至远程仓库。第一次推送时会提示当前分支 feat/segment_tree 没有对应的上游分支
,按提示操作即可。
刷新github repo页面,并切换至feat/segment_tree
分支,就能看到推送上去的代码了。
此时我发现代码有个bug:默认构造函数没有给类成员属性a和b赋初始值。赶紧修复并提交第二次commit。
用git log
命令可以查看已经提交的两次记录与相应的commit id。
现在假设代码已经开发完毕,测试后没有bug,需要将所有commit合并为一个commit(我们刚刚进行了两次commit),再合并至pre-release
分支。
在确认代码合并前,需要用git log
命令找到第一次commit的前一个commit id,这里为77a4610d908b8891bbb98b6a30cb4b36fd157a9a
。
通过git rebase
命令合并代码。
输入后出现如下界面,我们需要将第一个pick之后所有的pick改为s
或squash
。对每一行来说,pick意味着本次提交生效,squash意味着本次提交内容将合并至上一次提交。
改完后效果如下。
输入:x
保存修改内容并退出vim。此时将再次弹出新编辑页面,提示修改commit info。
将所有的提交信息归纳总结一下即可。这里我们直接采用第一次commit info。
输入:x
保存修改内容并退出vim。此时显示git rebase
命令已经成功执行。
通过git log
查看我们已经修改完的commit信息,现在只有两条commit,但第二次commit会包含我们所有的新提交内容。
注意:我们在本地合并提交后,远程仓库的feat/segment_tree
分支的commit仍然未合并(可以看一下github repo看看是不是真的如此)。
我们需要使用git push -f
命令,将本地feat/segment_tree
分支强制覆盖远程feat/segment_tree
分支(直接git push
会提示本地分支内容与远程分支有冲突,这里我们以本地分支内容为准,所以直接强制覆盖即可)。
覆盖后刷新github repo页面并点进提交,可以看到两次commit确实被合并为一次,并包含所有的提交内容。
到这里,feat/segment_tree
的分支就已经处理完毕了。我们checkout回pre-release
分支,使用git merge
命令将feat/segment_tree
分支的代码合并过来,并同样推送至github。
由于pre-release
分支也是第一次执行推送,会提示当前分支 pre-release 没有对应的上游分支
,按提示操作即可。
推送后查看github repo的pre-release
分支,会发现代码提交已经被合并过来了。
当我们想把pre-release
的代码发布时,需要发起从pre-release分支到main分支的Pull Request
,并提醒repo管理员进行code review(CR)。CR过程没问题后,才能将代码合并至main
分支。
点击页面Pull Request
标签,并点击New pull request
按钮。
选定base分支为main
,compare分支为pre-release
分支,即可显示这两个分支的相异提交及其涉及代码。
点击Create pull request
并补充标题与描述,就算正式创建一个Pull Request
了。页面显示本次Pull Request
没有代码冲突,可以合并。
这里可以看到下面的Merge pull request
按钮是绿色的,因为我们是这个仓库的管理员,有权限合并代码。实际上,当我们参与多人项目时,没有权限合并自己发起的PR。PR必须经过若干人review内容,确认代码无问题并符合规范后,才能合并入main
分支。
我们点击Merge pull request
合并代码,可以看到Merged
的状态。
如果仅仅只是维护ACM代码,其实到这里就够用了。但在进行项目开发时,还差最后一步,代码才算完整发布。
我们需要给当前的main
分支打tag(对于开发人员来说不用管了,your task is over)。点击页面右侧的Create a new release
发布新release,给定版本号和描述后发布。
发布后就可以看到版本信息了。
注意tag是可以当做分支使用的(但不要基于tag新建分支!一般来说只用来看看代码内容就够了)。执行git fetch
更新一下仓库信息,然后直接checkout到tag,能看到对应tag的代码。