Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git的分支管理策略 #19

Open
YunaQiu opened this issue Apr 20, 2016 · 8 comments
Open

git的分支管理策略 #19

YunaQiu opened this issue Apr 20, 2016 · 8 comments

Comments

@YunaQiu
Copy link
Contributor

YunaQiu commented Apr 20, 2016

关于git的正确打开方式

马上部门就要开始毕业季的项目开发啦~这也是大多数15级的小朋友第一次正式参与开发的部门项目。经过一学期的学习,相信大家对于git的使用已经不太陌生,但不知道大家对于git的分支管理是否也有足够的了解呢?
分支管理是git作为版本控制工具最强大的优势之一。怎么协调管理团队开发中每个人的开发工作,怎么管理一个项目各个阶段的版本迭代,这与这个团队是否懂得合理运用分支进行项目管理息息相关。相信准备参与毕业季开发的小朋友最近都已经加入到部门的gitlab中了,大家也可以走走看看以前的项目都是怎么进行版本管理的。

废话说完了,下面是正题:

首先我们先来看看一张比较典型的git分支管理图

是不是感觉看得有点晕?其实在一般的需要进行版本迭代的项目中,日常的分支只需要masterdevelop两条就够了,其他都是临时性的分支。如果是仅使用一段时间的比较小的短期项目(比如治愈系、毕业季等),那可能就连dev分支都没必要了。但像波板糖这种需要长期维护的项目,dev分支还是必须的。
下面我们一条条来讲各个分支的功能。

一、主分支master

这个分支大家一点都不陌生了,初建立版本仓库时默认新建的就是这个分支。
主分支也就是发布分支。无论何时,主分支都是必须的。而且绝大多数时候,一个项目的主分支都只会有一个。这条分支上,存放的是所有给用户正式使用的发布版本。对于需要不断迭代更新版本的项目,我们都会习惯给主分支上的每个版本设立一个tag,用来标注它的版本号,方便进行版本的回退操作。

二、开发分支develop

主分支是用来发布正式版本的,而日常的开发则是发布在develop分支上(有时我们也会简写成dev分支)。当一个版本开发完毕,打算正式发布时,我们就会将该分支上的版本merge到master分支上。但有时开发完成后我们并不会直接发布,而是还要再经过一个预发布的测试过程,这种情况后文再提。

在这里再重提下合并分支时no fastforward的问题。不知道大家是否还记得上学期柏晓培训git时提到的--no-ff参数?默认情况下的git merge dev是快进式合并,合并后master分支会直接指向dev分支。为了保证版本演进的清晰,强烈建议采用git merge --no-ff dev的方式。下面两张图对比下:

三、预发布分支release

前面提到了有时候开发完一个版本后,我们不会立马上线,而是会先测试一段时间。这时候我们会选择新建一个临时的release分支,专门用来在版本测试中对代码进行修复和完善。待版本测试完毕后,再将可以发布的版本分别合并到master分支和develop分支上,最后再根据需要删除该临时分支。预发布分支可以采用“release-版本号”的方式命名。至于图片我就不放了,大家参考最上面的那张大图。

四、修复分支hotfixes

有时候bug并不一定出现在开发过程中,也可能是已发布的版本中发现有bug。此时在dev分支正在开发新版本的情况下,我们并不方便将bug放到下一个新版本中再修复。这时就需要从master分支上新建一个临时分支,用来针对已发布的版本进行bug的修复。同样,修复完成后的版本要分别合并到master分支和dev分支上。

五、功能分支feature

这其实是一个比较少用到的分支。曾经在部门的某次项目开发中,因为项目中包含好几个功能模块,开发组成员的工作又是按功能模块划分的,于是gitlab的分支上一下出现了五六个以功能命名的分支。更崩溃的是,这个开发组其实只有3个人。对于这种想撞墙的分支管理方式,我给出的回应是:“来,我们来聊聊人生。”
功能分支。顾名思义,是为了开发某种特定功能而从dev上延伸出来的临时分支。但是请注意:这里的功能并不是在版本规划内的功能。换句话说,只是这个项目有开发这个功能的需要,但它并不在版本计划内,它有可能会在下次版本发布,也有可能在下下次版本中发布,甚至也可能在开发到一半时被cancel掉了。这取决于这个功能的开发进度和功能设计的完善程度,只有当这个功能稳定下来,被考虑进新一个版本的计划中时,它才会被合并回dev分支,并最终发布到master上。


五种分支介绍完啦~现在大家是不是觉得清晰了很多?
但是实际开发中还是有很多各种各样的情况,下面我针对有可能碰到的问题进行一个探讨。当然这些观点更多是从我的经验角度看的,而且大多是来源于网页开发的经验。如果其他老人们有其他建议或者不一样的观点,欢迎跟帖留言。或者是有自己在开发中遇到的问题也可以发上来一起讨论下。

  1. 是否需要针对每个开发成员建立一条分支?
    个人觉得,如果是担心dev分支太过混乱的话,可以在自己的本地仓库上给自己另外开一条开发分支,但是没必要将个人分支push到远程仓库上。我个人的观点是远程仓库的分支应该保持足够的简洁,尽量只保留有必要的分支。所以在自己开发的时候,如果想保持dev分支的简洁,可以在本地个人分支上开发,然后在合并到dev分支上,最后只将dev分支push到远程上即可。
  2. 如何判断两个分支应该谁合并谁?
    这个问题通常只存在在对分支管理还不熟练的情况下。实际上从上面的讲解中大家应该能看出来,分支是有从属关系的。例如master分支永远都是最高层的分支,别人只会从它身上延伸出新分支,或者合并到master上,但绝不会有master分支合并到其他分支的情况。同样的,dev分支延伸自master分支,通常也只合并到master分支。剩下的临时分支则是延伸自dev或master,合并也都是合并回dev或master。所以其实他们是层次分明的,并不存在分不清谁合并谁的情况。
  3. 应该多久向远程仓库push一次?
    这里只提供一个参考。通常完成一个小的功能点会commit一次版本,一般积累三四个版本,或者一段连续开发告一段落后就会像仓库push一次。比如今天连续开发了两三个小时,打算明天或者改天再接着干的时候,就push到远程。不建议隔太久或者堆积了太多版本才push,因为这样跟远程分支合并和解决冲突的成本会比较高。
  4. 如果需要临时发布一个定制版的系统,该怎么办?
    这个问题曾经出现在投票系统上。当时因为光音派对需要在投票结束后,跳转到抽奖页面,而需要临时开发一个特别版的投票系统。我们当时的处理方法是从master的稳定版本上延伸出一个定制版分支,再那个分支上进行开发定制的版本。待光音投票结束后,再切换master分支。原有的定制版分支则一直保留着,方便以后有需要时可以查看。
  5. 如果需要开发的系统本身就有两个不同的、需要独立开发的版本线怎么办?
    个人觉得分情况对待吧。如果这两条版本线是有部分代码重合的,仅仅是部分实现细节不一样,那么可以放在一个仓库中。每条版本线各自开主分支和开发分支。但是如果这两个版本的代码基本不相干(比如波板糖的安卓版和ios版),那自然是分成两个版本仓库进行管理比较好。
  6. 通常在一天或者一个时间段的开发中,开发流程是怎么走的?
    先pull最新版本到本地,然后在本地进行开发若干版本,开发完成后再push上远程。如果出现了冲突,则将远程的版本pull到本地,解决了冲突后再push上远程。
@Zhangjd
Copy link
Member

Zhangjd commented Apr 20, 2016

上面提到的是 Git-flow 工作流, 虽然经典但是比较繁琐, 我个人倒是觉得在部门两三个人开发, 功能不多, 分工明确的情景下, 用 Fork Workflow 或者 Centralized Workflow (跟 svn 差不多) 更合适. 参见 大话 Git 工作流.

@YunaQiu
Copy link
Contributor Author

YunaQiu commented Apr 20, 2016

确实,部门开发中通常要不就一条master走到底,要不就是master+develop,其实基本满足需要了。毕竟并没有那么庞大的开发团队需要管理

@bilong
Copy link

bilong commented Apr 20, 2016

@tugui
Copy link
Contributor

tugui commented Apr 23, 2016

对于解决冲突,最好的办法就是:尽量减少多个人同时修改一个文件的可能性。因为conflict是以文件为单位的,所以在开发的准备阶段,就应该确定好分工,同时尽量使功能模块化,这样即使大家的commit差十几个版本,也不会有太多的问题;另外一点就是及时push到远程仓库,保持自己的commit与远程对应分支的距离。

@hongruiqi
Copy link

关于merge顺序的问题

分支是有上下游关系的,这种关系也决定了分支合并的方向,即下游分支合并到上游分支,如dev合并到master。单机的情况下,这种模式很清晰。但涉及与中心仓库协作的时候,会遇到中心仓库和本地同名的分支,记中心仓库分支为master,本地分支为qi/master。这两个分支同样具有上下游的关系,即qi/master应该是master的下游分支。合并的时候,qi/master应该merge到master。

我们在学习git的时候,都见过一个常用的指令,git pull。而这个指令实际上是违背这个原则的。git pull所做的分为两个步骤,一是将远程的master fetch到本地,二是将远程的master merge到本地的qi/master。顺序正好相反。

所以,如果要严格遵守merge的顺序,方法应该是这样的:
1 本地的master分支与远程的master保持一致。
2 另外建立一个本地分支local,所有本地提交在此分支进行。
3 要向远程push前,切换回本地master,执行git pull,此时一定是fast forward的。然后在本地master上执行git merge local。
4 尝试git push。如果失败,基于本地master建立新分支m。reset本地master至merge前版本,重新执行git pull,git merge m。

这样可以保证合并时上游分支在下游分支之前,可以形成更清晰的历史。git也提供了参数first parent,用来过滤查看上游分支上的提交。

这种方式的缺点是要求团队成员都遵守同样的规则。只要有一次违规,就没有效果了。

@hongruiqi
Copy link

关于bug修复

bug修复建议的流程如下:
1 找到引入bug的提交
2 在此提交上建立新分支,在此分支上修bug
3 修复bug后,将新分支merge到所有受bug影响的分支。

优点
1 方便追踪bug的引入时间,确定影响范围
2 其它分支只需merge与bug修复相关的变更,不会引入无关提交,避免对其它分支的功能开发造成干扰。

@tugui
Copy link
Contributor

tugui commented May 18, 2016

为了保持远程仓库的记录整洁,可以通过使用git rebase来将分支线性化。
具体的可以参考团队开发里频繁使用 git rebase 来保持树的整洁好吗?这一篇文章;

另外提一个开发过程中出现的问题:改了开头和结尾的文件,在合并其它人的提交的时候,被认为是整个文件出现冲突,所以会保留两份几乎同样的代码记录……怎么解决?

@tugui
Copy link
Contributor

tugui commented May 18, 2016

在把服务器校巴接口的代码push到gitlab的时候,出现了一个有趣的情况:本地运行git status命令时提示HEAD detached at *** ,git push之后远程仓库的master分支版本与本地的版本不一致,但是从提交记录来看是线性的关系。解决方法我参考了git detached,也就是将“匿名提交记录”保存到一个新的分支,然后再与master分支进行merge操作。

@YunaQiu YunaQiu closed this as completed May 18, 2016
@YunaQiu YunaQiu reopened this May 18, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants