如何规范地使用git——以ACM模板管理为例

发布时间 2023-11-26 21:23:00作者: JHSeng

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命令,查看当前仓库可用分支与所处分支。

现在,我们可以进行代码开发了。一个完整的开发流程如下:

  1. pre-release分支新建开发分支
  2. 在开发分支上贡献代码
  3. 测试自己的代码、确认正确性,并合并所有commit
  4. 将开发分支的代码改动合并至pre-release分支
  5. pre-release分支提起Pull Request,将代码改动从pre-release分支合并至main分支
  6. 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改为ssquash。对每一行来说,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的代码。