Git提供异常灵活的分支操作,但是在实践中也是需要制定良好的分支策略才能保证有效的利用Git来管理代码。
最近发现了一篇很好的英文文章,https://www.atlassian.com/git/tutorials/comparing-workflows,根据其内容作了整理。
关于分支策略,有如下几种好的实践方式。
Centralized Workflow / 集中式工作流
集中式工作流非常类似于SVN,但是你也可以享受到Git带来的好处,比如可以完全在本地工作直到你要提交,在比如利用Git强壮的分支和合并模型。
How It Works / 如何工作
中央仓库上只有一个master分支,类似于SVN中的trunk。所有的改变都提交到master分支。
Managing Conflicts / 合并提交和管理冲突
如果提交的时候中央库上已经有了新的提交,产生了冲突,需要pull中央库的最新提交,然后rebase自己的修改到最新提交之上。
例如上图所示,中央库的master分支origin/master上有了新的提交,本地的master分支上也有了新的提交。
这个时候,如果直接git push,git会报告需要合并修改。通过以下的几步合并修改并解决冲突。
1)git pull --rebase
加上--rebase选项是为了把本地的提交移动到中央库分支的master之上,如下图所示。rebase相当于先获取中央库的提交,然后把本地的提交一个一个应用上去。
如果不加上rebase选项,也可以完成合并,但是每次会生成一个“Merge branch ...”的提交。
比如A和B各自添加了文件0,1,分别提交,合并修改的时候没有用rebase选项;之后A和B又分别提交了文件2和3,使用rebase选项解决冲突。
下面是通过”git log --graph --oneline“命令观察分支图的结果,可以很清楚的看出其中的区别。
* dc5fbe2 3
* 98eda49 2
* cfc8ea7 Merge branch 'master' of https://github.com/gitworkflow/learngit
|\
| * a3aa78c 0
* | 90cc835 1
|/
* b2d5caf new
2)如果中央库的提交和本地提交没有修改相同文件,那么现在可以直接push了。否则的话,git会在合并有冲突的地方暂停。
比如现在A修改了文件0和1,A的提交历史如下并且已经提交到了中央库。
* a014fa1 vi 1
* fb1193a vi 0
* dc5fbe2 3
* 98eda49 2
B也修改了文件0和1,B的本地提交历史如下
* 57e6473 edit 1
* d0ea63e edit 0
* dc5fbe2 3
* 98eda49 2
现在B运行”git pull --rebase“命令,git会先拉取到中央库的最新提交a014fa1,然后在其之上应用本地的提交d0ea63e和57e6473。
由于有冲突,git会报告下面错误并暂停rebase。
......
CONFLICT (content): Merge conflict in 0
......
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
这里git给出的提示也非常直接和详细。
这个时候也可以运行git status获取到详细冲突信息。
那接下来,我们需要
2.1) 编辑冲突文件,解决冲突。
2.2) git add --all
2.3) git rebase --continue
这个时候git会报告 CONFLICT (content): Merge conflict in 1,接下来冲突上面的操作直到解决全部冲突。
在这个过程中的任何时候,如果我们需要放弃这次合并,可以运行“git rebase --abort”命令,就会回到第一步运行”git pull --rebase"之前的状态。
2.4) git push
最后别忘记push提交到中央库。
Feature Branch Workflow / 特性分支工作流
如果习惯了上面的集中式工作流,就很容易添加特性分支来在开发者之间鼓励合作和提高交流效率。
特性分支工作流的核心思想是所有的特性开发都必须在一个独立分支上进行。这也意味着master分支上永远不应该包含未完成的代码。这对于持续集成来说是非常重要的。
How It Works / 如何工作
中央仓库上也有一个master分支,代表正式的项目历史。
但是开发者不能把修改提交到本地的master分支。开发者每次开发一个新的特性之前,必须创建一个新的分支,注意给分支取一个有意义的名字。
此外特性分支也需要push到中央仓库,这样才能和其他开发者分享代码。当然这样也便于备份本地提交。
Pull Request
特性分支也使得通过pull request来讨论变更成为可能。
一旦某个开发者完成一个feature,不能马上合并到master分支,必须把feature分支push到中央库,然后发起一个pull request来请求合并到master分支。这样其他开发者就可以review修改。
其实除了进行code review,pull request也是进行讨论的一个方式。
比如在Github的pull request发起界面,你可以填写comment提出问题,收到request的开发者可以review代码提出意见,也可以填写comment来回答问题。
讨论中,开发者可以对这个feature 分支再提交修改,这些提交历史以及讨论内容都会显示在这个pull request的历史记录中。
最终,如果这个pull request得到认可,就可以合并到master分支上。
可以通过Github等工具提供的类似[Merge pull request]按钮来完成合并,也可以通过下面的命令行合并。
git checkout master
git pull origin master
git merge origin/feature-xxx --no-ff
git push
首先pull master分支的最新修改,然后合并feature分支到master分支。
加上 --no-ff选项,可以确保不使用fast forward合并,这样虽然上面的过程会产生一个合并提交,但是保留了feature分支的提交历史,这样通过git log --graph可以看到feature分支的历史信息。
(如果要保证线性的提交历史,可以去掉 --no-ff选项,但是对于feature分支工作流,不建议这么做)
Gitflow Workflow / Gitflow工作流
Gitflow工作流围绕着项目发布定义了严格的分支模型。虽然比较复杂,但是对于大型项目管理提供了一个健壮的框架。
和feature分支工作流相比,它使用了独立的分支来管理发布。
参考下图,Gitflow工作流涉及到2个主分支(master,develop)和3个支持分支(feature,release,hotfix)。
具体介绍可以看这篇文章http://blog.csdn.net/fw0124/article/details/50426740
Forking Workflow / Forking工作流
前面介绍的几种工作流都是只有一个服务器端仓库(中央仓库)。Forking工作流和上面的几种工作流之间有一个根本性的区别是每个开发者有自己的服务端仓库。这意味着每个开发者有两个服务器端仓库,一个是公共的仓库(offical repo),一个是开发者自己的仓库。
开发者只有权限提交修改到私有服务器端仓库,只有项目维护者有权限push修改到公共仓库。
从技术的角度,对于Git 来说,这两种服务器端仓库没有区别,称作正式仓库只是因为它是项目维护者的服务器端仓库。
How It Works / 如何工作
使用Forking工作流,项目进展方式如下:
1)项目维护者创建公共仓库(offical repo)
2)项目开发者fork公共仓库,创建私有服务器仓库拷贝。
其他的开发者不能push到这个仓库,但是可以pull其修改。
开发者可以使用分支以便于和其他开发者分享修改。
3)项目开发者clone私有服务器仓库到本地。
4)项目开发者进行开发。
5)项目开发者push修改到私有服务器仓库。
6)项目开发者发起一个pull request通知维护者需要提交修改到正式仓库。
如上面所说,pull request也在开发者之间提供了一个便利的讨论方式。
7)项目维护者pull开发者的提交到本地,merge到本地的master分支上,并进行检查,如果没有问题,push到正式仓库的master分支。
维护者可以在Github等工具提供的pull request的界面中直接审查修改,评论和执行合并。
不过如果出现了合并冲突,需要到命令行执行如下命令来解决冲突。
git fetch https://bitbucket.org/user/repo feature-branch
# Inspect the changes
git checkout master
git merge FETCH_HEAD
git push origin master
8)其他的开发者可以同步正式仓库的修改到私有仓库中。
git pull upstream master
对于开源项目,Fork工作流是一个非常理想的工作方式。
Forking工作流对于松散组织的团队来说是个非常强大的工具。任和开发者可以方便地和其他开发者分享变更,任何分支都能够有效地合并到正式代码库中。