代码分支管理指南

介绍

这是我们团队的 Git 分支管理规范。每个人对工具的使用往往各有偏好,各种方法各有利弊,无所谓对错。但涉及团队协作的方面需要有一些一致的规范,所以请大家务必遵守。

除了一致性之外,这个规范的目的是以下几点:

  • 确保可以轻易确定特定时间发布或运行的版本。在新发布的程序存在重大缺陷时,可以尽快 rollback 到上一个稳定版本。
  • 在需要修复紧急 bug 并尽快发布时,可以只发布必要的 bugfix 而不同时发布还不应发布的其他改动。

branch 和 tag

每个官方的 repo(leancloud/ 下的都是官方 repo)有且仅有以下的 branch 和 tag。

Branch: master release。其中 master 对应目前的开发分支,所有的 pull request 都应该发到这个分支。release 是当前发布的分支,在这个分支只能增加从 master cherrypick 过来的 commit。详见本文后面的说明。

Tag: 对应每个发布版本的 tag。SDK 和应用程序的 tag 遵照 <major>.<minor>.<patch> 的命名,如 2.5.1;服务端程序的 tag 以发布的日期命名,如 2014.11.13,如果有 bugfix,则在后面增加小写字母,如 2014.11.13 后是 2014.11.13a,然后是 2014.11.13b

目前还有部分 repo 包含多个独立部署的项目(如 uluru-platform)。在这样的 repo 打 tag 时需要附上项目名做前缀,如 bigquery-2.5.1。但我们需要逐步把这些项目拆分到独立的 repo。

发布新版流程

  • 确保所有要发布的 pull request 都已经 merge 到 master
  • 使用 master branch 的代码进行测试,如果发现 bug,把对应的 bugfix merge 到 master
  • 删除旧的 release branch,并从当前的 master 创建新的 release branch;
  • 在 Jenkins 上从releasebranch 发起新的 build 并发布;
  • 发布完成后在当前的 release branch 打上对应版本的 tag。

Bugfix 流程

这里的 bugfix 指的是修复已经发布的程序(release branch)中的缺陷。master 里的 bug 请直接 merge bugfix 到 master

  • 如果此缺陷在master中还存在,请先 merge bugfix 到master ,否则跳到下一步;
  • releasebranch 从mastercherrypick 修复该缺陷的一个或多个 commit;
  • 在 Jenkins 上发布当前releasebranch;
  • 发布完成后在当前的releasebranch 打上递增的 tag。比如,如果上一个 tag 是2.5.1 ,这个 tag 应该是2.5.2 ;如果上一个是2014.11.13 ,这个就是2014.11.13a

其他

并不是每个 bug 都有专门发布 bugfix 版的必要,对于不紧急的 bug,可以在master里 fix 后随下一个版本发布。

在一个官方 repo 下只应该有以上说的 branch 和 tag,在开发过程中使用到的 feature branch 等请都放在个人的 fork,一律通过向master发 pull request 的方式给官方 repo 提交代码。

转自http://open.leancloud.cn/git-branch-guide.html

git commit emoji

commit 格式

git commit 时,提交信息遵循以下格式:

1
2
3
4
5
:emoji1: :emoji2: 主题

提交信息主体

Ref <###>

初次提交示例:

1
git commit -m ":tada: Initialize Repo"

emoji 指南

emoji emoji 代码 commit 说明
🎨 (调色板) :art: 改进代码结构/代码格式
⚡ (闪电)🐎 (赛马) :zap:``:racehorse: 提升性能
🔥 (火焰) :fire: 移除代码或文件
🐛 (bug) :bug: 修复 bug
🚑 (急救车) :ambulance: 重要补丁
✨ (火花) :sparkles: 引入新功能
📝 (备忘录) :memo: 撰写文档
🚀 (火箭) :rocket: 部署功能
💄 (口红) :lipstick: 更新 UI 和样式文件
🎉 (庆祝) :tada: 初次提交
✅ (白色复选框) :white_check_mark: 增加测试
🔒 (锁) :lock: 修复安全问题
🍎 (苹果) :apple: 修复 macOS 下的问题
🐧 (企鹅) :penguin: 修复 Linux 下的问题
🏁 (旗帜) :checked_flag: 修复 Windows 下的问题
🔖 (书签) :bookmark: 发行/版本标签
🚨 (警车灯) :rotating_light: 移除 linter 警告
🚧 (施工) :construction: 工作进行中
💚 (绿心) :green_heart: 修复 CI 构建问题
⬇️ (下降箭头) :arrow_down: 降级依赖
⬆️ (上升箭头) :arrow_up: 升级依赖
👷 (工人) :construction_worker: 添加 CI 构建系统
📈 (上升趋势图) :chart_with_upwards_trend: 添加分析或跟踪代码
🔨 (锤子) :hammer: 重大重构
➖ (减号) :heavy_minus_sign: 减少一个依赖
🐳 (鲸鱼) :whale: Docker 相关工作
➕ (加号) :heavy_plug_sign: 增加一个依赖
🔧 (扳手) :wrench: 修改配置文件
🌐 (地球) :globe_with_meridians: 国际化与本地化
✏️ (铅笔) :pencil2: 修复 typo

参考

MYSQL事务隔离级别

Mysql有四种事务隔离级别,分别是:
Read Uncommitted
Read Committed
Repeatable Read
Serializable

关于这四个隔离级别的介绍:

未提交读(READ UNCOMMITTED)。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。

提交读(READ COMMITTED)。本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)。

可重复读(REPEATABLE READ)。在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象(稍后解释)。

串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥。

这四个级别逐渐增强,每个级别解决一个问题

脏读,最容易理解。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。

不重复读。解决了脏读后,会遇到,同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致。

幻读。解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会“惊奇的”发现了这些新数据,貌似之前读到的数据是“鬼影”一样的幻觉。

可以通过select @@tx_isolation来查看当前隔离级别

通过set [ global | session ] transaction isolation level Read uncommitted | Read committed | Repeatable | Serializable来修改事务隔离级别

如果选择global,意思是此语句将应用于之后的所有session,而当前已经存在的session不受影响。

如果选择session,意思是此语句将应用于当前session内之后的所有事务。

如果什么都不写,意思是此语句将应用于当前session内的下一个还未开始的事务。

换上了用Let's Encrypt Closed Beta自己签发的证书

先上图。两个小时的成果。
letsencrypt

在10月31的时候就收到了letsencrypt发的 Let’s Encrypt Closed Beta Invite‏ 说我的tms.im和www.tms.im已经加入内测白名单。可惜这封邮件被“智能”的丢进了垃圾邮件文件夹。导致我今天才看到。于是来折腾一下。

在开始之前先严重吐槽下,可能是由于内测或者该计划刚开始还不完善的原因。这个东西特别特别特别不亲民。我搞了两个多小时才签了一个证书出来。突然发现只签了tms.im没签www.tms.im。于是又用了40分钟的时间重新签了这两个。外加这货的有效期只有三个月,也就是说三个月以后我要重新签。在现在年付的付费证书已经这么便宜的情况下。到底letsencrypt有没有竞争力。

下面正文。

由于现在还是内测。官方在邮件里提供了一个内测用的API路径。

To use Let’s Encrypt’s official client to obtain your real
certificates, you will need to provide the production API URL on the
command line:

https://acme-v01.api.letsencrypt.org/directory

还提供了软件

git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt
./letsencrypt-auto --agree-dev-preview --server
https://acme-v01.api.letsencrypt.org/directory auth

但是装好软件以后运行./letsencrypt-auto报错。

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-Wa1Lwq/ConfigArgParse

去官方论坛https://community.letsencrypt.org/看了一下说只支持python2.7,不支持2.6。 好吧我centos6.7默认是2.6的python。也懒得搞成2.7了,于是另开了一台centos7的服务器来签发。

执行./letsencrypt-auto --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory auth -d tms.im -d www.tms.im

   报错The program nginx (process ID 7176) is already listening on TCP port 
    80. This will prevent us from binding to that port. Please stop the  
    nginx program temporarily and then try again.      

提醒我80端口被占用。验证个域名所有权还要关80端口额。还好只是个个人博客。如果我是生产用的域名和服务器怎么办。总不能停机去搞域名验证吧。

暂时停了80的nginx。终于进到了下一步。然后又开始报错

Failed authorization procedure. tms.im (dvsni): unauthorized :: The client lacks sufficient authorization :: Correct zName not found for TLS SNI challenge

只好切换用手动模式:后面加一个
-a manual

手动模式告诉我需要自己开一个 SimpleHTTPServer 来搞验证,并且给了我一堆指令。我以为它会自动开呢。用netstat看了一下,果然没有自动开。好吧我只好手动调用python开一个SimpleHTTPServer 。然后按照每次验证它给的路径和文件名和文件内容创建对应的验证文件。

当我以为这样就可以通过验证的时候,letsencrypt告诉我还是tooyoung

:: The client lacks sufficient authorization :: Invalid response from http://tms.im/.well-known/acme-challenge/g2eyBgAf3Z-NpzBAzTrvxopLxJ1PH1I8lt7sqiA01sQ [103.242.111.8]

什么。不是已经开好SimpleHTTPServer 了么。我的SimpleHTTPServer 里也有access log啊。我在浏览器打开也可以访问啊。再仔细一看IP是103.242.111.8。这,我不是改了DNS指向验证用的这台服务器了么,为什么非要去以前的服务器验证。

可能是远程API那边DNS更新比较慢吧。可是我用nslookup看了一下主要的几个DNS服务器都更新过了啊。鬼知道他们用的什么DNS,或者是就是有机制让你不能改IP验证?谁知道呢。只好在以前的服务器上也开了个SimpleHTTPServer,放入同样的验证文件。再进行验证。

这下终于生成成功了。最新的证书和私钥都在/etc/letsencrypt/live/$domain里面。内测阶段只能签三个月的证书。

/etc/letsencrypt/keys里面存的是所有的包括之前和最新的证书。

最好把整个/etc/letsencrypt目录都备份一下,里面是包括邮箱和token之类的东西。以后renew的时候用的。

折腾了两个多小时签发出第一张免费证书。letsencrypt还有很长的路要走啊。

利用prism.js实现代码高亮

发现一个小巧高效的代码高亮js,官网地址
http://prismjs.com/

Prism is a lightweight, extensible syntax highlighter, built with
modern web standards in mind. It’s a spin-off from Dabblet and is
tested there daily by thousands.

<link href="themes/prism.css" rel="stylesheet" />
<script src="prism.js"></script>

定制后加载到主题。
因为本站采用了instantclick做pjax加速,需要在触发pjax的时候同时触发高亮渲染。去instantclick官网的doc里找到

InstantClick fires four events to provide hooks into the lifecycle of
the page:

change : The page is changed, also triggered on initial page load and
if InstantClick is not supported. Replaces DOMContentLoaded.

Its callback can take an optional isInitialLoad argument, it’s a
Boolean that will be true when it’s the initial page change or when
InstantClick isn’t supported, and false when InstantClick changes the
page.

fetch : A page starts preloading.

receive : A page has been preloaded. You can modify its content.

wait : The user has clicked on a link, but the page isn’t preloaded
yet. Only triggers when the page isn’t displayed instantly.

这里采用on change触发InstantClick.on(‘change’, yourCallback);代码如下

InstantClick.on('change', function() {
    self.Prism.highlightAll(event);
});
InstantClick.init();

测试一下,一切正常。