Git工作流

版本控制

概述

在软件开发过程,每天都会产生新的代码,代码合并的过程中可能会出现如下问题:

  • 代码被覆盖或丢失
  • 代码写的不理想希望还原之前的版本
  • 希望知道与之前版本的差别
  • 是谁修改了代码以及为什么修改
  • 发版时希望分成不同的版本(测试版、发行版等)

因此,我们希望有一种机制,能够帮助我们:

  • 可以随时回滚到之前的版本
  • 协同开发时不会覆盖别人的代码
  • 留下修改记录,以便随时查看
  • 发版时可以方便的管理不同的版本

什么是版本控制系统

一个标准的版本控制系统 Version Control System (VCS),通常需要有以下功能:

  • 能够创建 Repository (仓库),用来保存代码
  • 协同开发时方便将代码分发给团队成员
  • 记录每次修改代码的内容、时间、原因等信息
  • 能够创建 Branch (分支),可以根据不同的场景进行开发
  • 能够创建 Tag (标签),建立项目里程碑

版本控制系统的发展史

版本控制系统发展至今有几种不同的模式:

  • Local VCS
    本地使用 复制/粘贴 的方式进行管理,缺点是无法协同开发

  • Centralized VCS (Lock,悲观锁)
    中央集中式版本控制系统(代表为SVN)团队共用仓库,当某人需要编辑文件时,进行锁定,以免其他人同时编辑时造成冲突。缺点是虽然避免了冲突,但不是很方便。其他人需要排队才能编辑文件,如果有人编辑了很久或是忘记解锁就会造成其他人长时间等待的情况。
    总有刁民想害朕

  • Centralized VCS (Merge,乐观锁)
    中央集中式版本控制系统团队共用仓库,不采用悲观锁方式来避免冲突,而是事后发现如果别人也修改相同文件(冲突),再进行手动修改解决。有很多 VCS 属于这种类型,如:CVS,Subversion,Perforce 等
    中央集中式版本控制系统的共同问题是,做任何操作都需要和服务器同步,如果服务器宕机则会造成无法继续工作的窘迫。
    天网恢恢,疏而不漏

  • Distributed VCS
    分布式版本控制系统(代表为Git),本地也拥有完整的代码仓库,就不会出现上述集中式管理的问题,即使没有网络,依然可以 commit 和看 log,也无需担心服务器同步问题。如:Git,Mercurial,Bazaar 等就属于分布式版本控制系统。缺点是功能比较复杂,上手需要一定的学习时间。

什么是Git

  • Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
  • Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
  • Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。

Git工作流简介

概述

工作流有各式各样的用法,但也正因此使得在实际工作中如何上手使用增加了难度。这篇指南通过总览公司团队中最常用的几种 Git 工作流让大家可以上手使用。

在阅读的过程中请记住,本文中的几种工作流是作为方案指导而不是条例规定。在展示了各种工作流可能的用法后,你可以从不同的工作流中挑选或揉合出一个满足你自己需求的工作流。

集中式工作流

如果你的开发团队成员已经很熟悉 Subversion,集中式工作流让你无需去适应一个全新流程就可以体验 Git 带来的收益。这个工作流也可以作为向更 Git 风格工作流迁移的友好过渡。

功能分支工作流

功能分支工作流以集中式工作流为基础,不同的是为各个新功能分配一个专门的分支来开发。这样可以在把新功能集成到正式项目前,用 Pull Requests 的方式讨论变更。

GitFlow 工作流

GitFlow 工作流通过为功能开发、发布准备和维护分配独立的分支,让发布迭代过程更流畅。严格的分支模型也为大型项目提供了一些非常必要的结构。(常用,较为繁琐,适当简化)

Forking 工作流

Forking 工作流是分布式工作流,充分利用了 Git 在分支和克隆上的优势。可以安全可靠地管理大团队的开发者(developer),并能接受不信任贡献者(contributor)的提交。(大团队,如跨国)

Pull Requests(请求合并)

Pull requests 让开发者更方便地进行协作的功能,提供了友好的 Web 界面可以在提议的修改合并到正式项目之前对修改进行讨论。

集中式工作流

概述

转到分布式版本控制系统看起来像个令人生畏的任务,但不改变已用的工作流你也可以用上 Git 带来的收益。团队可以用和 Subversion 完全不变的方式来开发项目。

但使用 Git 加强开发的工作流,Git 比 SVN 有几个优势。首先,每个开发可以有属于自己的整个工程的本地拷贝。隔离的环境让各个开发者的工作和项目的其他部分(修改)独立开来 —— 即自由地提交到自己的本地仓库,先完全忽略上游的开发,直到方便的时候再把修改反馈上去。

其次,Git 提供了强壮的分支和合并模型。不像 SVN,Git 的分支设计成可以做为一种用来在仓库之间集成代码和分享修改的『失败安全』的机制。

工作方式

像 Subversion 一样,集中式工作流以中央仓库作为项目所有修改的单点实体。相比 SVN 缺省的开发分支 trunk,Git 叫做 master,所有修改提交到这个分支上。该工作流只用到 master 这一个分支。

开发者开始先克隆中央仓库。在自己的项目拷贝中,像 SVN 一样的编辑文件和提交修改;但修改是存在本地的,和中央仓库是完全隔离的。开发者可以把和上游的同步延后到一个方便时间点。

要发布修改到正式项目中,开发者要把本地 master 分支的修改『推(push)』到中央仓库中。这相当于 svn commit 操作,但 push 操作会把所有还不在中央仓库的本地提交都推上去。

解决冲突

中央仓库代表了正式项目,所以提交历史应该被尊重且是稳定不变的。如果开发者本地的提交历史和中央仓库有分歧,Git 会拒绝 push 提交否则会覆盖已经在中央库的正式提交。

在开发者提交自己功能修改到中央库前,需要先 fetch 在中央库的新增提交,rebase 自己提交到中央库提交历史之上。这样做的意思是在说,『我要把自己的修改加到别人已经完成的修改上。』最终的结果是一个完美的线性历史,就像以前的SVN 的工作流中一样。

如果本地修改和上游提交有冲突,Git 会暂停 rebase 过程,给你手动解决冲突的机会。Git 解决合并冲突,用和生成提交一样的 git status 和 git add 命令,很一致方便。还有一点,如果解决冲突时遇到麻烦,Git 可以很简单中止整个 rebase 操作,重来一次(或者让别人来帮助解决)。

测试

  1. 有人先初始化好中央仓库

第一步,有人在服务器上创建好中央仓库。如果是新项目,你可以初始化一个空仓库;否则你要导入已有的 Git 或 SVN 仓库。

中央仓库应该是个裸仓库(bare repository),即没有工作目录(working directory)的仓库。

  1. 所有人克隆中央仓库

下一步,各个开发者创建整个项目的本地拷贝。通过 git clone 命令完成:

基于你后续会持续和克隆的仓库做交互的假设,克隆仓库时 Git 会自动添加远程别名 origin 指回『父』仓库。

  1. 小明开发功能

在小明的本地仓库中,他使用标准的 Git 过程开发功能:编辑、暂存(Stage)和提交。如果你不熟悉暂存区(Staging Area),这里说明一下:暂存区的用来准备一个提交,但可以不用把工作目录中所有的修改内容都包含进来。这样你可以创建一个高度聚焦的提交,尽管你本地修改很多内容。

git status # 查看本地仓库的修改状态
git add # 暂存文件
git commit # 提交文件

请记住,因为这些命令生成的是本地提交,小明可以按自己需求反复操作多次,而不用担心中央仓库上有了什么操作。对需要多个更简单更原子分块的大功能,这个做法是很有用的

  1. 小红开发功能

与此同时,小红在自己的本地仓库中用相同的编辑、暂存和提交过程开发功能。和小明一样,她也不关心中央仓库有没有新提交;当然更不关心小明在他的本地仓库中的操作,因为所有本地仓库都是私有的

  1. 小明发布功能

一旦小明完成了他的功能开发,会发布他的本地提交到中央仓库中,这样其它团队成员可以看到他的修改。他可以用下面的 git push 命令:

git push origin master

注意,origin 是在小明克隆仓库时 Git 创建的远程中央仓库别名。master 参数告诉 Git 推送的分支。由于中央仓库自从小明克隆以来还没有被更新过,所以 push 操作不会有冲突,成功完成

  1. 小红试着发布功能

一起来看看在小明发布修改后,小红 push 修改会怎么样?她使用完全一样的 push 命令:

git push origin master

但她的本地历史已经和中央仓库有分岐了,Git 拒绝操作并给出下面很长的出错消息:

这避免了小红覆写正式的提交。她要先 pull 小明的更新到她的本地仓库合并上她的本地修改后,再重试。

  1. 小红在小明的提交之上 rebase

小红用 git pull 合并上游的修改到自己的仓库中。这条命令类似 svn update ——拉取所有上游提交命令到小红的本地仓库,并尝试和她的本地修改合并:

git pull --rebase origin master

--rebase 选项告诉 Git 把小红的提交移到同步了中央仓库修改后的 master 分支的顶部,如下图所示:

如果你忘加了这个选项,pull 操作仍然可以完成,但每次 pull 操作要同步中央仓库中别人修改时,提交历史会以一个多余的『合并提交』结尾。对于集中式工作流,最好是使用 rebase 而不是生成一个合并提交

  1. 小红解决合并冲突

rebase 操作过程是把本地提交一次一个地迁移到更新了的中央仓库 master 分支之上。这意味着可能要解决在迁移某个提交时出现的合并冲突,而不是解决包含了所有提交的大型合并时所出现的冲突。这样的方式让你尽可能保持每个提交的聚焦和项目历史的整洁。反过来,简化了哪里引入 Bug 的分析,如果有必要,回滚修改也可以做到对项目影响最小。

如果小红和小明的功能是相关的,不大可能在 rebase 过程中有冲突。如果有,Git 在合并有冲突的提交处暂停 rebase 过程,输出下面的信息并带上相关的指令:

CONFLICT (content): Merge conflict in

Git 很赞的一点是,任何人可以解决他自己的冲突。在这个例子中,小红可以简单的运行 git status 命令来查看哪里有问题。冲突文件列在 Unmerged paths(未合并路径)一节中:

接着小红编辑这些文件。修改完成后,用老套路暂存这些文件,并让 git rebase 完成剩下的事:

git add
git rebase --continue

要做的就这些了。Git 会继续一个一个地合并后面的提交,如其它的提交有冲突就重复这个过程。

如果你碰到了冲突,但发现搞不定,不要惊慌。只要执行下面这条命令,就可以回到你执行 git pull --rebase 命令前的样子:

git rebase --abort
  1. 小红成功发布功能

小红完成和中央仓库的同步后,就能成功发布她的修改了:

git push origin master

总结

仅使用几个 Git 命令我们就可以模拟出传统 Subversion 开发环境。对于要从 SVN 迁移过来的团队来说这太好了,但没有发挥出 Git 分布式本质的优势。

如果你的团队适应了集中式工作流,但想要更流畅的协作效果,绝对值得探索一下功能分支工作流的收益。通过为一个功能分配一个专门的分支,能够做到一个新增功能集成到正式项目之前对新功能进行深入讨论。

适用于小型团队

add:放到暂存
commit:把暂存区所有一次性提交到分支

功能分支工作流

概述

一旦你玩转了集中式工作流,在开发过程中可以很简单地加上功能分支,用来鼓励开发者之间协作和简化交流。

功能分支工作流背后的核心思路是所有的功能开发应该在一个专门的分支,而不是在 master 分支上。这个隔离可以方便多个开发者在各自的功能上开发而不会弄乱主干代码。另外,也保证了 master 分支的代码一定不会是有问题的,极大有利于集成环境。

功能开发隔离也让 pull requests 工作流成功可能,pull requests 工作流能为每个分支发起一个讨论,在分支合入正式项目之前,给其它开发者有表示赞同的机会。另外,如果你在功能开发中有问题卡住了,可以开一个 pull requests 来向同学们征求建议。这些做法的重点就是,pull requests 让团队成员之间互相评论工作变成非常方便

工作方式

功能分支工作流仍然用中央仓库,并且 master 分支还是代表了正式项目的历史。但不是直接提交本地历史到各自的本地 master 分支,开发者每次在开始新功能前先创建一个新分支。功能分支应该有个有描述性的名字,比如 animated-menu-items 或 issue-#1061,这样可以让分支有个清楚且高聚焦的用途。

在 master 分支和功能分支之间,Git 是没有技术上的区别,所以开发者可以用和集中式工作流中完全一样的方式编辑、暂存和提交修改到功能分支上。

另外,功能分支也可以(且应该)push 到中央仓库中。这样不修改正式代码就可以和其它开发者分享提交的功能。由于 master 仅有的一个『特殊』分支,在中央仓库上存多个功能分支不会有任何问题。当然,这样做也可以很方便地备份各自的本地提交

Pull Requests

功能分支除了可以隔离功能的开发,也使得通过 Pull Requests 讨论变更成为可能。一旦某个开发完成一个功能,不是立即合并到 master,而是 push 到中央仓库的功能分支上并发起一个 Pull Request 请求去合并修改到 master。在修改成为主干代码前,这让其它的开发者有机会先去 Review 变更。

Code Review 是 Pull Requests 的一个重要的收益,但 Pull Requests 目的是讨论代码一个通用方式。你可以把 Pull Requests 作为专门给某个分支的讨论。这意味着可以在更早的开发过程中就可以进行 Code Review。比如,一个开发者开发功能需要帮助时,要做的就是发起一个 Pull Request,相关的人就会自动收到通知,在相关的提交旁边能看到需要帮助解决的问题。

一旦 Pull Request 被接受了,发布功能要做的就和集中式工作流就很像了。首先,确定本地的 master 分支和上游的 master 分支是同步的。然后合并功能分支到本地 master 分支并 push 已经更新的本地 master 分支到中央仓库

示例

下面的示例演示了如何把 Pull Requests 作为 Code Review 的方式,但注意 Pull Requests 可以用于很多其它的目的。

小红开始开发一个新功能

在开始开发功能前,小红需要一个独立的分支。使用下面的命令新建一个分支:

git checkout -b marys-feature master

这个命令检出一个基于 master 名为 marys-feature 的分支,Git 的 -b 选项表示如果分支还不存在则新建分支。这个新分支上,小红按老套路编辑、暂存和提交修改,按需要提交以实现功能:

git status
git add
git commit

添加登陆功能

review代码审核

登陆合并请求

允许合并,还可以评论

此时已经有了功能

在这里看之前的分支合并

将当前分支切换回master,同步代码

删除分支

删除远端分支

适合8-12人团队,每个人一个功能分支,master主干代码不会被污染。

GitFlow工作流

概述

GitFlow 工作流定义了一个围绕项目发布的严格分支模型。虽然比功能分支工作流复杂几分,但提供了用于一个健壮的用于管理大型项目的框架。

GitFlow 工作流没有用超出功能分支工作流的概念和命令,而是为不同的分支分配一个很明确的角色,并定义分支之间如何和什么时候进行交互。除了使用功能分支,在做准备、维护和记录发布也使用各自的分支。当然你可以用上功能分支工作流所有的好处:Pull Requests、隔离实验性开发和更高效的协作

工作方式

GitFlow 工作流仍然用中央仓库作为所有开发者的交互中心。和其它的工作流一样,开发者在本地工作并 push 分支到中央仓库中。

历史分支

相对使用仅有的一个 master 分支,GitFlow 工作流使用2个分支来记录项目的历史。master 分支存储了正式发布的历史,而 develop 分支作为功能的集成分支。这样也方便 master 分支上的所有提交分配一个版本号。

剩下要说明的问题围绕着这2个分支的区别展开

功能分支

每个新功能位于一个自己的分支,这样可以 push 到中央仓库以备份和协作。但功能分支不是从 master 分支上拉出新分支,而是使用 develop 分支作为父分支。当新功能完成时,合并回 develop 分支。新功能提交应该从不直接与 master 分支交互

注意,从各种含义和目的上来看,功能分支加上 develop 分支就是功能分支工作流的用法。但 GitFlow 工作流没有在这里止步

发布分支

一旦 develop 分支上有了做一次发布(或者说快到了既定的发布日)的足够功能,就从 develop 分支上 fork 一个发布分支。新建的分支用于开始发布循环,所以从这个时间点开始之后新的功能不能再加到这个分支上 —— 这个分支只应该做 Bug 修复、文档生成和其它面向发布任务。一旦对外发布的工作都完成了,发布分支合并到 master 分支并分配一个版本号打好 Tag。另外,这些从新建发布分支以来的做的修改要合并回 develop 分支。

使用一个用于发布准备的专门分支,使得一个团队可以在完善当前的发布版本的同时,另一个团队可以继续开发下个版本的功能。这也打造定义良好的开发阶段(比如,可以很轻松地说,『这周我们要做准备发布版本 4.0』,并且在仓库的目录结构中可以实际看到)。

常用的分支约定:

  • 用于新建发布分支的分支: develop
  • 用于合并的分支: master
  • 分支命名: release-* 或 release/*

维护分支

维护分支或说是热修复(hotfix)分支用于生成快速给产品发布版本(production releases)打补丁,这是唯一可以直接从 master 分支 fork 出来的分支。修复完成,修改应该马上合并回 master 分支和 develop 分支(当前的发布分支),master 分支应该用新的版本号打好 Tag。

为 Bug 修复使用专门分支,让团队可以处理掉问题而不用打断其它工作或是等待下一个发布循环。你可以把维护分支想成是一个直接在 master 分支上处理的临时发布

示例

新建分支

使用右键-Git GUI Here

在master下新建分支并推送到远端

拉取远程分支

切换远程分支

机遇develop新建feature_res注册功能分支

使不明白,下载一个小乌龟用了!!!

小红切换分支

推送到远端

这个操作一般为领导、组长完成

小明拉去远端

切换为远程的develop分支

基于develop分支创建分支

切换到注册功能分支

新建文件

提交并推送新功能

此时仓库有三个分支

develop没有小明的注册功能,feature_reg有

小明发送合并请求

一定要选develop

可以确当或者评论修改

合并成功

develop中有了注册功能

开发到一定阶段才会合并到master,一直基于develop开发

小明切换回develop分支开发新版本

拉取,此时显示了注册功能

预发布分支

新建release-1.0.0分支,基于develop

切换分支到1.0.0

这个版本需要测试人员拉下来测试运行起来

一定要在发布之前测试一遍

推送到远端

测试完成没有问题再请求合并

只有release才能合并到master

此时master有了注册功能

这时候已经到了第一个里程碑版本,已经可以给别人用了

小明切换回master分支

拉去代码下来,创建标签

推送,勾选包括标签

仓库中有了标签

正式里程碑版本

点击1release

有bug,提出问题

提交问题

创建hotfix分支

基于标签创建hotfix分支,0004对应问题编号

修改文件小红登陆,重新提交

打开仓库

请求合并

小明切换分支到master,然后拉取,创建标签1.0.1

推送到服务器

1.0.1上来了

此时除了develop和master分支,其余的分支要删掉

远端自己开发的feature分支删除掉,hotfix删掉,预发布删掉

此时GitFlow工作流已经完成了

简化

只留develop和master,不要feature,把develop当成中央集中式开发,有问题develop分支上解决。预发布分支必须有,hotfix也要有。

Forking工作流

Forking 工作流和前面讨论的几种工作流有根本的不同。这种工作流不是使用单个服务端仓库作为『中央』代码基线,而让各个开发者都有一个服务端仓库。这意味着各个代码贡献者有 2 个 Git 仓库而不是 1 个:一个本地私有的,另一个服务端公开的。

跨国项目等,比如vue

fork是复制它的仓库到我的仓库

此时修改提交到我得仓库,请求合并发给作者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335