“Your organization’s codebase is sustainable when you are able to change all of the things that you ought to change, safely, and can do so for the life of your codebase.”
——Software Engineering at Google, Titus Winters, Tom Manshreck and Hyrum Wright
课程的团队课设最终要求大家交付一个完整可用的软件系统。时长两个月的团队开发中,我们将时时对团队最重要的资产——代码库进行修改、迭代和完善。因此,保障它稳定、安全、可持续地存在,并能够稳健地持续合并来自每个成员的变更、扩展和迭代是重要的。
我们可以采用无数种可能方式来管理这一资产:基于Q〇群文件分享代码压缩包会得到助教组充满敬意的赞👍、邮寄代码的完整纸质副本会在这门课程的历史上留下足够先锋的脚印👣......但如果你也一样,对 软工课堂展示-Pre-Draft5By熊-ffffffinal最终版真的绝对不再改了.pptx
这样的文件名抱持与生俱来的恐惧、又或是憧憬无纸化办公的环保主义者,那么在团队开发中引入版本控制系统(Version Control System, VCS),绝对是值得的投资——事实上,也是当下软件工程领域明确的共识。
软件工程是随着时间的推移而集成的编程......开发本质上是一个分支和合并的过程,无论是在多个开发人员之间还是在不同时间点的单个开发人员之间进行协调。
多人、长时的开发流程,需要正式的版本控制系统来管理源代码、协调工程师之间的活动。团队开发的过程中我们会自然地产生如下需要:
- 追踪工作的最新进度和历史进展;
- 并行地进行工作,提交更改;
- 合并修改、跟踪合并;
- ......
为了支撑这些需要,除了将 VCS 集成进我们的工作流程之外,我们还需有一套共识性的规范来指导对 VCS 的使用——团队开发中对代码库负责的不是某一个人,而是每一个人。
在这篇 Guide 里我们将一起通过一次团队的实践,认识版本控制系统在集体开发中的具体做法,并对形成行之有效的协作规范有所见解。
版本控制系统的选择有很多,你可在这里进一步了解。这篇 Guide 基于目前最流行、也是大家最可能选用的分布式版本控制系统 Git 设计。
附录 A 提供了一个软件工程小组的 Git 规范。接下来会给定一些任务,请依据指引遵照这份规范来进行代码修改和 Git 操作。
请注意,这份 Git 规范并不是团队开发的最佳实践,在不同的情形下、不同的团队中需要针对各类因素来设计适用的规范和工作流。我们基于这样一个目标采用了这份规范并设计了这次作业:涵盖团队开发阶段使用版本控制系统时可能遇到的常见场景和具体问题,从而让大家对这些问题有基本的认识、进而制定和协调最适用于各自团队的工作流。
助教组会根据 commit 记录等仓库遗留的操作记录检查每个团队的完成情况。
请保留全部的操作记录,包括分支、issue、pr 等。
当你的操作出现不致命的小差错、与“规范”相悖时,不用担心,这些无心之过可以原样保留在操作历史里。但是,“觉得自己搞砸了”的情形下,希望你能在代码仓库根目录下的 error-logs.txt
中留下你对错误的记录。
如果出现团队成员误操作、导致难以还原的情况,请首先了解可能的恢复方式、或在课程群中讨论有关做法,在完全无果的情况下可以重新开始。再次提醒大家:团队开发中对代码库负责的不是某一个人,而是每一个人,请谨慎地对待这份团队最重要的资产。这些情况,需要在作业提交时记录。
助教组可能会根据记录情况询问团队相关问题。
请按照顺序完成各组任务。同组内无序列表标注的任务没有严格的顺序。
标记为 🚧Maintenance
的任务请团队中的特定成员负责,通常可以由小组中的 DevOps 岗完成——当然,也可以分配工作给某几位同学一起完成;
标记为 🧑💻Every
的任务请团队中的每一位成员参与;
标记为 🚩Team
的任务请团队将列表中的任务分配给各位成员完成,每位成员至少完成一项、一个任务只能分配给一个人完成。
- 请大家在一起讨论对于 DevOps 的理解,分配本团队的 DevOps 相关工作。操作上,也可以由团队决定一位同学来担当全部相关工作。
- 请大家调研并根据小组内的网络环境、使用习惯等因素决定 DevOps 的各项考虑和服务选型,也可以使用一体化的 DevOps 平台例如腾讯云 CODING. 至少,需要决定一款代码托管平台。对于这次作业,请在 GitHub 或 GitCode 中二选一完成。
- 选择好之后,请各位成员在相关平台上完成账户注册、创建组织等工作。请注意,本次作业的代码仓库需要保证至少课程组内部可以自由访问以保证公正性;除非特殊情况,建议保持公开。
Fork
(或在使用其他平台时Clone
)GitHub 仓库 或 GitCode 仓库,初始化本团队的仓库;- 设置代码仓库中成员的相关权限;
- 建立一个
dev
分支; - 检出分支
chore/init
; - 在根目录下创建一个
.gitignore
文件,忽略根目录下的info.txt
文件; - 在根目录下创建一个
error-logs.txt
文件; - 为仓库设置 Issue 模板,你可以参照 GitHub 教程,或 GitCode 教程;
- 提交更改,按照合并规范操作,最终合并你的更改到
dev
分支; - 通知团队的所有成员现在可以开始进入下一个阶段
Task.1
了。
-
每位成员:拉取代码仓库到本地;
-
在根目录下新建一个
info.txt
文件,向其中添加如下信息:name: <你的用户名> description: <你的一句话自我介绍>
请注意不要包含太敏感的个人信息。
-
接下来的任务,模拟开发中的各种情形,所有操作在
/src
下完成。提醒:请注意分支和提交规范。 -
请认领如下任务中的至少一项:
-
新建文件
frontend.json
,格式如下:{ "data": [ { "name": "<名称>", "link": "<链接>" }, ... ] }
列举至少三个你所了解的前端框架。
-
新建文件
backend.json
,格式如下:{ "data": [ { "name": "<名称>", "link": "<链接>" }, ... ] }
列举至少三个你所了解的后端框架。
-
新建文件
midend.json
,格式如下:{ "data": [ { "content": "<内容>", "language": "<语言>" }, ... ] }
列举至少两条你所了解的双关 / 谐音冷笑话。
-
修改文件
tga-awards-2022.json
,文件内记录了几项 2022 年度 TGA 大奖的获奖作品,其中有一项似乎记载错误。这是一个 BUG,请按照Bug 提出与修复
的规范进行修复。如果获奖结果和你的喜好不一致,你也可以声称某一项是 Bug 并修改为你喜欢的。 -
修改文件
golden-melody-awards.json
,文件内记录了近些年金曲奖的年度专辑奖获奖信息,其中有一项似乎记载错误。这是一个 BUG,请按照Bug 提出与修复
的规范进行修复。如果获奖结果和你的喜好不一致,你也可以声称某一项是 Bug 并修改为你喜欢的。 -
修改文件
anime-list.json
,文件内记录了一些数据。原本的记录方法是:{ "data": [ { "name": "<名字>", "en-name": "<英文名字>" }, ... ] }
助教派来的 PM 路过瞅了一眼,拍了拍脑袋,觉得太长了,需要重构成如下形式:
{ "data": { "names": ["<所有名字>"], "en-names": ["<所有英文名字>"] } }
请你不跟 ta 一般见识,帮 ta 完成这个微不足道的梦想。
-
修改文件
really-ugly-json.json
,代码风格差强人意,请你帮忙调整代码风格(不影响语义)。
-
-
当你完成任务后,提交更改(aka.
git commit
); -
按照合并规范操作到提出 Pull Request;
-
全员完成后进行到下一阶段。
- 请大家在一起讨论你们如何进行 Code Review、如何处理团队产生的 Pull Request;
- 请大家完成本次任务中所有产生的合并,如果产生了冲突请讨论为什么会产生冲突、应该如何解决。并且讨论附录 A 中的合并规范可能出于什么原因要求采用
git rebase
,而没有提到我们更为熟悉也一如其名的git merge
,这样的选择会带来什么样的问题和益处?可以参考这篇文章来了解相关信息; - 请大家根据分支规范完成发布前的确认、版本发布和Tag 操作。然后,请根据你们的实践结果在一起讨论完成一次迭代后,你们如何完成代码的确认和发布。
-
接下来的任务,所有操作均在
/src/info
下完成,其中预置文件的格式均相同:{ "username": "<用户名>", "data": "<数据>" }
请保留格式,只修改内容;
-
请认领如下任务中的至少一项,每位同学领取的任务不能相同,先不要着急完成:
- 修改文件
music.json
,将内容修改为你的用户名和你最喜欢的歌名; - 修改文件
movie.json
,将内容修改为你的用户名和你最喜欢的电影名; - 修改文件
anime.json
,将内容修改为你的用户名和你最近印象最深的番剧(aka. 动漫作品); - 修改文件
game.json
,将内容修改为你的用户名和你最喜欢的游戏名,只改用户名也没关系; - 修改文件
food.json
,将内容修改为你的用户名和你最喜欢的食物; - 修改文件
TA.json
,将内容修改为你的用户名和你最喜欢的助教:这一项真的不会影响分数、只跟某人的心情有关; - 修改文件
pl.json
,将内容修改为你的用户名和你最喜欢的编程语言。
- 修改文件
-
认领好任务后,请在此时一次检出新分支,对应你选择的一项,命名为
feature/type-1
,type
对应你选择的那项任务,例如feature/food-1
; -
仍然不要开始完成任务,回到第二步,认领你第一次没有选择的一项,每位同学领取的任务不能相同。然后再次进行第三步,这次将分支命名为
feature/type-2
; -
现在可以开始完成你所选择的第一项任务了。请在
feature/type-1
分支上操作,完成修改之后,提交一次更改(FYI, aka.git commit
);然后再次修改<数据>
项的内容为其他内容,再次提交一次更改。接下来,执行合并规范至提交 Pull Request。等待,不要开始第二项任务;
- 请按照你们团队的 Code Review 和 Pull Request 处理流程,由有仓库管理权限的成员完成第一项任务所做修改的所有合并工作;
- 解决掉所有 Pull Request 后,请仓库管理者通知大家可以进行第二项任务了。
-
请完成你所选择的第二项任务。请切换到
feature/type-2
分支,完成修改之后,提交更改,然后再执行合并规范,如果此时你遇到了冲突,请解决每一个冲突:如果有来自他人编辑的信息,出于尊重请你把信息合并成:{ "username": "<他人的用户名>, <你的用户名>", "data": "<他人的最新数据>, <你的数据>" }
的形式;
-
按照合并规范操作到提出 Pull Request,然后不用再等待,请团队的代码管理成员
🚧Maintenance
尽快处理 Pull Request,使修改合并到dev
分支。
- 请大家在一起检查最终完成的
dev
分支代码,是否出现了修改丢失的状况。如果有,是谁的什么操作导致的? - 请讨论在哪些地方遇到了冲突,如何解决,有没有意料之外的状况,应对这些状况进行了哪些操作。如果在合并 Pull Request 的阶段遇到了冲突,请讨论冲突的缘由。作为参考,可以在完成后的代码仓库下执行命令
gitk
,对照显示的分支图进行讨论; - 请大家根据分支规范完成发布前的确认、版本发布和Tag 操作,发布第二次迭代后完成的版本。
-
请大家一起完成以下任务,或许只需要一个人来操作,但需要所有人了解操作的流程。
-
请设想这样一个场景:在发布前检查中你们发现
/src/tofix/classdata.json
存在 Bug:由于助教的某种疏忽,任课老师的名字打错了!请依照分支规范的说明,在release
分支上进行修复; -
请设想这样一个场景:在将代码开源之前,你们突然发现
/src/tofix/SUPER-SENSITIVE-DATA.txt
含有某种绝密信息!而该文件(和它的信息)已经出现在远端仓库(的许多许多次提交)中了。请先 Clone 一份你们的仓库来避免惨剧发生,然后根据这篇指引执行操作,将这个文件从所有提交中移除,然后在远端新建一个代码库,将你们完成操作的结果推送到新的代码库。务请注意:这个文件是在你们开始修改前被不怀好意的助教引入的,请检查仓库中你们开始本次作业以前的各次提交,查找文件在被移动到这个位置之前还在哪里出现过,对应又该如何执行操作。找不到也没关系啦!但请一定要注意清除敏感数据时要考虑到文件或许被移动或改名而存在于其他路径的状况。
-
作为作业的终点,请在团队博客上提交一篇博客,内容至少包括:
- 团队的代码仓库地址;
- 团队完成 Task. HotFix! 后新建的代码仓库地址;
- 需要说明的状况。
-
同时,推荐团队完成以下内容,这部分内容在之后的团队项目博客作业(具体来说是项目展示部分)中仍然需要完成,因此现在完成可以减少之后的负担:
- 团队选定的 DevOps 技术选型以及选择的原因、使用相关技术的方式;团队对代码仓库的管理、对这方面工作的安排;
- 团队对于代码审查的考虑,代码发布的流程;
- 在代码管理中,团队会遇到哪些风险,如何解决这些风险;
-
最后,如果确实有所心得,希望团队能记载过程中种种实践和讨论的所得,梳理一下,这些心得可能包括:
- 这次实践中遇到了哪些问题,如何解决和避免;
- 版本管理系统的协作中,除了使用软件之外,还需考虑哪些内容:例如,流程、规范等;
- 适用于你们团队的版本管理系统协作规范;
- 对具体软件操作的理解:例如上面提及的
git merge
和git rebase
;
等等,这些内容不一定要留在博客中,更不参与计分,但一定会成为团队实践和个人软工路上的宝贵财富。
一个软件工程小组的 Git 规范。
这份规范由 roife 在 2022 级北航软件工程的团队开发中编写。
<type>(<scope>): <subject>
<body>
- type 有下面几类
feat
新功能fix
修补bug(在<body>
里面加对应的 Issue ID)test
测试相关style
代码风格变化(不影响运行)refactor
重构(没有新增功能或修复 BUG)perf
性能优化chore
构建过程变动(包括构建工具/CI等)
- scope(可选):影响的模块
- subject:主题(一句话简要描述)
- body(可选):详细描述,包括相关的 issue、bug 以及具体变动等,可以有多行
[feat](账号模块): 增加微信登录验证
[fix](管理员 UI): Safari 下界面适配
1. xxx 元素 yyyy
2. aaa 页面 bbbb
Issue: #3
[refactor](招聘信息接口): xxx 接口更新
[style]: 格式规范更改,重新格式化
请不要用 fix #3
之类的 message 关闭 Issue!请按照 BUG 提出与修复的描述用 Pull Request!
代码仓库分为以下六类分支:
- main 分支
线上在跑的版本
- 提供给用户使用的正式版本和稳定版本;
- 🏷️ 所有版本发布和 Tag 操作都在这里进行;
- ❌ 不允许开发者日常 push,只允许从 release 合并。
- release 分支
将要上线的版本
- 从 develop 分支检出,只用于发布前的确认;
- 允许从中分出 fix 分支,修复的 commit 需要 push 回 dev;
- ❌ 不允许开发者日常 push,只允许从 dev 合并。
- dev 分支
日常开发汇总
- 开发者可以检出 feature 和 fix 分支,开发完成后 push 回 dev;
- 保证领先于 main;
- ❌ 不允许开发者日常 push,只允许完成功能开发或 bug 修复后通过 pull request 进行合并。
- feature 分支
- 从 dev 分支检出,用于新功能开发;
- 命名为
feature/name
,如feature/resume_generation
; - 开发完毕,经过测试后合并到 dev 分支;
- ✅ 允许开发者日常 push.
- fix 分支
- 从 dev 或 release 分支检出,用于 bug 修复(feature 过程中的 bug 直接就地解决);
- 特殊情况下允许直接从 main 直接开 fix 分支进行修复;
- 命名为
fix/issue_id
,如fix/2
; - 修复完毕,经过测试后合并到原来的分支(dev/release/main),并且保证同时合并到 dev;
- ✅ 允许开发者日常 push.
- chore 分支
- 从 dev 分支检出,用于各项修正,如重构、风格优化等;
- 命名为
chore/name
,如chore/resume_generation
; - 开发完毕,经过测试后合并到 dev 分支;
- ✅ 允许开发者日常 push.
不许在别人的分支上开新分支,只能在主分支开分支。分支粒度可以尽可能小。
我在 my_feature
上开发了一段时间了,想要合并到 dev
,可以这么做:
- 拉取最新的
dev
; rebase
当前分支到dev
,并且解决rebase
带来的问题;rebase
完成后,将当前分支推到远端(TA注:请考虑一下这个推送的操作要在什么条件下进行),在 GitHub 上发起一个到 dev 的 Pull Request,及时合并,防止更多冲突。
假设我在 my_feature
上开发,其他人开发的功能上线到 dev
分支了。
那么我可以在当前的分支下 rebase
一下 dev
分支,这样我这个分支的几个 commits 相对于 main
还是处于最顶端的,也就是说 rebase
主要用来跟上游同步,同时把自己的修改顶到最上面。
开发的时候,如果你的功能很复杂,尽量及时 rebase
上游分支,有冲突提前就 fix 掉。
这样即使我们自己的分支开发了很久,也不会积累太多的 conflict,最后合并进主分支的时候特别轻松。
反对从主分支 checkout 出新分支,自己闷头开发,结果最后合并进主分支的时候,产生有一大堆冲突。
最后,不要在公共分支(例如 dev/main)上瞎 rebase!
- 提出一个 issue,按照 template 简要记录 bug 相关的信息,事后能看懂即可,不用太详细;
- 开一个
fix
分支,对 bug 进行修复。fix 修复完成后需要进行测试,确认已经修复; - 开一个 pull request,并将 pull request 和 issue 关联起来;
- 合并 PR。
❌ 不要在 commit message 里面用 fix #3
等直接 close issue,应该在 pull request 关联 issue.
如果是在 feature 分支开发过程中遇到的小 bug,可以直接顺手本地修了,不用按照这个流程来。
我们预设大家已经通过既往课程对 VCS 有了基本的认识、并熟悉个人代码管理的基本操作。如果你不具备相关的背景知识,这里提供如下链接供你参考:
这篇 Guide 参考了 Git 团队协作 和 Google 软件工程。对于前者,请寻找出版商购买;对于后者,你可以线上阅读中文版或英文版。
这里还有一些参考文章以备阅读:
- Git 使用 Merge 和 Rebase
- Commit message 和 Change log 编写指南
- GitHub Pull Request入门
- A successful Git branching model
Ver.3 by @KumaXX