一个成功的git分支模型


A successful Git Brabching model

在这篇文章中我将介绍一些在过去一年中(无论是工作还是个人项目中)较为成功的开发模型,这里不会介绍很多项目的细节,仅仅是介绍分支策略和发布管理。

git-model@2x

整个模型都是专注于Git 作为一个源代码版本管理工具进行的。

Why git

尽管Git和中央源代码管理系统之间的优劣讨论从未停止过,期间的争论也很激烈,但是作为一个开发者,我更习惯用Git作为我的版本管理工具,Git彻底改变了开发者合并和分支的思维习惯。在传统的CVS/Subversion系统中 ,合并分支是一件非常恐怖的事情,你需要非常小心。

但是在Git中,这些行为变得非常地简单并且安全,这几乎变成了程序员每天工作流中的核心部分。就拿书来说,讲CVS/Subversion系统的书中,分支和合并几乎是在后面的章节中进行介绍的(为高级用户准备的)但是在几乎所有介绍Git的书中,分支和合并的内容都放到了前三章作为基础内容进行介绍。

Decentrailzed but centrailzed

(标题我译为分布且集中,但是好像并不是很恰当)

在我们的日常工作中,工作较好的模型是存在一个中心化的truth”(可信)仓库,需要注意的是,这个仓库仅仅是在逻辑上被设置为一个中央仓库(因为Git是一个DVCS<分布式版本控制系统>,它在技术层面并没有一个实际意义上的中央仓库)我们将这个“中央”仓库称为origin,几乎所有的Giter都对这个名字非常熟悉。
centr-decentr@2x

所有的开发者都从origin 拉取代码,并将修改push到origin仓库中。除了到中央仓库push-pull之外,开发者还可以从其他的子团队分支中拉取代码。举例来说,在开发一个非常重大的feature的时候,可能会有多个开发者共同合作,很多时候push到“中央”仓库可能还太早,这个时候这些子团队分支就起到作用了。就像上图所示,这些子团队分支就像Alice-Bob,Alice-David和Clair-David子团队分支。

从技术上来说,让Alice 定义一个新的名叫Bob的远程分支,并将其指向Bob的仓库,并不需要多少成本,反之亦然。

The main branches

(主分支策略)

这个开发模型在很大程度上是由现有的模型演变而来,就像下图所示。核心仓库有两个无限生命周期的分支:

  • master
  • develop

origin仓库的master分支相信对所有的Git user一定非常熟悉,与master分支同时存在的分支并行发展,被称为develop分支。

main-branches

我们通常将origin/master分支作为源代码的HEAD,并将其标记为production-ready(可供发布)状态。

我们把origin/develop分支作标记为最新开发进度的HEAD,所有在develop分支上所做的改变都将体现在下个发布版本中。有的人将这个分支为“集成分支”,很多自动化的预发布版本都是从这个分支编译而来。

develop分支上的源代码达到了一个可以发布的点的时候,所有的变更都将以某种方式合并回master并将打上一个发布tag。当然具体是怎么做的将在今后进行讨论。

因此,每次将变更合并到master就意味着新的生成版本发。所以这件事需要非常严格地进行审核,从理论上讲,我们可以在每次commitmaser分支的时候,用git hooks进行自动化编译,并自动化地将我们的软件发布到我们的生产服务器。

Supporting branches

除了main分支和develop分支,我们的开发模型还拥有很多支持分支来帮助团队成员之间进行协作,这些分支可以方便跟踪新特性,为生产发布做准备和快速热修复产品中的问题。不像前面提到的主要分支,这些分支最终都会本删除,所以这些分支的生命周期是有限的。

支持分支主要分为以下三类:

  • Feature branches
  • Release branches
  • Hotfix branches

这些分支都有其自身的目标,并遵守相应严格的规则去确定哪个分支是其的源分支,那些分支是其需要合并进入的分支。

实际上从技术层面这些分支和普通分支一样并不是特殊的分支。这些分支的分类取决于我们如何去使用它。这些分支实际上就是普通的Git分支。

Feature branches

可能检出的分支:

  • develop

需要并入的分支:

  • develop

分支可以命名为:

除了:master,develop,release-*或者hotfix-*之外的名称

fb@2x

Feature分支(有的时候也成为主题分支)是在开发新的特性或者是发布新的版本的时候使用的。当开始一个新的特性的开发的时候,往往新特性的内容在当时是不可见的,但是实质上,feature将会一直存在直到该feature开发完毕,最终将会被合并回develop分支(为了能够真正地将新特性加入到上游分支,)或者被废弃(例如令人失望的尝试)。

Feature 分支往往只存在在开发仓库中,而不是在origin仓库中。

Creating a feature branch

当你需要开始开发一个新的feature的时候,你可以从develop分支检出新的feature分支:

$ git checkout -b myfeature develop

此时将会切换到新的分支 "myfeature"

在develop分支中加入新的feature

当你结束一个新的特性开发之后,往往需要将该分支合并到develop中将新特性真正加入到上游发布版本中:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

这里的no-ff选项往往会通过创建一个新的commit开进行合并,就算这个合并能够用fast-forward方式进行处理。这样就可以避免丢失一些在开发过程中的提交历史信息,通过这种方式能够正确地组织相应的commit以及将feature很好地进行合并。我们可以通过下图进行比较理解:

merge-without-ff@2x

在后面的例子中,我们是无法看到在实现分支的时候的所有提交历史的,你只能通过阅读相应的log开确定提交历史,当然回滚整个feature(例如一组commit)变成了一件非常头痛的事情,当然如果你用课no-ff选项,这些事情都变得很简单。

当然,这会创建一些新的(空的)commit,但是得到的好处远远能够抵消这点不足。

Realease branches

可能由以下分支检出:

  • develop

必须要合并回以下分支:

  • develop
  • master

分支命名规则:

  • release-*

Release分支为新产品的发布做准备,其允许做一些一些最终的修改(last-minute dotting of i’s and t’s)。进一步说明,该类型的分支,允许少量的bug-fix以及添加一些为发布准备的说明性信息(例如版本号,编译日期等等),当release分支上的所有工作都做完之后,develop分支就可以完全为下一个大型的release接受新的提交了。

这里有一个比较关键的时间点,就是何时从develop分支检出新的release分支,这个时间点往往是develop分支已经能够完全反映当前发布版本所需要的所有特性。此时至少所有的需要在发行版本中所包含的(release-to-be-build)特性都已经被合并到develop分支中了,需要提一下的是,所有下个版本的新特性都不能够合并到develop分支,直至新的release被检出(branch off)。

显然,发布版本号是在为了即将到来的新的发布版本开启一个新的release分支的时候指定的——换句话说不要太早。这样做的原因是,当develop中有一些被标记为next release的变更,但是并不明确next release最终会变成0.3还是1.0,只有release分支开始才被确定。一个新的release分支的启用往往是在版本号被确定之后。

创建一个release分支

发布分支是从develop分支中创建的。举例来说,当前的生产分支是1.1.5,这个时候有一个大的发布版本即将发布。此时develop分支应该应该已经准备好并处于next release状态,这个时候我们应该已经确定了具体的发布版本号为1.2(而不是1.1.6或者2.0),所以我们检出新的分支,并给予发布分支一个名称用于标识新的版本号。

$ git checkout -b release-1.2 develop
Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-)

在创建一个新的分支并切换到该分支之后,我们运行了一个bump-version.sh的脚本,这个脚本将会修改一些文件并让目前的工作空间变成能够反映新的版本特性。(当然这些修改也可以手动进行——这里的重点是需要进行一些修改)然后,这个已经被确认的版本号就会被提交。

结束一个release分支

当一个release分支已经准备好成为一个真正的发布版本,一些相应的动作需要被执行。首先这个发布分支需要合并到master分支当中(切记,所有提交到master分支上的变更都是可发布的)。然后,这个master分支上的commit应该打一些标记便于将来快速查询,当然这些信息需要简单易懂。

最后,这些在release分支上的变更应该合并回develop分支,这样在后面的分支才能够拥有在这个release分支上的bugfix

前两步的命令如下:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2

目前,release分支已经完成了它的使命,并打了一个tag便于今后查询。

说明,你也可以利用-s或者-u命令为你打的tag进行一些数字签名。

为了保有在release分支上的变更,我们需要将这些变更合并回develop分支。当然,用git命令:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)

这些步骤可能引起一些合并冲突(当然这是很正常的,因为我们已经修改了版本号)。如果这样,就fix这些冲突,并commit它。

到这里为止,这个release分支真真正正完成了它所有的使命,这个时候我们就可以把这个分支删除了,因为它已经失去了利用价值了。

$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).

Hotfix分支

可能检出的分支:

  • master

需要并入的分支:

  • develop和master

分支可以命名为:

hotfix-*

hotfix-branches@2x

Hotfixrelease分支十分相似,因为它们都是为了新产品发布而准备的,尽管这个分支往往是计划外的。它们往往会在一个非修复不可的,非常紧急的或者不期望看到的行为或者bug的时候生产版本中诞生。当生产版本上一个非常严重的bug需要立即修复的时候,一个hotfix分支就被检出了,这个分支可以从master分支的相应tag中检出。

这样做的关键在于,在develop分支上工作的团队不需要中断当前的工作,与此同时,另一个人可以快速修复生产版本上的bug。

创建一个hoxfix分支

Hoxfix分支是从master中诞生的,举例来说,当前的生产版本是1.2,而且该版本在实际业务中正运行着,但是有一些bug需要修复,而此时在develop分支上的版本还不稳定。我们就可以检出一个hotfix分支来解决这个问题。

$ git checkout -b hotfix-1.2.1 master
Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
Files modified successfully, version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)

不要忘记将该版本号进行标记。

然后修复bug并通过一个或多个commit进行提交。

$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)

结束一个hotfix分支

当bug结束修复,这个bugfix分支需要重新合并到master分支中,而且也要同时合并到develop分支中,这是为了确保这些bugfix能够真正合并到下个发布版本中。这里就和结束release分支很像了。

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1

说明,你也可以利用-s或者-u命令为你打的tag进行一些数字签名。

然后将bugfix分支合并到develop分支中:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)

这里有一个例外,如果此时有一个release分支存在,这个hotfix就需要合并到release分支中,而不是develop分支。将hotfix合并到release分支中,是因为,在release分支结束的时候,最终也会合并入develop分支中,这样最终的结果也是讲bugfix安全地合并到了develop分支中。

最后删除hotfix分支:

$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).

总结

虽然在这个分支模型中,没有什么令人震惊的奇技淫巧,但开篇的那个大图所标示的分支模型已经被证明在实际开发中能够起到非常大的作用。这个模型提供了一个优雅的协作范式,它易于理解,并能够让整个团队一直保持易于理解并便于协作的状态。

这里有一副PDF版本的高质量模型图,将它下载下来并作为一个快速查询图用吧!

当然这里还有一个gitflow的keynote,你可以下载下来用哦;)

Sublime Text 安装配置,代理设置,插件管理


 

步骤1: 下载并且安装

你可以从以下链接下载最新版本:

目前我使用的版本是ST3 build 3065 更新于2014年8月29日

译者 用的版本是st3 build 3083

步骤2: 安装Package Control (插件管理工具)

如果想安装其他的插件,需要先安装这个Package Control 插件,请遵照这个链接中的安装教程:

Package Control Install

注意:如果你在使用代理,可能需要手动下载并安装这个包,安装完成之后,需要重启下sublime text (出于安全因素的考虑)

Sublime Text 安装配置,代理设置,插件管理

Sublime Text – Installation and Configuration (English)

我一直想写一篇文章,用来说明如何按照我自己的喜好来进行Sublime Text(目前版本是V3)的安装和设置。
这篇文章更多的是为我自己写的一篇指南,以免未来的什么时候我忘记了。如果这篇文章能够帮到你,那就更棒啦!

步骤1: 下载并且安装

你可以从以下链接下载最新版本:

目前我使用的版本是ST3 build 3065 更新于2014年8月29日

译者 用的版本是st3 build 3083

步骤2: 安装Package Control (插件管理工具)

如果想安装其他的插件,需要先安装这个Package Control 插件,请遵照这个链接中的安装教程:

Package Control Install

注意:如果你在使用代理,可能需要手动下载并安装这个包,安装完成之后,需要重启下sublime text (出于安全因素的考虑)

译者
由于考虑到国内不一定能打开安装链接,我把安装代码贴在下面,请在st3中按快捷键’ctrl+`’然后再弹出的输入框中输入以下代码并回车(2015/8/25):

import urllib.request,os,hashlib; h = 'eb2297e1a458f27d836c04bb0cbaf282' +</blockquote>
'd0e7a3098092775ccb37ca9d6b2e4b7d'; pf = 'Package Control.sublime-package';
ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) );
by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest();
print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

更新: 这个脚本是每天更新的,复制过去脚本会失效,请用上面的安装链接践行安装。(2017-04-01)

步骤2(附): Package Control的代理设置

如果你需要在代理环境中使用Sublime Text ,那么你需要配置一下代理来让Package Control 正常工作。

将以下代码加入到
Preferences > Package Settings > Package Control > Settings – User
中:


//proxy settings "http_proxy": "your.proxy.fqdn:80", "https_proxy": "your.proxy.fqdn:80", "proxy_username": "my_username", "proxy_password": "my_password",

注意:
你可能需要修改80端口为别的你代理的端口(比如8080), 你可以在IE的代理设置中找到这个端口 (译者用的是ss,视实际情况而定)。

完成之后,重启下Sublime Text

步骤3: 安装Sublime Text的插件

现在,你可以安装所有能让Sublime Text更酷的插件! 以下的步骤是安装所有插件需要的步骤:

  1. Cmd+shift+P (OSX) / Ctrl+Shift+P (Win/Linux)
  2. 输入:Install Packsge
  3. 搜索你需要的插件名称
  4. 按回车进行安装

我在Sublime Text中使用的插件有:

  • CSS Comb
  • Emmet<sub>1</sub>
  • Emmet CSS Snippets
  • FileDiffs (only on Windows machines)
  • Gist
  • Predawn<sub>2</sub>
  • PowerShell
  • SASS
  • SCSS
  • Sidebar Enhancements
  • SublimeLinter
  • VBDotNet
  • vbScript
  • WordPress

1: To use Emmet you need PyV8 installed. By default when installing Emmet, PyV8 will attempt to download and install automatically. If it doesn’t you an download it from here Emmet PyV8.
如果想要使用emmet插件需要安装PyV8依赖,一般情况下可以自动安装完成,如果它傲娇了,请务必点击上面的链接下载自行安装。

2: You must install Predawn prior to configuring User Settings in Step 5 below (otherwise the settings in the Colour Scheme & Theme section will fail).
For more information about PreDawn and the options available have a look here Predawn GitHub
如果你想要更改颜色主题,请一定要按照步骤5中的用户配置进行相应配置。

步骤4: 编辑asp页面以包含aspx 文件

由于这部分并不重要,是作者自己个人的偏好,并没有普遍性,所以不翻译了

步骤5: Sublime Text的用户设置:

想要改变SublimeText的默认设置,你需要把你的个人设置添加到User Settings文件中,你可以在Preferences &gt; Settings – User找到并编辑这个文件:

下面的是我自己添加的:

// Colour Scheme & Theme (for ST3)

"color_scheme": "Packages/User/predawn (SL).tmTheme",
"theme": "predawn-DEV.sublime-theme",

// Typography

"font_face": "Source Code Pro",
"font_size": 13,
"font_options": ["no_round"],
"highlight_line": true,
"caret_extra_width": 1,
"caret_style": "phase",
"tab_size": 2,
"translate_tabs_to_spaces": true,
"word_wrap": false,

// Whitespace, Matching, Copy & Auto-Complete

"copy_with_empty_selection": false,
"drag_text": false,
"match_brackets_content": false,
"match_selection": false,
"match_tags": false,
"translate_tabs_to_spaces": true,
"trim_trailing_white_space_on_save": true,

// Interface & Behavior

"close_windows_when_empty": false,
"draw_minimap_border": true,
"enable_tab_scrolling": false,
"hot_exit": false,
"overlay_scroll_bars": "enabled",
"open_files_in_new_window": false,
"preview_on_click": false,
"remember_open_files": false,
"scroll_past_end": true,
"scroll_speed": 5.0,
"show_full_path": false,

// Other Settings

"ignored_packages":
[
"Vintage"
],

注意:上面的是Sublime Text 3的设置,如果你正在使用ST2你需要把Colour Scheme & Theme 部分修改成下面的

//Colour Scheme Theme (for ST2)
"theme": "predawn.sublime-theme",
"color_scheme": "Packages/Predawn/predawn.tmTheme",

步骤6: 改变Sublime Text的图标

现在有很多的Sublime Text的炫酷图标,我也把我自己的改掉了,你可以按照下面的步骤进行修改:

Mac OSX

略(有兴趣请大家翻原文看)

Windows8

  1. 把新的图标拷贝到安装目录下(我的是D:\SublimeText3\)
  2. 右键点击 Sublime Text.exe
  3. 点击创建快捷方式
  4. 重命名快捷方式
  5. 右键点击快捷方式并选择属性
  6. 选择快捷方式栏
  7. 点击更改图标
  8. 选择新的图标
  9. 点击ok
  10. 右键点击快捷方式并放到开始中
    > 尼玛简直太感动太详细了,我居然翻译完了。。。)

步骤7: 安装Sublime Text 的许可

土豪请随意购买许可。

原文是教你如何购买许可的,当然这没啥意思,所以我找了几个许可大家随意取用(土豪请务必购买正版,请支持正版):

—– BEGIN LICENSE —–
Andrew Weber
Single User License
EA7E-855605
813A03DD 5E4AD9E6 6C0EEB94 BC99798F
942194A6 02396E98 E62C9979 4BB979FE
91424C9D A45400BF F6747D88 2FB88078
90F5CC94 1CDC92DC 8457107A F151657B
1D22E383 A997F016 42397640 33F41CFC
E1D0AE85 A0BBD039 0E9C8D55 E1B89D5D
5CDB7036 E56DE1C0 EFCC0840 650CD3A6
B98FC99C 8FAC73EE D2B95564 DF450523
—— END LICENSE ——
—– BEGIN LICENSE —–
K-20
Single User License
EA7E-940129
3A099EC1 C0B5C7C5 33EBF0CF BE82FE3B
EAC2164A 4F8EC954 4E87F1E5 7E4E85D6
C5605DE6 DAB003B4 D60CA4D0 77CB1533
3C47F579 FB3E8476 EB3AA9A7 68C43CD9
8C60B563 80FE367D 8CAD14B3 54FB7A9F
4123FFC4 D63312BA 141AF702 F6BBA254
B094B9C0 FAA4B04C 06CC9AFC FD412671
82E3AEE0 0F0FAAA7 8FA773C9 383A9E18
—— END LICENSE ——
—– BEGIN LICENSE —–
J2TeaM
2 User License
EA7E-940282
45CB0D8F 09100037 7D1056EB A1DDC1A2
39C102C5 DF8D0BF0 FC3B1A94 4F2892B4
0AEE61BA 65758D3B 2EED551F A3E3478C
C1C0E04E CA4E4541 1FC1A2C1 3F5FB6DB
CFDA1551 51B05B5D 2D3C8CFE FA8B4285
051750E3 22D1422A 7AE3A8A1 3B4188AC
346372DA 37AA8ABA 6EB30E41 781BC81F
B5CA66E3 A09DBD3A 3FE85BBD 69893DBD
—— END LICENSE ——

注册码来源

相杀相爱,人生就是要有激情不断的爱情


史密斯夫妇影评

早早就看过一遍《史密斯夫妇》,对于布拉德·皮特与安吉丽娜·朱莉之间的绯闻也略有耳闻,今天同学在看《史密斯夫妇》的时候,我也凑过去重温了一遍这部豆瓣上也算高分(7.5)的电影,。

注意:严重剧透,请慎重观看。

说实话,这是一部非常有趣的电影,有帅哥美女也有枪战飙车,最重要的是,夫妇之间情感流露的表达,在我看来简直就是子弹中流露着温情,刀刃中暗藏着暧昧,真心是一部不可多得的电影。整部电影最让我觉得赞的点在于夫妻两人在相搏的时候,咬牙狠心,想着对着自己的老婆(老公)开枪,给人的感觉就是一种莫名的喜感,真心没有说哪一方会得胜,哪一方会被杀。搏斗期间还不忘问候自己的baby.搏斗到最后,是John 先放下枪,给人的感觉又是作为一个男人不舍得杀死自己的妻子,这其中可以看出男人是心爱着她的,于此同时,Jane又说“oh come,just kill me!”(我是凭着印象写的,未必是原话,但是意思是差不多的)。可以看出,Jane内心是宁愿被John 杀死,也不愿亲手开枪杀死眼前的这个男人。这个男人给的小甜蜜真心是可以侵染一个女人的心,而且这个男人还这么handsome 。 最后枪战变成了肉搏,变成了“肉搏”(在这里我不禁要说呵呵了,话说安吉丽娜·朱莉真心有个好身材)

最后天亮的时候穿的白衬衫真心是迷人的(若隐若现的感觉是最为性感的)。到这里,开始有了不打不相识的感觉,他们开始相互袒露心声,虽然还是有所戒备,但是其实对于在这种地方结尾观众老爷也不会有什么不满意了,但是,很不幸,故事还没有结束…

整部电影的打斗场面没有什么好说的,真心不是一部动作片,但是我喜欢的是它的情节,巧妙的剧本很吸引人,两个相互敌对的间谍组织成员结成的夫妇将会擦出怎么样的火花,导演并没有让我们失望,这部电影在我看来都可以算是温情爱情片了哈哈。在追杀情节中,男主说我其实以前结过婚,Jane猛地一踩刹车,给了John一耳光,真的是很搞笑也很温馨,安吉丽娜把小女生的心理表现得非常到位,但是恰巧她又是一个冷酷无情的杀手。

影片的剧情本来没有什么跌宕起伏,但是导演并不甘心平淡的结局,最后派遣给他们的任务是组织企图抹杀他们夫妇两而设的圈套,虽然不是什么360度大转变的情节,但是也着实让我没有想到,因为只有把观众都引导向导演安排的心境,这个电影才能达到所需要的效果,这个情节的加入让本以为已经以圆满结尾结束的整部电影,现在又让观众提起了精神继续看下去,说实话,真的是被导演牵着鼻子走,所以看着真心是不错。

在无情剧透影评之后,我还是想说,这部电影是一部“温情动作谍战爱情”片,还是推荐大家去看看的。