使用git协同开发的一些总结探究

March 18, 2025

git 一般使用体验

git 是用于版本管理以及代码协作开发的一个工具,对个人而言,git 的作用主要体现在版本管理这一个方面上,像是用于维护云端代码仓库,做完一个功能点之后可以进行一次提交,出现问题时,可以通过对应功能模块的提交记录进行排查以快速定位出现问题的版本,现今的开发工具多数都内置对 git 的支持,对于个人使用而言,git 是较容易上手的。

更贴近业务的开发流程中,团队协作开发的情况往往是很常见的,从个人开发转变到团队开发,需要更深入地学习 git 的知识。笔者个人体验下来,除了要接触新的一套开发流程之外,还背上了一些心理负担,毕竟是合作开发的代码仓库,~~有时会担心误操作将代码删除了或开源了,~~现实点来讲,也会有因为重复或无效操作造成的开发质量及效率低下的顾虑。所以自然是要多学多问多总结了。

vscode 的图形化工具

就像上文说的那样,vscode 也有支持 git 的一套图形化工具,个人使用时,新建本地仓库,推送至远程仓库,提交与同步,新建以及切换分支,这些功能对应的按钮都很明显,易于使用。但是包装精细的后果就是难以触及原理,每当 git 出现错误或功能不生效,需要查看输出时,大多都会因为一个操作自动调用的一大堆 git 指令而手足无措,对于一些特殊操作的处理,例如取消合并,也有可能会找不到对应的功能按钮而默认这样的功能不存在。学习的过程中,最好还是要结合一些终端 git 指令进行操作。

使用终端键入 git 指令

自己手动键入 git 指令是对学习 git 的重要实践,观察每次执行指令的反馈,能让我们对每条指令的作用有更深的理解。常用的指令不多,配合上终端的 tab 自动补全,很快就能上手基础的操作。

git init #初始化仓库
git clone #克隆仓库

git push #推送到线上仓库
git pull #包含git fetch与git merge,从线上获取新提交并合并到本地分支上

git restore <> #还原更改

git add <> #暂存提交
git reset <> #取消暂存

git commit -m "msg" #以msg内容进行提交
git reset --soft HEAD~1 #取消提交到暂存状态,mixed和hard分别是到更改和未更改状态,此时在reflog中会留下记录

git branch #查看分支
git branch <> #新建分支
git checkout <> #签出到分支
git branch -d <> #删除分支

git merge <> #将其他分支合并到当前分支
git merge --abort #取消解决冲突,合并中止

在使用指令的时候,也能理解到图形化工具按钮上一些专有名词的实际意义,对构建一个扎实的 git 基础有相当不可或缺的作用。当然,图形化工具能详细和具体地反映 git 处理情况,笔者觉得指令熟练后结合使用工具也是一个能提高开发效率的选择。

本地操作和线上的界限

使用图形化工具进行一些会与线上仓库关联的举动时,会弹窗提醒,而使用指令进行的 push 和 pull,往往键入后立即就执行了。所以理解一下本地和线上的界限也许或多或少能减轻一下心理上的负担,笔者使用下来感觉,基础的使用流程只要不执行 git push,那么则不会影响到线上的仓库,这时本地的分支就算是卡住在某个操作不知道怎么执行了,也可以直接删除分支然后从线上仓库再拉一个分支下来。当然,想最没负担地操作的话就自己建一个测试仓库吧。

git 协作方式

将视角转移到团队上,团队协作一般会制定一些规则,使用 git 协作也不例外,开源的做法一般是去 issue 区认领需求,然后拷贝一个仓库到自己的账号上(fork),新建一个分支并编写自己的更新提交,最后再去提 pr 修改。若是自己的小团队进行一些非公开项目的开发,也可以省略掉认领和 fork 的步骤,分配好任务后直接在主仓库新建分支进行开发,当然最后也要提 pr 让对应负责人进行 review 和合并就是了。这是目前笔者了解到的协作方式,且使用 github 居多,后文的内容会基于这些前提展开。

提一提 submodule

如果有一个部分的内容是多个项目之间复用的,则可以使用子仓库模式,好处是可以统一更改,只需要在不同的项目间设置好子仓库的接入方式,并且在克隆项目时记得要同时初始化子仓库以及下载子仓库的依赖就可以了。

测试分支和主分支

一般项目在上线前需要通过一个测试流程来确保生产环境的稳定,所以一般开发项目会有一个测试分支,测试分支一般也会专门部署到线上,作用是在测试环境验证开发分支的更改有效并且无漏洞,并且可以给非技术人员直接看到修改,不需要配置本体运行环境。测试分支的存在使得主分支的更新更有效且稳定,并且一定程度上提高了开发质量和效率,开发分支并入测试分支查看线上效果时一般没有很大的心理负担,但是也有情况会耽误开发,例如将无法运行的代码推送到了测试环境,那么测试分支的代码就不能被正常构建,这样会导致其他合作人员想推送到线上测试环境构建的新功能也无法被构建,所以在推送到测试分支前,要在本地先确保代码能正常运行。另外,因为开发分支是从主分支新建出去的,所以每次修改分支的内容时都要将修改同步合并到测试分支,不然下次其他成员从主分支新建分支,合并到测试环境时,就会带上一些别的提交,导致冲突或者 pr 信息混乱。关于合并,下文会有更详细的解释。

成员分别提 pr 进行开发

那么根据以上的模式,开发的流程就是开发人员会从主分支中新建分支进行开发,开发过程中的 commit 又开发人员自主规划,但是有一个良好的提交习惯和遵从团队的约定也是很重要的。开发完成后合并入测试分支进行测试,最后确保功能没有问题过后则可以并入主分支了。这里面提到的合并都是在平台上提出 pull request(pr)实现的,pr 中可以描述本次更新的内容,主分支的 pr 一般由管理者进行复查和合并,这样确保了多人协作的情况下清晰有序的开发,并且通过不同操作的权限设置可以避免成员的一些风险操作。

合并 pr 以及冲突情况

合并方式

有三种处理分支合并的策略,分别是 fast-forward、merge、rebase。merge 和其他两种的方式不同的就是在于会创建一个 merge commit 提交,同时在历史记录上会保留分支的更新记录,这样的好处是可以清晰的知道每个新建出去的分支都进行了什么更改。但是在测试环境下,分支的合并一般都是比较频繁的,如有查阅测试环境历史的需求,可以使用 rebase 方式重写历史,rebase 可以将当前提交移动到制定分支的最新提交上,可以保持线性化的历史。fast-forward merge 则是一种特定情况下默认的合并方式,特定情况指的是提交没有形成分叉,即新建分支之后原分支和新建分支只能有一个分支有新提交,这样 fast-forword merge 就能保持线性的历史记录。不过如果为了保持分支的存在和合并行为的可查,一般都会开启 no-ff 模式,来阻止默认进行 fast-forward 合并。

总结

merge:创建一个 merge commit,保留分叉的历史记录。

fast-forward:只在分支提交无分叉时保持线性的提交记录。

rebase:创建一个提交,重写历史以保持记录整洁。

污染分支的情况描述,引出问题

冲突的情况下直接在 github 上处理,使测试分支 merge 进开发分支,开发分支会携带测试分支的内容,在并入主分支时会导致一些仍然还在测试的内容被连带上线。查看 github 的文档,在 github 上合并是会将基础分支都合并到头部分支(比较分支),所以这就会导致开发分支被污染的情况发生。

处理方式

1.从自己的开发分支上新建一个分支解决冲突

2.merge 到自己分支上本地解决冲突

3.在自己的分支上修改内容防止冲突发生

上文提到污染的情况根本原因是 github 上合并方向问题,所以有这么三种方式。

第一种方式和第二种方式思路是一样的,就是不让目标分支影响开发分支的内容,第一种方式比较复杂,不过可以直接使用 github 上的编辑器进行合并。

第二种就是一般解决冲突比较常用的方式,直接在本地将开发分支合并到目标分支(与 github 的 pr 合并方向相反),然后直接将 merge 提交同步到线上,完成合并,这样就不会将目标分支的内容带到开发分支。

最后一种适用于冲突比较小的情况,例如冲突的内容只是不影响代码执行结果的前后位置差异,则可以在自己的开发分支上将位置差异给消除,这样就能消除冲突,直接在 github 上的 pr 合并了。

探究与总结

构建一个 git 的思维模型

如果仅仅以使用 git 这个角度来讲,其实实践经验的重要性会大于理论知识,但是使用 git 与他人进行合作,又不能总是因为自己的错误操作而连累整个项目。所以在进行实践前,构建一套自己能解释的思维模型,后续再通过实践经验去修正和补充会让这个学习过程变得更加有效。

以我的经验举例,通过我一开始模型去解释,commit 提交的作用是标记一个功能点的开发,可以在时间线上清晰的看见开发的流程,而 branch 分支则是标记比较大的实验性功能开发,可以灵活切换到试验版本和稳定版本,这时候的理解还是偏向个人开发多一点。当真正进行正式的多人开发,遇到测试分支和线上主分支,并且成员各自新建开发分支进行提交最后 pr 合并的模式时,我对 branch 分支的理解就更为全面了,branch 分支可以有 feature 开发、fix 修复问题、beta 分支测试、main 分支上线多种功能。同时因为分支使用的频率增多,我又新增了对合并以及对冲突的概念。合并是对测试或者线上分支应用开发分支修改的一种方式,冲突则是不同开发分支对同一处进行了不同的修改,git 无法判定使用哪个修改,就会出现冲突。