深入理解学习Git工作流

Posted zhchoutai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解学习Git工作流相关的知识,希望对你有一定的参考价值。

文/ixirong(简书作者)
原文链接:http://www.jianshu.com/p/91acec85c3a4
著作权归作者全部。转载请联系作者获得授权,并标注“简书作者”。

  • 我们以使用SVN的工作流来使用git有什么不妥?
  • git 方便的branch在哪里,团队多人怎样协作?冲突了怎么办?怎样进行公布控制?
  • 经典的master-公布、develop-主开发、hotfix-只是修复怎样避免代码不经过验证上线?
  • 怎样在github上面与他人一起协作,star-fork-pull request是怎样的流程?

我个人非常感激这篇文章,所以进行了整理,希望能帮到很多其它的人。

整篇文章由 xirong 整理自 oldratlee 的github。方便统一的学习回想。在此感谢以下两位的贡献。

原文链接:Git Workflows and Tutorials
中文简体:由 oldratlee 翻译在 github 上 git-workflows-and-tutorials


一、译序

工作流事实上不是一个0基础主题,背后的本质问题事实上是有效的项目流程管理和高效的开发协同约定,不仅是GitSVNVCSSCM工具的使用。

这篇指南以大家在SVN中已经广为熟悉使用的集中式工作流作为起点,循序渐进地演进到其他高效的分布式工作流。还介绍了怎样配合使用便利的Pull Request功能,体系地解说了各种工作流的应用。

行文中实践原则和操作演示样例并重。对于Git的资深玩家能够梳理思考提升,而新接触的同学。也能够跟着step-by-step操作来操练学习并在实际工作中上手使用。

关于Git工作流主题,网上体系的中文资料不多。主要是零散的操作说明。希望这篇文章能让你更深入理解并在工作中灵活有效地使用起来。

PS

文中Pull Request的介绍用的是Bitbucket代码托管服务,因为和GitHub基本一样,假设你用的是GitHub(我自己也主要使用GitHub托管代码),不影响理解和操作。

PPS

本指南循序渐进地解说工作流。假设Git用的不多,能够从前面的讲的工作流開始操练。操作过程去感受指南的解说:解决什么问题、怎样解决这个问题,这样理解就深了,也方便活用。

Gitflow工作流是经典模型,体现了工作流的经验和精髓。随着项目过程复杂化。会感受到这个工作流中深思熟虑和威力!

Forking工作流是协作的(GitHub风格)能够先看看Github的Help:Fork A RepoUsing pull requests 。照着操作,给一个Github项目贡献你的提交,有操作经验再看指南easy意会。指南中给了自己实现Fork的方法Fork就是服务端的克隆。在指南的操练中使用代码托管服务(如GitHubBitbucket),能够点一下button就让开发人员完毕仓库的fork操作。

:see_no_evil: 自己理解粗浅。翻译中不足和不正确之处,欢迎建议(提交Issue)和指正(Fork后提交代码)!

二、Git工作流指南

:point_right: 工作流有各式各样的使用方法。但也正因此使得在实际工作中怎样上手使用变得非常头大。这篇指南通过总览公司团队中最经常使用的几种Git工作流让大家能够上手使用。

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

技术分享
Git Workflows

2.1 集中式工作流

假设你的开发团队成员已经非常熟悉Subversion,集中式工作流让你无需去适应一个全新流程就能够体验Git带来的收益。

这个工作流也能够作为向更Git风格工作流迁移的友好过渡。

技术分享
Git Workflows: SVN-style

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

但使用Git加强开发的工作流,Git有相比SVN的几个优势。


首先,每一个开发能够有属于自己的整个project的本地拷贝。隔离的环境让各个开发人员的工作和项目的其它部分改动独立开来 ——
即自由地提交到自己的本地仓库,先全然忽略上游的开发,直到方便的时候再把改动反馈上去。

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

2.1.1 工作方式

Subversion一样。集中式工作流以中央仓库作为项目全部改动的单点实体。

相比SVN缺省的开发分支trunkGit叫做master。全部改动提交到这个分支上。

本工作流仅仅用到master这一个分支。

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

要公布改动到正式项目中。开发人员要把本地master分支的改动『推』到中央仓库中。这相当于svn commit操作。但push操作会把全部还不在中央仓库的本地提交都推上去。

技术分享
git-workflow-svn-push-local

2.1.2 冲突解决

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

技术分享
git-workflow-svn-managingconflicts

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

假设本地改动和上游提交有冲突,Git会暂停rebase过程。给你手动解决冲突的机会。

Git解决合并冲突,用和生成提交一样的git statusgit add命令,非常一致方便。另一点,假设解决冲突时遇到麻烦,Git能够非常easy中止整个rebase操作,重来一次(或者让别人来帮助解决)。

2.1.3 演示样例

让我们一起逐步分解来看看一个常见的小团队怎样用这个工作流来协作的。有两个开发人员小明和小红,看他们是怎样开发自己的功能并提交到中央仓库上的。

有人先初始化好中央仓库

技术分享

第一步。有人在server上创建好中央仓库。假设是新项目,你能够初始化一个空仓库;否则你要导入已有的GitSVN仓库。

中央仓库应该是个裸仓库(bare repository)。即没有工作文件夹(working directory)的仓库。

能够用以下的命令创建:

ssh [email protected]
git init --bare /path/to/repo.git

确保写上有效的userSSH的username),host(server的域名或IP地址),/path/to/repo.git(你想存放仓库的位置)。
注意,为了表示是一个裸仓库,依照约定加上.git扩展名到仓库名上。

全部人克隆中央仓库

技术分享

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

git clone ssh://[email protected]/path/to/repo.git

基于你兴许会持续和克隆的仓库做交互的如果。克隆仓库时Git会自己主动加入远程别名origin指回『父』仓库。

小明开发功能

技术分享

在小明的本地仓库中,他使用标准的Git过程开发功能:编辑、暂存(Stage)和提交。
假设你不熟悉暂存区(Staging Area)。这里说明一下:暂存区的用来准备一个提交,但能够不用把工作文件夹中全部的改动内容都包括进来。
这样你能够创建一个高度聚焦的提交,虽然你本地改动非常多内容。

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

请记住,由于这些命令生成的是本地提交。小明能够按自己需求重复操作多次。而不用操心中央仓库上有了什么操作。
对须要多个更简单更原子分块的大功能,这个做法是非常实用的。

小红开发功能

技术分享

与此同一时候。小红在自己的本地仓库中用同样的编辑、暂存和提交过程开发功能。

和小明一样。她也不关心中央仓库有没有新提交;
当然更不关心小明在他的本地仓库中的操作。由于全部本地仓库都是私有的。

小明公布功能

技术分享

一旦小明完毕了他的功能开发,会公布他的本地提交到中央仓库中,这样其他团队成员能够看到他的改动。

他能够用以下的git push命令

git push origin master

注意,origin是在小明克隆仓库时Git创建的远程中央仓库别名。master參数告诉Git推送的分支。


因为中央仓库自从小明克隆以来还没有被更新过。所以push操作不会有冲突。成功完毕。

小红试着公布功能

技术分享

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

git push origin master

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

error: failed to push some refs to ‘/path/to/repo.git‘
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. ‘git pull‘)
hint: before pushing again.
hint: See the ‘Note about fast-forwards‘ in ‘git push --help‘ for details.

这避免了小红覆写正式的提交。

她要先pull小明的更新到她的本地仓库合并上她的本地改动后。再重试。

小红在小明的提交之上rebase

技术分享

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

git pull --rebase origin master

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

技术分享

假设你忘加了这个选项。pull操作仍然能够完毕,但每次pull操作要同步中央仓库中别人改动时。提交历史会以一个多余的『合并提交』结尾。


对于集中式工作流。最好是使用rebase而不是生成一个合并提交。

小红解决合并冲突

技术分享

rebase操作过程是把本地提交一次一个地迁移到更新了的中央仓库master分支之上。


这意味着可能要解决在迁移某个提交时出现的合并冲突。而不是解决包括了全部提交的大型合并时所出现的冲突。
这种方式让你尽可能保持每一个提交的聚焦和项目历史的整洁。反过来。简化了哪里引入Bug的分析,假设有必要,回滚改动也能够做到对项目影响最小。

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

CONFLICT (content): Merge conflict in <some-file>
技术分享

Git非常赞的一点是,不论什么人能够解决他自己的冲突。

在这个样例中。小红能够简单的执行git status命令来查看哪里有问题。
冲突文件列在Unmerged paths(未合并路径)一节中:

# Unmerged paths:
# (use "git reset HEAD <some-file>..." to unstage)
# (use "git add/rm <some-file>..." as appropriate to mark resolution)
#
# both modified: <some-file>

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

git add <some-file> 
git rebase --continue

要做的就这些了。

Git会继续一个一个地合并后面的提交,如其他的提交有冲突就反复这个过程。

假设你碰到了冲突。但发现搞不定。不要惊慌。仅仅要运行以下这条命令。就能够回到你运行git pull --rebase命令前的样子:

git rebase --abort

小红成功公布功能

技术分享

小红完毕和中央仓库的同步后。就能成功公布她的改动了:

git push origin master

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

假设你的团队适应了集中式工作流。但想要更流畅的协作效果。绝对值得探索一下 功能分支工作流 的收益。


通过为一个功能分配一个专门的分支,可以做到一个新增功能集成到正式项目之前对新功能进行深入讨论。


2.2 功能分支工作流

功能分支工作流以集中式工作流为基础,不同的是为各个新功能分配一个专门的分支来开发。

这样能够在把新功能集成到正式项眼下,用Pull Requests的方式讨论变更。

技术分享
Git Workflows: Feature Branch
技术分享

一旦你玩转了集中式工作流,在开发过程中能够非常easy地加上功能分支,用来鼓舞开发人员之间协作和简化交流。

功能分支工作流背后的核心思路是全部的功能开发应该在一个专门的分支。而不是在master分支上。
这个隔离能够方便多个开发人员在各自的功能上开发而不会弄乱主干代码。


另外,也保证了master分支的代码一定不会是有问题的,极大有利于集成环境。

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

2.2.1 工作方式

功能分支工作流仍然用中央仓库,而且master分支还是代表了正式项目的历史。


但不是直接提交本地历史到各自的本地master分支,开发人员每次在開始新功能前先创建一个新分支。
功能分支应该有个有描写叙述性的名字,比方animated-menu-itemsissue-#1061。这样能够让分支有个清楚且高聚焦的用途。

master分支和功能分支之间,Git是没有技术上的差别。所以开发人员能够用和集中式工作流中全然一样的方式编辑、暂存和提交改动到功能分支上。

另外,功能分支也能够(且应该)push到中央仓库中。这样不改动正式代码就能够和其他开发人员分享提交的功能。
因为master仅有的一个『特殊』分支,在中央仓库上存多个功能分支不会有不论什么问题。当然。这样做也能够非常方便地备份各自的本地提交。

2.2.2 Pull Requests

功能分支除了能够隔离功能的开发,也使得通过Pull Requests讨论变更成为可能。


一旦某个开发完毕一个功能,不是马上合并到master。而是push到中央仓库的功能分支上并发起一个Pull Request请求去合并改动到master
在改动成为主干代码前,这让其他的开发人员有机会先去Review变更。

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

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

仓库管理的产品解决方式像BitbucketStash,能够良好地支持Pull Requests

能够看看StashPull Requests文档

2.2.3 演示样例

以下的演示样例演示了怎样把Pull Requests作为Code Review的方式,但注意Pull Requests能够用于非常多其他的目的。

小红開始开发一个新功能

技术分享

在開始开发功能前。小红须要一个独立的分支。使用以下的命令新建一个分支

git checkout -b marys-feature master

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

git status
git add <some-file>
git commit

小红要去吃个午饭

技术分享

早上小红为新功能加入一些提交。
去吃午饭前,push功能分支到中央仓库是非常好的做法,这样能够方便地备份。假设和其他开发协作,也让他们能够看到小红的提交。

git push -u origin marys-feature

这条命令push marys-feature分支到中央仓库(origin),-u选项设置本地分支去跟踪远程相应的分支。


设置好跟踪的分支后,小红就能够使用git push命令省去指定推送分支的參数。

小红完毕功能开发

技术分享

小红吃完午饭回来,完毕整个功能的开发。在合并到master之前
她发起一个Pull Request让团队的其他人知道功能已经完毕。

但首先,她要确认中央仓库中已经有她近期的提交:

git push

然后,在她的Git GUIclient中发起Pull Request,请求合并marys-featuremaster,团队成员会自己主动收到通知。
Pull Request非常酷的是能够在相关的提交旁边显示评注,所以你能够非常对某个变更集提问。

小黑收到Pull Request

技术分享

小黑收到了Pull Request后会查看marys-feature的改动。决定在合并到正式项眼下是否要做些改动。且通过Pull Request和小红来回地讨论。

小红再做改动

技术分享

要再做改动。小红用和功能第一个迭代全然一样的过程。编辑、暂存、提交并push更新到中央仓库。小红这些活动都会显示在Pull Request上。小黑能够断续做评注。

假设小黑有须要,也能够把marys-feature分支拉到本地,自己来改动,他加的提交也会一样显示在Pull Request上。

小红公布她的功能

技术分享

一旦小黑能够的接受Pull Request,就能够合并功能到稳定项目代码中(能够由小黑或是小红来做这个操作):

git checkout master
git pull
git pull origin marys-feature
git push

不管谁来做合并。首先要检出master分支并确认是它是最新的。然后运行git pull origin marys-feature合并marys-feature分支到和已经和远程一致的本地master分支。


你能够使用简单git merge marys-feature命令,但前面的命令能够保证总是最新的新功能分支。
最后更新的master分支要又一次push回到origin

这个过程经常会生成一个合并提交。有些开发人员喜欢有合并提交,由于它像一个新功能和原来代码基线的连通符。


但假设你偏爱线性的提交历史,能够在运行合并时rebase新功能到master分支的顶部,这样生成一个快进(fast-forward)的合并。

一些GUIclient能够仅仅要点一下『接受』button运行好上面的命令来自己主动化Pull Request接受过程。


假设你的不能这样。至少在功能合并到master分支后能自己主动关闭Pull Request

与此同一时候,小明在做和小红一样的事

当小红和小黑在marys-feature上工作并讨论她的Pull Request的时候。小明在自己的功能分支上做全然一样的事。

通过隔离功能到独立的分支上,每一个人都能够自主的工作,当然必要的时候在开发人员之间分享变更还是比較繁琐的。

到了这里,但愿你发现了功能分支能够非常直接地在 集中式工作流 的仅有的master分支上完毕多功能的开发。


另外。功能分支还使用了Pull Request,使得能够在你的版本号控制GUIclient中讨论某个提交。

功能分支工作流是开发项目异常灵活的方式。问题是,有时候太灵活了。对于大型团队,经常须要给不同分支分配一个更详细的角色。
Gitflow工作流是管理功能开发、公布准备和维护的经常使用模式。


2.3 Gitflow工作流

Gitflow工作流通过为功能开发、公布准备和维护分配独立的分支,让公布迭代过程更流畅。

严格的分支模型也为大型项目提供了一些很必要的结构。

技术分享
Git Workflows: Gitflow Cycle

这节介绍的Gitflow工作流借鉴自在nvieVincent Driessen

Gitflow工作流定义了一个环绕项目公布的严格分支模型。

尽管比功能分支工作流复杂几分,但提供了用于一个健壮的用于管理大型项目的框架。

Gitflow工作流没实用超出功能分支工作流的概念和命令,而是为不同的分支分配一个非常明白的角色,并定义分支之间怎样和什么时候进行交互。
除了使用功能分支,在做准备、维护和记录公布也使用各自的分支。
当然你能够用上功能分支工作流全部的优点:Pull Requests、隔离实验性开发和更高效的协作。

2.3.1 工作方式

Gitflow工作流仍然用中央仓库作为全部开发人员的交互中心。

和其他的工作流一样。开发人员在本地工作并push分支到要中央仓库中。

2.3.2 历史分支

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

技术分享

剩下要说明的问题环绕着这2个分支的差别展开。

2.3.3 功能分支

每一个新功能位于一个自己的分支,这样能够push到中央仓库以备份和协作
但功能分支不是从master分支上拉出新分支,而是使用develop分支作为父分支。

当新功能完毕时。合并回develop分支
新功能提交应该从不直接与master分支交互。

技术分享

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

2.3.4 公布分支

技术分享

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


另外,这些从新建公布分支以来的做的改动要合并回develop分支。

使用一个用于公布准备的专门分支,使得一个团队能够在完好当前的公布版本号的同一时候,还有一个团队能够继续开发下个版本号的功能。
这也打造定义良好的开发阶段(比方,能够非常轻松地说,『这周我们要做准备公布版本号4.0』,而且在仓库的文件夹结构中能够实际看到)。

经常使用的分支约定:

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

2.3.5 维护分支

技术分享

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

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

2.3.6 演示样例

以下的演示样例演示本工作流怎样用于管理单个公布循环。如果你已经创建了一个中央仓库。

创建开发分支

技术分享

第一步为master分支配套一个develop分支。

简单来做能够本地创建一个空的develop分支push到server上:

git branch develop
git push -u origin develop

以后这个分支将会包括了项目的所有历史,而master分支将仅仅包括了部分历史。其他开发人员这时应该克隆中央仓库,建好develop分支的跟踪分支:

git clone ssh://[email protected]/path/to/repo.git
git checkout -b develop origin/develop

如今每一个开发都有了这些历史分支的本地拷贝。

小红和小明開始开发新功能

技术分享

这个演示样例中。小红和小明開始各自的功能开发。他们须要为各自的功能创建对应的分支。新分支不是基于master分支。而是应该基于develop分支

git checkout -b some-feature develop

他们用老套路加入提交到各自功能分支上:编辑、暂存、提交:

git status
git add <some-file>
git commit

小红完毕功能开发

技术分享

加入了提交后,小红认为她的功能OK了。假设团队使用Pull Requests,这时候能够发起一个用于合并到develop分支。
否则她能够直接合并到她本地的develop分支后push到中央仓库:

git pull origin develop
git checkout develop
git merge some-feature
git push
git branch -d some-feature

第一条命令在合并功能前确保develop分支是最新的。注意。功能决不应该直接合并到master分支。


冲突解决方法和集中式工作流一样。

小红開始准备公布

技术分享

这个时候小明正在实现他的功能,小红開始准备她的第一个项目正式公布。
像功能开发一样。她用一个新的分支来做公布准备。这一步也确定了公布的版本:

git checkout -b release-0.1 develop

这个分支是清理公布、运行全部測试、更新文档和其他为下个公布做准备操作的地方。像是一个专门用于改善公布的功能分支。

仅仅要小红创建这个分支并push到中央仓库,这个公布就是功能冻结的。

不论什么不在develop分支中的新功能都推到下个公布循环中。

小红完毕公布

技术分享

一旦准备好了对外公布,小红合并改动到master分支和develop分支上。删除公布分支。合并回develop分支非常重要,由于在公布分支中已经提交的更新须要在后面的新功能中也要是可用的。
另外,假设小红的团队要求Code Review。这是一个发起Pull Request的理想时机。

git checkout master
git merge release-0.1
git push
git checkout develop
git merge release-0.1
git push
git branch -d release-0.1

公布分支是作为功能开发(develop分支)和对外公布(master分支)间的缓冲。仅仅要有合并到master分支,就应该打好Tag以方便跟踪。

git tag -a 0.1 -m "Initial public release" master
git push --tags

Git有提供各种勾子(hook),即仓库有事件发生时触发运行的脚本。
能够配置一个勾子。在你push中央仓库的master分支时,自己主动构建好对外公布。

终于用户发现Bug

技术分享

对外公布后,小红回去和小明一起做下个公布的新功能开发,直到有终于用户开了一个Ticket抱怨当前版本号的一个Bug


为了处理Bug,小红(或小明)从master分支上拉出了一个维护分支,提交改动以解决这个问题,然后直接合并回master分支:

git checkout -b issue-#001 master
# Fix the bug
git checkout master
git merge issue-#001
git push

就像公布分支,维护分支中新加这些重要改动须要包括到develop分支中,所以小红要运行一个合并操作。然后就能够安全地删除这个分支了:

git checkout develop
git merge issue-#001
git push
git branch -d issue-#001

到了这里。但愿你对集中式工作流功能分支工作流Gitflow工作流已经感觉非常舒适了。
你应该也坚固的掌握了本地仓库的潜能,push/pull模式和Git健壮的分支和合并模型。

记住,这里演示的工作流仅仅是可能使用方法的样例,而不是在实际工作中使用Git不可违逆的条例。
所以不要畏惧按自己须要对工作流的使用方法做取舍。不变的目标就是让Git为你所用。


2.4 Forking工作流

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

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

技术分享

Forking工作流的一个主要优势是,贡献的代码能够被集成。而不须要全部人都能push代码到仅有的中央仓库中。
开发人员push到自己的服务端仓库。而仅仅有项目维护者才干push到正式仓库。
这样项目维护者能够接受不论什么开发人员的提交,但无需给他正式代码库的写权限。

效果就是一个分布式的工作流,能为大型、自发性的团队(包含了不受信的第三方)提供灵活的方式来安全的协作。


也让这个工作流成为开源项目的理想工作流。

2.4.1 工作方式

和其他的Git工作流一样。Forking工作流要先有一个公开的正式仓库存储在server上。
但一个新的开发人员想要在项目上工作时,不是直接从正式仓库克隆,而是fork正式项目在server上创建一个拷贝。

这个仓库拷贝作为他个人公开仓库 ——
其他开发人员不同意push到这个仓库,但能够pull到改动(后面我们非常快就会看这点非常重要)。
在创建了自己服务端拷贝之后,和之前的工作流一样,开发人员运行git clone命令克隆仓库到本地机器上。作为私有的开发环境。

要提交本地改动时,push提交到自己公开仓库中 —— 而不是正式仓库中。
然后,给正式仓库发起一个pull request。让项目维护者知道有更新已经准备好能够集成了。
对于贡献的代码,pull request也能够非常方便地作为一个讨论的地方。

为了集成功能到正式代码库,维护者pull贡献者的变更到自己的本地仓库中,检查变更以确保不会让项目出错,
合并变更到自己本地的master分支
然后pushmaster分支到server的正式仓库中。
到此。贡献的提交成为了项目的一部分。其他的开发人员应该运行pull操作与正式仓库同步自己本地仓库。

2.4.2 正式仓库

Forking工作流中,『官方』仓库的叫法仅仅是一个约定,理解这点非常重要。
从技术上来看,各个开发人员仓库和正式仓库在Git看来没有不论什么差别。
其实,让正式仓库之所以正式的唯一原因是它是项目维护者的公开仓库。

2.4.3 Forking工作流的分支使用方式

全部的个人公开仓库实际上仅仅是为了方便和其他的开发人员共享分支。
各个开发人员应该用分支隔离各个功能,就像在功能分支工作流Gitflow工作流一样。
唯一的差别是这些分支被共享了。在Forking工作流中这些分支会被pull到还有一个开发人员的本地仓库中,而在功能分支工作流和Gitflow工作流中是直接被push到正式仓库中。

2.4.4 演示样例

项目维护者初始化正式仓库

技术分享

和不论什么使用Git项目一样,第一步是创建在server上一个正式仓库,让全部团队成员都能够訪问到。
通常这个仓库也会作为项目维护者的公开仓库。

公开仓库应该是裸仓库。无论是不是正式代码库。
所以项目维护者会执行像以下的命令来搭建正式仓库:

ssh [email protected]
git init --bare /path/to/repo.git

BitbucketStash提供了一个方便的GUIclient以完毕上面命令行做的事。


这个搭建中央仓库的过程和前面提到的工作流全然一样。
假设有现存的代码库,维护者也要push到这个仓库中。

开发人员fork正式仓库

技术分享

其他全部的开发须要fork正式仓库。
能够用git clone命令SSH协议连通到server
拷贝仓库到server还有一个位置 —— 是的。fork操作基本上就仅仅是一个服务端的克隆。
BitbucketStash上能够点一下button就让开发人员完毕仓库的fork操作。

这一步完毕后,每一个开发都在服务端有一个自己的仓库。和正式仓库一样。这些仓库应该是裸仓库。

开发人员克隆自己fork出来的仓库

技术分享

下一步。各个开发人员要克隆自己的公开仓库,用熟悉的git clone命令。

在这个演示样例中。假定用Bitbucket托管了仓库。记住,假设这种话各个开发人员须要有各自的Bitbucket账号,
使用以下命令克隆服务端自己的仓库:

git clone https://[email protected]/user/repo.git

相比前面介绍的工作流仅仅用了一个origin远程别名指向中央仓库。Forking工作流须要2个远程别名 ——
一个指向正式仓库,还有一个指向开发人员自己的服务端仓库。别名的名字能够随意命名,常见的约定是使用origin作为远程克隆的仓库的别名
(这个别名会在执行git clone自己主动创建)。upstream(上游)作为正式仓库的别名。

git remote add upstream https://bitbucket.org/maintainer/repo

须要自己用上面的命令创建upstream别名。这样能够简单地保持本地仓库和正式仓库的同步更新。
注意,假设上游仓库须要认证(比方不是开源的)。你须要提供用户:

git remote add upstream https://[email protected]/maintainer/repo.git

这时在克隆和pull正式仓库时。须要提供用户的password。

开发人员开发自己的功能

技术分享

在刚克隆的本地仓库中,开发人员能够像其他工作流一样的编辑代码、提交改动新建分支

git checkout -b some-feature
# Edit some code
git commit -a -m "Add first draft of some feature"

全部的改动都是私有的直到push到自己公开仓库中。假设正式项目已经往前走了,能够用git pull命令获得新的提交:

git pull upstream master

因为开发人员应该都在专门的功能分支上工作。pull操作结果会都是快进合并

开发人员公布自己的功能

技术分享

一旦开发人员准备好了分享新功能,须要做二件事。


首先,通过push他的贡献代码到自己的公开仓库中。让其他的开发人员都能够訪问到。


他的origin远程别名应该已经有了,所以要做的就是:

git push origin feature-branch

这里和之前的工作流的差异是,origin远程别名指向开发人员自己的服务端仓库,而不是正式仓库。

第二件事,开发人员要通知项目维护者。想要合并他的新功能到正式库中。
BitbucketStash提供了Pull Requestbutton,弹出表单让你指定哪个分支要合并到正式仓库。


一般你会想集成你的功能分支到上游远程仓库的master分支中。

项目维护者集成开发人员的功能

技术分享

当项目维护者收到pull request,他要做的是决定是否集成它到正式代码库中。有二种方式来做:

  1. 直接在pull request中查看代码
  2. pull代码到他自己的本地仓库。再手动合并

第一种做法更简单。维护者能够在GUI中查看变更的差异。做评注和运行合并。
但假设出现了合并冲突。须要另外一种做法来解决。

这样的情况下,维护者须要从开发人员的服务端仓库中fetch功能分支,
合并到他本地的master分支,解决冲突:

git fetch https://bitbucket.org/user/repo feature-branch
# 查看变更
git checkout master
git merge FETCH_HEAD

变更集成到本地的master分支后。维护者要push变更到server上的正式仓库,这样其他的开发人员都能訪问到:

git push origin master

注意,维护者的origin是指向他自己公开仓库的。即是项目的正式代码库。到此,开发人员的贡献全然集成到了项目中。

开发人员和正式仓库做同步

技术分享

因为正式代码库往前走了,其他的开发须要和正式仓库做同步:

git pull upstream master

假设你之前是使用SVNForking工作流可能看起来像是一个激进的范式切换(paradigm shift)。


但不要害怕。这个工作流实际上就是在功能分支工作流之上引入还有一个抽象层。
不是直接通过单个中央仓库来分享分支,而是把贡献代码公布到开发人员自己的服务端仓库中。

演示样例中解释了。一个贡献怎样从一个开发人员流到正式的master分支中,但相同的方法能够把贡献集成到任一个仓库中。
比方,假设团队的几个人协作实现一个功能,能够在开发之间用同样的方法分享变更。全然不涉及正式仓库。

这使得Forking工作流对于松散组织的团队来说是个很强大的工具。任一开发人员能够方便地和还有一开发人员分享变更,不论什么分支都能有效地合并到正式代码库中。


2.5 Pull Requests

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

技术分享

开发人员向团队成员通知功能开发已经完毕,Pull Requests是最简单的使用方法。
开发人员完毕功能开发后。通过Bitbucket账号发起一个Pull Request
这样让涉及这个功能的全部人知道要去做Code Review和合并到master分支。

可是。Pull Request远不止一个简单的通知,而是为讨论提交的功能的一个专门论坛。
假设变更有不论什么问题,团队成员反馈在Pull Request中,甚至push新的提交微调功能。


全部的这些活动都直接跟踪在Pull Request中。

技术分享

相比其他的协作模型,这样的分享提交的形式有助于打造一个更流畅的工作流。
SVNGit都能通过一个简单的脚本收到通知邮件;可是,讨论变更时,开发人员通常仅仅能去回复邮件。
这样做会变得杂乱。尤其还要涉及后面的几个提交时。
Pull Requests把全部相关功能整合到一个和Bitbucket仓库界面集成的用户友好Web界面中。

2.5.1 解析Pull Request

当要发起一个Pull Request,你所要做的就是请求(Request)还有一个开发人员(比方项目的维护者)
pull你仓库中一个分支到他的仓库中。这意味着你要提供4个信息以发起Pull Request
源仓库、源分支、目的仓库、目的分支。

技术分享

这几值多数Bitbucket都会设置上合适的缺省值。

但取决你用的协作工作流,你的团队可能会要指定不同的值。


上图显示了一个Pull Request请求合并一个功能分支到正式的master分支上,但能够有多种不同的Pull Request使用方法。

2.5.2 工作方式

Pull Request能够和功能分支工作流Gitflow工作流Forking工作流一起使用。
但一个Pull Request要求要么分支不同要么仓库不同,所以不能用于集中式工作流
在不同的工作流中使用Pull Request会有一些不同,但主要的过程是这种:

  1. 开发人员在本地仓库中新建一个专门的分支开发功能。
  2. 开发人员push分支改动到公开的Bitbucket仓库中。
  3. 开发人员通过Bitbucket发起一个Pull Request
  4. 团队的其他成员review code,讨论并改动。
  5. 项目维护者合并功能到官方仓库中并关闭Pull Request

本文后面内容说明,Pull Request在不同协作工作流中怎样应用。

2.5.3 在功能分支工作流中使用Pull Request

功能分支工作流用一个共享的Bitbucket仓库来管理协作,开发人员在专门的分支上开发功能。
但不是马上合并到master分支上,而是在合并到主代码库之前开发人员应该开一个Pull Request发起功能的讨论。

技术分享

功能分支工作流仅仅有一个公开的仓库,所以Pull Request的目的仓库和源仓库总是同一个。
通常开发人员会指定他的功能分支作为源分支,master分支作为目的分支。

收到Pull Request后,项目维护者要决定怎样做。

假设功能没问题,就简单地合并到master分支。关闭Pull Request
但假设提交的变更有问题,他能够在Pull Request中反馈。之后新加的提交也会评论之后接着显示出来。

在功能还没有全然开发完的时候,也可能发起一个Pull Request
比方开发人员在实现某个需求时碰到了麻烦。他能够发一个包括正在进行中工作的Pull Request
其他的开发人员能够在Pull Request提供建议。或者甚至直接加入提交来解决这个问题。

2.5.4 在Gitflow工作流中使用Pull Request

Gitflow工作流和功能分支工作流类似。但环绕项目公布定义一个严格的分支模型。
Gitflow工作流中使用Pull Request让开发人员在公布分支或是维护分支上工作时,
能够有个方便的地方对关于公布分支或是维护分支的问题进行交流。

技术分享

Gitflow工作流中Pull Request的使用过程和上一节中全然一致:
当一个功能、公布或是热修复分支须要Review时。开发人员简单发起一个Pull Request
团队的其他成员会通过Bitbucket收到通知。

新功能一般合并到develop分支。而公布和热修复则要同一时候合并到develop分支和master分支上。


Pull Request可能用做全部合并的正式管理。

2.5.5 在Forking工作流中使用Pull Request

Forking工作流中,开发人员push完毕的功能到他自己的仓库中,而不是共享仓库。
然后。他发起一个Pull Request。让项目维护者知道他的功能已经能够Review了。

在这个工作流,Pull Request的通知功能很实用,
由于项目维护者不可能知道其他开发人员在他们自己的仓库加入了提交。

技术分享

因为各个开发有自己的公开仓库,Pull Request的源仓库和目标仓库不是同一个。
源仓库是开发人员的公开仓库。源分支是包括了改动的分支。
假设开发人员要合并改动到正式代码库中,那么目标仓库是正式仓库。目标分支是master分支。

Pull Request也能够用于正式项目之外的其他开发人员之间的协作。
比方,假设一个开发人员和一个团队成员一起开发一个功能,他们能够发起一个Pull Request
用团队成员的Bitbucket仓库作为目标。而不是正式项目的仓库。
然后使用同样的功能分支作为源和目标分支。

技术分享

2个开发人员之间能够在Pull Request中讨论和开发功能。
完毕开发后,他们能够发起还有一个Pull Request,请求合并功能到正式的master分支。
Forking工作流中,这种灵活性让Pull Request成为一个强有力的协作工具。

2.5.6 演示样例

以下的演示样例演示了Pull Request怎样在在Forking工作流中使用。
也相同适用于小团队的开发协作和第三方开发人员向开源项目的贡献。

在演示样例中,小红是个开发,小明是项目维护者。

他们各自有一个公开的Bitbucket仓库,而小明的仓库包括了正式project。

小红fork正式项目

技术分享

小红先要fork小明的Bitbucket仓库,開始项目的开发。

她登陆Bitbucket,浏览到小明的仓库页面,
Forkbutton。

技术分享

然后为fork出来的仓库填写名字和描写叙述,这样小红就有了服务端的项目拷贝了。

小红克隆她的Bitbucket仓库

技术分享

下一步,小红克隆自己刚才fork出来的Bitbucket仓库,以在本机上准备出工作拷贝。命令例如以下:

git clone https://[email protected]/user/repo.git

请记住。git clone会自己主动创建origin远程别名,是指向小红fork出来的仓库。

小红开发新功能

技术分享

在開始改代码前,小红要为新功能先新建一个新分支。她会用这个分支作为Pull Request的源分支。

git checkout -b some-feature
# 编辑代码
git commit -a -m "Add first draft of some feature"

在新功能分支上。小红按须要加入提交。甚至假设小红认为功能分支上的提交历史太乱了,她能够用交互式rebase来删除或压制提交。
对于大型项目,整理功能分支的历史能够让项目维护者更easy看出在Pull Request中做了什么内容。

小红push功能到她的Bitbucket仓库中

技术分享

小红完毕了功能后,push功能到她自己的Bitbucket仓库中(不是正式仓库)。用以下简单的命令:

git push origin some-branch

这时她的变更能够让项目维护者看到了(或者不论什么想要看的协作者)。

小红发起Pull Request

技术分享

Bitbucket上有了她的功能分支后,小红能够用她的Bitbucket账号浏览到她的fork出来的仓库页面,
点右上角的【Pull Request】button,发起一个Pull Request
弹出的表单自己主动设置小红的仓库为源仓库,询问小红以指定源分支、目标仓库和目标分支。

小红想要合并功能到正式仓库。所以源分支是她的功能分支,目标仓库是小明的公开仓库,
而目标分支是master分支。另外。小红须要提供Pull Request的标题和描写叙述信息。
假设须要小明以外的人审核批准代码。她能够把这些人填在【Reviewers】文本框中。

技术分享

创建好了Pull Request,通知会通过Bitbucket系统消息或邮件(可选)发给小明。

小明review Pull Request

技术分享

在小明的Bitbucket仓库页面的【Pull Request】Tab能够看到全部人发起的Pull Request
点击小红的Pull Request会显示出Pull Request的描写叙述、功能的提交历史和每一个变更的差异(diff)。

假设小明想要合并到项目中,仅仅要点一下【Merge】button。就能够允许Pull Request并合并到master分支。

但假设像这个演示样例中一样小明发现了在小红的代码中的一个小Bug。要小红在合并前修复。
小明能够在整个Pull Request上加上评注。或是选择历史中的某个提交加上评注。

技术分享

小红补加提交

假设小红对反馈有不论什么疑问。能够在Pull Request中响应。把Pull Request当作是她功能讨论的论坛。

小红在她的功能分支新加提交以解决代码问题,并push到她的Bitbucket仓库中。就像前一轮中的做法一样。
这些提交会进入的Pull Request。小明在原来的评注旁边能够再次review变更。

小明接受Pull Request

终于,小明接受变更,合并功能分支到master分支,并关闭Pull Request


至此,功能集成到项目中,其他的项目开发人员能够用标准的git pull命令pull这些变更到自己的本地仓库中。

到了这里,你应该有了全部须要的工具来集成Pull Request到你自己的工作流。
请记住,Pull Request并非为了替代不论什么 基于Git的协作工作流
而是它们的一个便利的补充,让团队成员间的协作更轻松方便。












































































































































































































以上是关于深入理解学习Git工作流的主要内容,如果未能解决你的问题,请参考以下文章

Git 学习深入理解git reset 命令

工具Git

使用plumbing命令来深入理解git add和git commit的工作原理

SourceTree 实现 git flow 流程

Git之深入解析如何贮藏工作分支与清理工作目录

Git之深入解析如何通过GPG签署和验证工作