极简设计

idea 2016-01-01

idea 生成serialVersionUID两个方法 一、 File => Settings... => Inspections => Serialization issues => Serializable class without 'serialVersionUID' 在类名上点一下,alt+cneter 二、 GenerateSerialVersionUID插件 alt+insert

Read More


术业有专攻,让我们回归本位 2016-01-01

整个中国的IT产业链处于一种极度不健康的循环中,N多的公司在做N多相同的事情,整个产业链处于一种严重的资源浪费和恶性竞争中 你有平台,我也有平台,请允许我在这里用“平台”这个词,但是这个词实在是被用烂了,一个总共只有10来个人的公司,刚成立了2、3年的,也号称有自己的“平台”!尤其是现在叫的最多的“开发平台”,大公司在做,小公司也在做 拿基于J2EE的企业应用领域来说,一个基于J2EE的开发平台,至少要有:门户(portal其实是两个概念)、cms、workflow、report、eform、组织机构、权限体系、数据建模、快速开发工具(代码生成),高级一点的平台还有ESB、BI等 这些子系统,每一个要想做好做精都需要投入很大的人力、财力去研发。别说一个小公司,就是一个中等规模的公司要想做好也是很难的

而现状是:2、3个人基于Spring、Struts1(Struts2、webwork)、hibernate、ajax拼装一下,然后再做个代码生成的工具,OK,这时就号称是平台了

还有工作流领域,调用了几个JBPM的API接口,看了一些JBPM的源码,就想搭建自己的工作流管理系统。这些都是目前整个产业链不成熟, IT人士浮躁的表现。你有工作流,我也有工作流,你有开发平台,我也有开发平台,你有打包的产品型OA,我也有(例如:金和OA,领航OA,OA2008等等),大家去网上搜一搜“开发平台”、“工作流”、“OA”就知道现在这些东西是何其的多了,而且还何其的相似

卖产品,做项目,你便宜我比你还便宜,互相之间恶性竞争。不知道,大家注意没有,所有的东西都在涨价,猪肉,青菜都在涨,可唯独只有软件产品和项目的金额在降,你10万能做,我8万就能做,你8万我甚至5万就能做,结果导致赔钱做项目,卖产品的可能还稍微好一点

其实这也从另一个方面也说明了中国的用户也基本处在比较低级的层次,你想想,一个几万块钱做出来的项目能用么,肯定是一堆垃圾呀。没有项目做,着急,可是有了项目做,却在赔钱!

经济危机后倒闭一批公司,其实这样是好事,那些要技术没技术,要业务没有业务只会模仿跟风的公司就应该让它倒闭,让它退出整个产业链

术业有专攻,专业的队伍做专业的事情,找准自己的定位,做自己最擅长的事情。整个业内也应该建立一种良好的秩序,有做工作流的,有做portal的,有做cms的,有做报表的,有做BI的 每个公司都应该做自己擅长的事情,然后互相合作,而不是互相的恶性竞争,再出现几个比较大的集成商,来负责去整合产业链内产品,去建立自己的实施队伍,从而也保证了实施和服务的质量,而产品型的公司,只负责不停地研发改进产品,不停地为集成商提供服务就可以了

以前总有人在说做产品必须直接和最终用户打交道,才知道他们真实地使用感受,只有做最终用户的产品才能赚钱,为什么会这样呢,说到底还是整个产品链的不健康,不成熟,因为现状是,集成商在尽可能地压榨产品提供商的利润,而产品提供商被压榨的只剩一点微薄的利润,这就导致了产品提供商也不能专心地去做自己的产品,而转向也做最终用户的项目,但是小公司又没有大公司的那些优势,那怎么办,只有在价格上做恶性的竞争,结果这又导致了,大公司也没办法了,也要降低自己的价格,就这样整个的恶性的循环就这样形成了

一个售前的CASE

这是一个项目型的公司,领导说现在有一个要用到工作流引擎的项目,然后技术经理A,你去考察一下工作流的产品吧 这时技术经理A就说了,找什么工作流产品呀,我们自己用JBPM搞就可以了,现在那东西免费开源,很成熟,很简单,结果领导一听好呀,这东西不要钱(呵呵,领导最关注的是钱)真是好事,你搞吧 结果技术经理A就领着1个兄弟开干了,结果一个月后,领导问怎么样了,答曰:基本的功能可以了,但是有几个特殊的功能还要扩展,2个月后,领导又问,怎么样了,答曰,这几个扩展功能不太好整呀,此时已经用掉了4人月,4人月,做过项目的人都知道,一个5000月薪的程序员他的月成本就要到至少8000,4×8000=32000 一个小项目的总额度能有多少呢,只是一个技术性的工作流引擎就耗掉了这些成本(为什么说只是一个技术性的工作流引擎,因为它仅仅实现了流程的自动化导航,任务自动分配,包括国内的几大工作流引擎厂商的产品也基本停留在这个阶段,笔者或专门写文正来阐述工作流的应用现状),现在该领导着急了,不要做了,给我停,继续考察工作流引擎的产品,结果找到我们,因为我们成熟的工作流引擎产品的简化版也不过2-3万,而且还有专业的技术团队做服务!

这个例子就是为了说明,术业有专攻,专业的事情有专业的团队去做,上面的这个公司,其实他的特长在于对于某个行业的业务熟悉性,那么好它就专职做好自己的行业业务就可以了。“平台”也是同样的道理:门户想自己做,cms要自己做、workflow要自己做、report要自己做、BI也要自己做,谁的都不想用,都自己做。N多的公司都在走这样的路线,结果就导致了,整个产业链内,很多都在做重复的事情,互相之间没有任何的合作,因为你有,我也有,当然就不需要合作了,只有竞争

术业有专攻,就从自我做起吧,下一步易维就是要回归本位,回到专业的企业业务流程解决方案咨询商、培训商、提供商的定位上来

Read More


https 2016-01-01

Nginx 1.9.5 支持HTTP/2

http2主要以SPDY技术为主,为提升访问速度降低加载时间(多路复用/优先级),提高安全性(TLS) 一个TCP连接即可传送网页内容及图片等资源 ,传输内容也均以gzip或DEFLATE格式压缩(可包括HTTP头) 还可以主动推送内容

互联网基本协议:TCP/IP,应用层协议HTTP带来web,使互联网发扬光大 SPDY改动的是HTTP而不是TCP/IP,改变HTTP只需更新Browser和web server就行了,而改动TCP/IP就困难多了,牵扯面太广,需要更新巨量的路由器,服务器和客户端的操作系统

SPDY介于HTTP和TCP之间,处于Session layer,其下面还强制使用SSL,增强数据安全性

SPDY带来巨大的副作用,用SSL后,GFW 等,无法识别数据内容进行过滤。这可能是SPDY设计者的一个重要目的,但出于某种原因,他们没说出来。不知道,SPDY会不会因此在一些国家被禁用

  • 多路复用:多个请求整合至一个TCP连接之中,减少重连接数量提升效率 http201

  • 串流优先级:客户端可以指定哪些资源请求可被优先处理

  • 压缩Header:减小header的体积
  • 主动推送:在客户端尚未对某些资源发出请求时,服务器可以抢先作出回应。(当前Nginx和CF并未实施,未来会加入这一功能。)

主流浏览器均要求 HTTP/2 被包裹在 TLS 中,因此,HTTP/2 + TLS(HTTPS)已是事实上的标准

演示对比:HTTP/1.1 VS HTTP/2

https://www.cloudflare.com/http2/

https://okwoo.com/cloudflare-http-2-and-enabled-spdy


Nginx for Windows With http_v2_module 编译日记

http://bilibibi.me/88


HSTS:HTTP Strict Transport Security,安全功能,告诉浏览器只能通过HTTPS访问当前资源, 禁止HTTP方式,很大程度抵御SSL剥离攻击

用户输入http://foo.com或者直接foo.com存在中间人攻击潜在威胁,跳转过程可能被恶意网站利用来直接接触用户信息,而不是原来的加密信息,如在nginx让http页跳到https 的302重定向过程中可能会被劫持篡改

有了HSTS标识这个网站禁止使用HTTP方式加载,浏览器应该自动把所有尝试使用HTTP的请求自动替换为HTTPS请求


现代浏览器支持一些安全相关的响应头,只需要修改服务器(nginx)配置即可,不需要修改程序代码,成本很低 https://linux.cn/article-5847-1.html


HTTPS,也称HTTP over TLS TLS是传输层加密协议,前身是SSL协议

TLS 1.0通常被标示为SSL 3.1 TLS 1.1为SSL 3.2 TLS 1.2为SSL 3.3

tcp-ip-model

Handshake/Change Ciper Spec/Alert protocol组成SSL Handshaking Protocols

TTPS和HTTP协议相比提供了

  • 数据完整性:内容传输经过完整性校验
  • 数据隐私性:内容经过对称加密,每个连接生成一个唯一的加密密钥
  • 身份认证:第三方无法伪造服务端(客户端)身份

数据完整性和隐私性由TLS Record Protocol保证 身份认证由TLS Handshaking Protocols实现

RSA算法SSL握手过程

ssl_handshake_rsa

1.[明文] 客户端发送随机数client_random和支持的加密方式列表 2.[明文] 服务器返回随机数server_random,选择的加密方式和服务器证书链 3.[RSA] 客户端验证服务器证书,使用证书中的公钥加密premaster secret发送给服务端 4.服务端使用私钥解密premaster secret 5.两端分别通过client_random,server_randompremaster secret生成master secret,用于对称加密后续通信内容


证书

cert

主要包含

  • 证书信息:过期时间和序列号
  • 所有者信息:姓名等
  • 所有者公钥

为什么服务端要发送证书给客户端

互联网证书无限,客户端(os或浏览器等)无法内置所有证书,需要通过服务端将证书发送给客户端

客户端为什么要验证接收到的证书

防中间人攻击

客户端<------------攻击者<------------服务端 伪造证书 拦截请求

客户端如何验证接收到的证书

引入数字签名

|txt|----哈希---->| 消息摘要 |----私钥加密---->| 数字签名 |

将一段文本通过哈希(hash)生成消息摘要,将其用私钥加密后生成数字签名

假设消息传递在Alice,Bob,和Pat三人之间发生 Alice将消息连同数字签名一起发送给Bob,Bob接收到消息后,可以这样验证接收到的消息就是Alice发送的

1.Bob也用哈希对Alice发的消息计算出消息摘要1 2.Bob用Alice公钥解密数字签名得到消息摘要2 消息摘要1=消息摘要2,即消息就是Alice发送

前提是Bob知道Alice的公钥。更重要的是,和消息本身一样,公钥不能在不安全的网络中直接发送给Bob

此时要引入证书颁发机构(CA),CA数量并不多,Bob客户端内置所有受信任CA的证书 CA对Alice的公钥(和其他信息)数字签名后生成证书 Alice将证书发送给Bob后,Bob通过CA证书的公钥验证证书签名。 Bob信任CA,CA信任Alice 使得 Bob信任Alice,信任链(Chain Of Trust)就是这样形成的 事实上,Bob客户端内置的是CA的根证书(Root Certificate),HTTPS协议中服务器会发送证书链(Certificate Chain)给客户端


TLS协议

TLS协议包括TLS Record Protocol和TLS Handshake Protocol 总览中的流程图仅涉及到TLS Handshake Protocol

TLS Record Protocol

在TLS协议中,有四种子协议运行于Record protocol之上

  • Handshake protocol
  • Alert protocol
  • Change cipher spec protocol
  • Application data protocol

Record protocol起到了这样的作用

  • 在发送端:将数据(Record)分段,压缩,增加MAC(Message Authentication Code)和加密
  • 在接收端:将数据(Record)解密,验证MAC,解压并重组

值得一提的是,Record protocol提供了数据完整性和隐私性保证,但Record类型(type)和长度(length)是公开传输的

Record Protocol有三个连接状态(Connection State),连接状态定义了压缩,加密和MAC算法 所有的Record都是被当前状态(Current State)确定的算法处理的。

TLS Handshake Protocol和Change Ciper Spec Protocol会导致Record Protocol状态切换。

empty state --------------------> pending state ----------------------> current state Handshake Protocol Change Cipher Spec

初始当前状态(Current State)没有指定加密,压缩和MAC算法,因而在完成TLS Handshaking Protocols一系列动作之前,客户端和服务端的数据都是明文传输的 当TLS完成握手过程后,客户端和服务端确定了加密,压缩和MAC算法及其参数,数据(Record)会通过指定算法处理。

其中,Record首先被加密,然后添加MAC(message authentication code)以保证数据完整性

TLS Handshaking Protocols

Handshakeing protocols包括Alert Protocol,Change Ciper Spec Protocol和Handshake protocol 本文不详细介绍Alert Protocol和Change Ciper Spec Protocol


ssl_handshake_rsa2

客户端和服务端在握手hello消息中明文交换了client_random和server_random,使用RSA公钥加密传输premaster secret 最后通过算法,客户端和服务端分别计算master secret 其中,不直接使用premaster secret的原因是:保证secret的随机性不受任意一方的影响

除了使用RSA算法在公共信道交换密钥,还可以通过Diffie–Hellman算法 Diffie–Hellman算法原理

Diffie-Hellman_Key_Exchange

使用Diffie–Hellman算法交换premaster secret的流程 ssl_handshake_diffie_hellman

小结

TLS Handshaking Protocols协商了TLS Record Protocol使用的算法和所需参数,并验证了服务端身份 TLS Record Protocol在协商后保证应用层数据的完整性和隐私性

TLS Handshaking Protocol的核心是在公开信道上传递premaster secret

ECDH 握手

大型网站的 HTTPS 实践(一)—— HTTPS 协议和原理 http://op.baidu.com/2015/04/https-s01a01/

火狐浏览器分割HTTPS报文的问题,BEAST攻击 BEAST能够从SSL/TLS加密的会话中获取受害者的COOKIE值(通过进行一次会话劫持攻击)。 http://www.unclekevin.org/?p=38

常见的HTTPS攻击方法 http://drops.wooyun.org/tips/4403

https://www.google.com.tw/search?q=CRIME%E6%94%BB%E5%87%BB&oq=CRIME%E6%94%BB%E5%87%BB&aqs=chrome..69i57&sourceid=chrome&es_sm=0&ie=UTF-8

SSL/TLS协议的演化 2 – 准备知识 http://www.unclekevin.org/?p=555

SSL/TLS 协议族及其应用 http://www.unclekevin.org/?p=568

SSLv3 Record层 http://www.unclekevin.org/?p=577

SSLv3 握手协议 http://www.unclekevin.org/?p=587 TLS 1.0协议改进 http://www.unclekevin.org/?p=597 TLS 1.1协议改进 (应对CBC攻击) http://www.unclekevin.org/?p=601 TLS 1.2协议改进 http://www.unclekevin.org/?p=605 TLS 1.3 最新草案 http://www.unclekevin.org/?p=613 安全问题概述 http://www.unclekevin.org/?p=619 SSLv3的POODLE攻击 http://www.unclekevin.org/?p=623 安全问题–BEAST攻击 http://www.unclekevin.org/?p=630 安全问题 – CRIME和BREACH缺陷 http://www.unclekevin.org/?p=640 安全问题 – 先加密还是先认证? http://www.unclekevin.org/?p=676 安全问题 – RC4的谢幕 http://www.unclekevin.org/?p=701 安全问题–版本降级(Version Rollback) 攻击 http://www.unclekevin.org/?p=709 安全问题–TLS重协商(Renegotiation)攻击 http://www.unclekevin.org/?p=713 安全问题–Lucky 13 Attack http://www.unclekevin.org/?p=720 QUIC http://www.unclekevin.org/?p=887

https://ma.ttias.be/enable-http2-in-nginx/ https://www.futures.moe/writings/configure-nginx-with-security-and-effective-yes-or-no.htm https://www.futures.moe/writings/configure-nginx-with-security-and-effective-yes-or-no.htm

Read More


httpclient 2016-01-01

使用httpclient必须知道的参数设置及代码写法、存在的风险 http://jinnianshilongnian.iteye.com/blog/2089792

HttpClient 3 4.3 区别

3 HttpClient httpClient=new DefaultHttpClient(); 4.3 CloseableHttpClient httpClient = HttpClients.createDefault();

3.X的超时设置方法 HttpClient client = new HttpClient(); client.setConnectionTimeout(30000); client.setTimeout(30000); HttpClient httpClient= new HttpClient(); httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000); 4.X版本的超时设置(4.3后已过时) HttpClient httpClient=new DefaultHttpClient(); httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,2000);//连接时间 httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,2000);//数据传输时间 4.3版本超时设置 CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet httpGet=new HttpGet("http://www.baidu.com");//HTTP Get请求(POST雷同) RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();//设置请求和传输超时时间 httpGet.setConfig(requestConfig); httpClient.execute(httpGet);//执行请求 4.3版本不设置超时的话,一旦服务器没有响应,等待时间N久(>24小时)。

HttpConnection没有连接池的概念,多少次请求就会建立多少个IO,高并发下O可能会耗尽

httpclient 3


<dependency>
    <groupId>commons-httpclient</groupId>
    <artifactId>commons-httpclient</artifactId>
    <version>3.1</version>
</dependency>
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
HttpClient client = new HttpClient(connectionManager);
// and then from inside some thread executing a method
GetMethod get = new GetMethod("http://hc.apache.org/");
try {
    client.executeMethod(get);
    // print response to stdout
    // System.out.println(get.getResponseBodyAsStream());
    System.out.println(get.getResponseBodyAsString());
} finally {
    // be sure the connection is released back to the connection  manager
    get.releaseConnection();
}

多线程环境下应该使用一个全局单例的HttpClient,并且使用MultiThreadHttpConnectionManager来管理(复用)Connection

两种连接池

全局的ConnectionPool,每主机(per- host)HostConnectionPool maxHostConnections就HostConnectionPool中表示每主机可保持 连接的连接数 maxTotalConnections是ConnectionPool中可最多保持的连接数

每主机的配置类是 HostConfiguration,HttpClient有个int executeMethod(final HostConfiguration hostConfiguration, final HttpMethod method)方法可以指定使用哪个HostConfiguration,不过多数情况都是不显示指定HostConfiguration,这样 httpclient就用了默认的HostConfiguration=null,也就是说所有的请求可以认为指自同一个主机

如果不设置这两个参 数 默认的maxHostConnections大小只有2,也就是说,并发8个线程请求数据时,实际上会有6个线程处于等待被调度 maxTotalConnections默认20

MultiThreadedHttpConnectionManager,看名字好像是多线程并发请求似的, 其实不是,但它也确实用到了多线程,那是在发现连接不够用时起个等待线程wait信号,这个名称的含义应该是多线程情况线程安全的 HttpConnectionManager

httpclient 4.0 连接池

import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

public class HttpConnectionManager {

    private static HttpParams httpParams;
    private static ClientConnectionManager connectionManager;

    /**
     * 最大连接数
     */
    public final static int MAX_TOTAL_CONNECTIONS = 800;
    /**
     * 获取连接的最大等待时间
     */
    public final static int WAIT_TIMEOUT = 60000;
    /**
     * 每个路由最大连接数
     */
    public final static int MAX_ROUTE_CONNECTIONS = 400;
    /**
     * 连接超时时间
     */
    public final static int CONNECT_TIMEOUT = 10000;
    /**
     * 读取超时时间
     */
    public final static int READ_TIMEOUT = 10000;

    static {
        httpParams = new BasicHttpParams();
        // 设置最大连接数
        ConnManagerParams.setMaxTotalConnections(httpParams,
                MAX_TOTAL_CONNECTIONS);
        // 设置获取连接的最大等待时间
        ConnManagerParams.setTimeout(httpParams, WAIT_TIMEOUT);
        // 设置每个路由最大连接数
        ConnPerRouteBean connPerRoute = new ConnPerRouteBean(
                MAX_ROUTE_CONNECTIONS);
        ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);
        // 设置连接超时时间
        HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT);
        // 设置读取超时时间
        HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory
                .getSocketFactory(), 80));
        registry.register(new Scheme("https", SSLSocketFactory
                .getSocketFactory(), 443));

        connectionManager = new ThreadSafeClientConnManager(httpParams,
                registry);
    }

    public static HttpClient getHttpClient() {
        return new DefaultHttpClient(connectionManager, httpParams);
    }

}

每个路由(route)最大连接数

运行环境机器 到 目标机器的一条线路 用HttpClient的实现来分别请求 www.baidu.com 的资源和 www.bing.com 的资源会产生两个route 默认值为2,如果不设置这个参数值默认情况下对于同一个目标机器的最大并发连接只有2个!哪怕设置连接池的最大连接数为200,但是实际上还是只有2个连接在工作,其他剩余的198个连接都在等待

httpclient 4.0 后

从4.0版本之后ConnManagerParams被Deprecated ConnManagerParams的功能被挪到了 ThreadSafeClientConnManager 和 HttpConnectionParams两个类

httpclient 4.3 常用代码

CloseableHttpClient httpClient = HttpClients.createDefault(); String url = "http://www.baidu.com";

Get

httpClient.execute(new HttpGet(url));

响应

响应码

CloseableHttpResponse response = httpClient.execute(new HttpGet(url)); response.getStatusLine().getStatusCode()

响应的媒体类型

String contentMimeType = ContentType.getOrDefault(response.getEntity()).getMimeType(); 常量值 ContentType.TEXT_HTML.getMimeType()

响应Body

String bodyAsString = EntityUtils.toString(response.getEntity());

Post

httpClient.execute(new HttpPost(url));

重定向

CloseableHttpClient instance = HttpClientBuilder.create().disableRedirectHandling().build(); CloseableHttpResponse response = instance.execute(new HttpGet(url));

HEAD

HttpGet request = new HttpGet(url); request.addHeader(HttpHeaders.ACCEPT, "application/xml");

响应head

Header[] headers = response.getHeaders(HttpHeaders.CONTENT_TYPE); for (int i = 0; i < headers.length; i++) { System.out.println(headers[i]); }

允许所有证书

SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( builder.build()); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf).build();

HttpGet httpGet = new HttpGet("https://www.google.com");
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
    System.out.println(response.getStatusLine());
    HttpEntity entity = response.getEntity();
    EntityUtils.consume(entity);
} finally {
    response.close();
}

#

推荐使用HttpEntity的getConent()方法或者HttpEntity的writeTo(OutputStream)方法来消耗掉Http实体内容 当响应不太大,可用EntityUtils里的静态工具方法以字符串或者字节数组的形式读取Http实体 CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if (entity != null) { long len = entity.getContentLength(); if (len != -1 && len < 2048) { System.out.println(EntityUtils.toString(entity)); } else { // Stream content out } } } finally { response.close(); }

HttpClient 4.3教程 http://www.yeetrack.com/?p=779

Read More


http 2016-01-01

HTTP(HyperText Transfer Protocol)翻译之争

制定者提出transfer不是传输的意思,是针对资源的动作,而不是传输数据的含义 图灵社区翻译大讨论多数人认为应该翻译成“转移”,URL这个资源标识符在浏览器,Cache,Proxy,Server等实体中的转移和操作(manipulation)

不少WWW应用中违背RESTful设计的地方

1.Cookie之中携带状态信息违背了REST 2.返回客户的页面中内嵌Java Applet应用,使得客户端也有智能了,也违反了REST等等

HTTP/2

新事物诞生多源于旧事物的痛点 HTTP1.X作为WWW的基础,支撑Web多年。在线高清视频,网页游戏用户体验都很好 HTTP1.X设计上的痛点,目前通过折中方式绕过 HTTP/2的引入可以归功于谷歌对完美的极致的追求

1.严格的B/S模式

Server端不能主动发起通知。开发者要引入一些复杂折中机制 如AJAX, long poll,web socket

2.不支持真正的基于一个连接的并行多会话 这会影响处理速度,对于google这样的巨型应用服务商,网页加载速度慢一点都会带来巨额广告损失

3.对于数据中心,如果协议更轻便,单台物理服务器支撑更多并发连接,数据中心在耗电量不变的前提下可以提供更大规模应用支持,直接提高营收。

4.协议头部的数据冗余

Google有高水平开发人才,创新基因和占垄断地位的Web应用和Chrome,等待又意味着金钱的流失 于是挽袖子干,升级Chrome,大规模在Google应用部署了SPDY(类似HTTP/2的协议),Twister等厂商也跟进。挟天子以令诸侯,赶IETF上架。如果没有google的努力,HTTP1.X的时代会延续更久

HTTP/2标准的发展–HTTP Server Push

www的RESTful风格严格基于C/S模型。未想到过支持Server发起请求

1.Client Poll,两种poll方式

1.1 Client定时向Server询问是否有事件

1.2 Piggyback Server端将事件累积起来,在下一次应答Client请求的时候捎带送回去。有点像Linux异常的实现,在下一次系统调用的时候捎带把signal送回去。 这两种都是方式都是模拟,缺点显而易见

2.Server Push

Server Pull的目的是即时将消息发送到Client。基于现有HTTP 1X有两种hack机制:long poll和HTTP streaming

2.1 Long polling

long polling机制是:

1.Client发起一个请求,目的是创建一个控制通道。

2.server端保持这个请求,不应答,直到有事件产生,server发送response通知client

3.一次事件通知后,连接关闭。client发起一个新的请求,重建控制通道,等待下次事件

简言之,Client每个poll至少收到一个event响应(server也可以积攒多个事件,一次发送)

2.2 Streaming

Stream利用了协议设计中的奇淫巧计,算是HTTP1X下hack方式Server Push实现的精华了。可以在同一连接,分多个部分发送HTTP response,我能立刻想到是chunked编码

Server一直不发送last chunk,有事件就插入到chunk data通知Client,可以一直插入新的chunk data。这样一个HTTP连接可以用于多次事件通知,这也是streaming和long poll这两个名词的区别所在。

如果不理解chunked编码,可以参考我的一篇关于Chunked的日志:“HTTP Chunked Body/Trailer编码”

2.2.1.MIME Multipart

是streaming的一种实现。MIME Multipart本质上和chunk类似,只是在MIME中。不是HTTP机制。一个MIME内可以带若干个Message,Message之间使用叫做boundary的字符串分开。在MIME结束的地方,会有一个结尾boundary的特殊字符串,类比HTTP chunk编码的last chunk。

Mixed-Replace这种MIME type,底层传输也是基于HTTP chunked 编码。它的引入就是为了server push。这类multipart之中的所有单独的message都是针对同一个对象的信息。新message替换旧的。可以想象,把电影胶片有序排列,每幅胶片通过一个message传输,更新HTML的某个图像框,multipart MIME一气发送下来,展现出来的就是一部影片了

3.支持Server Push的新协议

3.1 Server-Sent Events(SSE)

是W3C的标准,与其说是全双工协议,不如说是long polling的标准化。看了协议描述,SSE同前文提到的multipart/x-mixed-replace是本质一样的

a)定义了标准的MIME type “text/event-stream ”

b)定义了消息格式,如下面

data: This is the first message. data: This is the second message, it data: has two lines. data: This is the third message. data: This is the first message. data: This is the second message, it data: has two lines.

data: This is the third message. c)标准化了前文例子中的keep alive

使用':’开头的数据,表示comments,每隔一段时间发送一次,作为keep-alive

d)建议disable chunked编码,可以独立于chunked编码工作。

SSE并非新协议,附属于HTTP1.X,没有被IETF标准化。

3.2 WebSockets

websocket独立实现了完全的Server Push。是HTML5中提出,并最终独立发展的全双工协议,恰如其名,web上面的socket

和streaming类似,websocket是client发起的长连接。但C/S双方可以随时互发信息,独立于HTTP,不受拘束

我比较奇怪的是HTML5这种信息表示层的规范会涉及通信协议的定义,可能和Google的SPDY一样,为了解决问题,赶IETF上架


HOL 及其它协议缺陷 HOL(head-of-line blocking) HTTP是一种顺次处理的ping-pang协议(ping即HTTP request,pang即HTTP response)。HOL指同一连接上,请求有序处理,即便请求之间没有关联性。

HOL是下面HTTP1.X的已有技术的传承:

1.1.HTTP1.0

每个连接一对request和response,然后关闭连接。

1.2.HTTP 1.1 Persistent connection

应用可以复用一个HTTP连接,多次ping-pang,时序是:

C2S: req1 req2 req3 S2C: resp1 resp2 resp3

进行完一对ping-pang,client才能发起新的请求。

在第一次听说persistent connection的时候我有如下疑问:

A).Client和Server之间如何协商Persistent Connection?

对于HTTP1.1,persistent connection是缺省行为。如果client不支持,需要在request中显示添加 connection : close字段。

B).谁来关闭Persistent Connection?

Client和Server都有权关闭persistent connection。

任何包括“connection:close”字段的HTTP消息(无论请求应答),都表示当前的ping-pang是最后一对了。

1.3 HTTP 1.1 Pipe line

client可以一次发送一批request,可能的时序如下:

C2S: req1 req2 req3 req4 req5 req6 S2C: resp1 resp2 resp3 resp4 resp5 resp6

这是HTTP 1.1协议下看起来最并行的情景了。即便这样,如果req1没有处理完,server也不会处理req2。即,pipeline的情况下也有HOL问题。

1.4 解决办法

Head-of-line blocking在HTTP1.1中是协议缺陷,不修改协议的情况下只能并发多个HTTP连接。

如果可以对协议进行修改,最好可以实现类似下面这样的完全无序并发:

C2S: req1 req2 req3 req4 req5 req6 S2C: resp3 resp1 resp4 resp6 resp5 resp2

2.不是缺陷的缺陷

【1】还提到了一些其它HTTP1.X的问题,但我觉得都不是事儿,设计者的出发点也不是协议功能。

其一是HTTP报文头部冗余。协议建议进行首部压缩,这句话对于任何协议都适用。首部都压缩后,我们网络工程师调试困难多多,还怎么构造文本直接通过telnet测试。安全网关处理这些信息,不得不分配内存解压缩,劳神费力易出错。

其二是引入了流控以及请求优先级的概念,都是协议增强,和缺陷无关。


SPDY事实标准 http://www.unclekevin.org/?p=289


(SM,NF,WAKA) http://www.unclekevin.org/?p=320

HTTP/2标准现状 http://www.unclekevin.org/?p=352

HTTP/2安全考量 http://www.unclekevin.org/?p=394

HTTP 头部压缩 http://www.unclekevin.org/?p=402

实现分析 http://www.unclekevin.org/?p=418

Read More


html5 离线缓存 2016-01-01

html5通过application cache API提供离线存储功能,前提是需要访问的web页面至少被在线访问过一次

一、离线本地存储和传统的浏览器缓存区别

浏览器缓存主要包含两类:

  • 缓存协商:Last-modified,Etag

浏览器向服务器询问页面是否被修改过,如果没有修改就返回304,浏览器直接浏览本地缓存文件,否则服务器返回新内容

  • 彻底缓存:cache-control,Expires

通过Expires设置缓存失效时间,失效之前不再跟服务器请求交互

  • 离线存储为整个web提供服务,浏览器缓存只缓存单个页面
  • 离线存储可以指定需要缓存的文件和哪些文件只能在线浏览,浏览器缓存无法指定
  • 离线存储可以动态通知用户进行更新

二、如何实现

离线存储是通过manifest文件来管理的,需要服务器端的支持,不同的服务器开启支持的方式也是不同的

CACHE MANIFEST//必须以这个开头
version 1.0 //最好定义版本,更新的时候只需修改版本号
CACHE:
    m.png
    test.js
    test.css
NETWORK:
    *
FALLBACK
    online.html offline.html

CACHE指定需要缓存的文件 NETWORK指定只有通过联网才能浏览的文件,*代表除了在CACHE中的文件 FALLBACK每行分别指定在线和离线时使用的文件 要让manifest管理存储,还需要在html标签中定义manifest属性,如下:

<!DOCTYPE HTML> <html lang="en" manifest='test.manifest'> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> </html>

最后别忘了,这些应用需要服务器支持

Apache服务器开启支持的方式是:在conf/mime.types中添加一段代码:

test/cache-manifest manifest

IIS服务器开启方式:

右键--HTTP---MIME映射下,单击文件类型---新建---扩展名输入manifest,类型中输入test/cache-manifest

三、通过JS动态控制更新

applicationCache对象提供个了一些方法和事件,管理离线存储的交互过程 通过在firefox8.0的控制台中输入window.applicationCache可以看到这个对象的所有属性和事件方法

applicationCache.onchecking = function(){
   //检查manifest文件是否存在
}

applicationCache.ondownloading = function(){
   //检查到有manifest或者manifest文件
   //已更新就执行下载操作
   //即使需要缓存的文件在请求时服务器已经返回过了
}

applicationCache.onnoupdate = function(){
   //返回304表示没有更新,通知浏览器直接使用本地文件
}

applicationCache.onprogress = function(){
   //下载的时候周期性的触发,可以通过它
   //获取已经下载的文件个数
}

applicationCache.oncached = function(){
   //下载结束后触发,表示缓存成功
}

application.onupdateready = function(){
   //第二次载入,如果manifest被更新
   //在下载结束时候触发
   //不触发onchched
   alert("本地缓存正在更新中。。。");
   if(confirm("是否重新载入已更新文件")){
       applicationCache.swapCache();
       location.reload();
   }
}

applicationCache.onobsolete = function(){
   //未找到文件,返回404或者401时候触发
}

applicationCache.onerror = function(){
   //其他和离线存储有关的错误
}

四、浏览器与服务器的交互

曾经有面试题:描述在浏览器的地址栏中输入:http://www.baidu.com 后发生了什么?

1、服务端返回baidu页面资源,浏览器载入html

2、浏览器开始解析

3、发现link,发送请求载入css文件

4、浏览器渲染页面

5、发现图片,发送请求载入图片,并重新渲染

6、发送请求js文件,阻塞渲染。如果js对dom进行了操作,则会进行rerender

对于支持离线存储的页面,浏览器和服务器的交互又是如何呢?

首次载入页面:

1-6 : 同上

7:请求页面中需要缓存的页面和数据,就算在之前的步骤中已经请求过(这是个耗能的地方)

8:服务器返回所有请求文件,浏览器进行本地存储

再次载入页面:

1:发送请求

2:使用本地存储的离线文件

3:解析页面

4:请求服务端的manifest文件,判断是否有改变,返回304则表示没有改变进入步骤5,否则进入步骤6

5:进入首次载入页面的7-8

6:使用本地存储,不重新请求

Read More


hash 2016-01-01

常用数据结构复杂度 http://bigocheatsheet.com/

map使用时性能注意 事先规划好容量 使用concurrentHashMap或特定场景选择如TreeMap的结构

JDK8中HashMap的新特性 如果某个桶中的链表记录过大的话(当前是TREEIFY_THRESHOLD = 8),就会把这个链动态变成红黑二叉树,使查询最差复杂度由O(N)变成了O(logN)。

SparseArray代替HashMap Android官方推荐使用SparseArray(稀疏数组)或者LongSparseArray代替HashMap 使用基本类型(Primitive)中的int作为Key,不需要Pair<K,V>或者Entry<K,V>这样的包装类,节约了内存 SpareAraay维护的是一个排序好的数组,使用二分查找数据,即O(log(N)),每次插入数据都要进行排序,同样耗时O(N);而HashMap使用hashCode来加入/查找/删除数据,即O(N/buckets_size) 总的来说,就是SparseArray针对Android嵌入式设备进行了优化,牺牲了微小的时间性能,换取了更大的内存优化;同时它还有别的优化,比如对删除操作做了优化; 如果你的数据非常少(实际上也是如此),那么使用SpareArray也是不错的

Read More


guava 2016-01-01

http://www.cnblogs.com/peida/archive/2013/06/08/3120820.html 使java代码更优雅,简洁 http://code.google.com/p/guava-libraries

com.google.common. annotations:普通注解类型 base:基本工具类库和接口 cache:JVM内缓存 collect:带泛型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合 eventbus:发布订阅风格的事件总线 hash io math net primitives reflect util.concurrent

一.基本工具类 1. 有语言歧义的null会产生令人费解的错误。很多 Guava 的工具类在遇到 null 时会直接拒绝或出错,而不是默默地接受他们 2. 前提条件:更容易对方法进行前提条件的测试 3. 常见的对象方法: 简化了Object常用方法 如 hashCode() 和 toString()实现 4. 排序:fluent Comparator提供多关键字排序 5. Throwable类: 简化异常检查和错误传播

二. 集合类:对JDK 集合类的扩展,Guava项目最完善和为人所知的部分 1.Immutable collections(不变的集合): 防御性编程,并提高效率 2.New collection types(新集合类型):主要有:multisets,multimaps,tables, bidirectional maps等 3.Powerful collection utilities(强大的集合工具类):java.util.Collections 中未包含的常用操作工具类 4. Extension utilities(扩展工具类): 给 Collection 对象添加一个装饰器? 实现迭代器?

三.缓存: 本地缓存,可以很方便的操作缓存对象,并且支持各种缓存失效行为模式

四.Functional idioms(函数式): 简洁, Guava实现了Java的函数式编程,可以显著简化代码

五. Concurrency(并发):实现简单正确的并发性代码 1. ListenableFuture(可监听的Future): Futures,用于异步完成的回调 2. Service: 控制事件的启动和关闭,为你管理复杂的状态逻辑

六. Strings: splitting,joining, padding 等

七. Primitives: 扩展 JDK 中未提供的对原生类型(如int、char等)的操作, 包括某些类型的无符号的变量

八. Ranges: 提供 Comparable 类型的范围处理, 包括连续和离散的情况

九. I/O: 简化 I/O 操作

十. Hashing: 提供比 Object.hashCode() 更复杂的 hash 方法, 提供 Bloom filters

十一. EventBus: 基于发布-订阅模式的组件通信,但是不需要明确地注册在委托对象中

十二. Math: 优化的 math 工具类

十三. Reflection: Java 反射机制工具类

Optional优雅的使用null

Map.get(key)若返回value值为null,代表该键指向的value值是null?该键在map中并不存在?

Preconditions优雅的检验参数

public static void getPerson(int age,String neme)throws Exception{ if(age>0&&neme!=null&&neme.isEmpty()!=true){ System.out.println("a person age:"+age+",neme:"+neme); }else{ System.out.println("参数输入有误!"); } }

public static void getPersonByPrecondition(int age,String neme)throws Exception{ Preconditions.checkNotNull(neme, "neme为null"); Preconditions.checkArgument(neme.length()>0, "neme为\'\'"); Preconditions.checkArgument(age>0, "age 必须大于0"); System.out.println("a person age:"+age+",neme:"+neme); }

Read More


gradle 基于DSL的新一代Java构建工具 2016-01-01

ant>maven>gradle 以 Groovy 为基础,基于DSL语法

寻找gradle历程

一开始的时候,只有一个工程,所有要用到的jar包都放到工程目录下面,时间长了,工程越来越大,使用到的jar包也越来越多,难以理解jar之间的依赖关系 再后来把旧的工程拆分到不同的工程里,靠ide来管理工程之间的依赖关系,各工程下的jar包依赖是杂乱的 一段时间后,发现用ide来管理项程很不方便,比如不方便脱离ide自动构建,于是写自己的ant脚本 再后来,维护ant脚本变得痛苦,管理jar包更加痛苦 svn能管理源码的版本,却不能管理构建出的部署部件的版本 于是决定用maven,然而pom.xml的配置实在太繁了! 最后,找到了神器,gradle!

groovy 比 xml好用

Gradle用groovy做为build脚本,比xml易读易用得多 ant里用xml表达一个if分支功能麻烦,不直观 gradle的build脚本就是groovy程序,所以做分支循环等非常方便自然

Convention over Configuration 比写大量ant基础脚本方便

ant每个项目得定义哪里放源码,哪里放jar包,哪里放编译出的class文件等等,非常麻烦 gradle和maven一样义了默认的目录结构,gradle的目录的结构规范和maven一样

自定义task,比maven灵活

maven里实现一个简单的自定义功能,得写maven插件 gradle里,task是一等公民,可以轻易的添加自己的功能

灵活的依赖管理

ant没有依赖管理的功能,都要自己手动做 maven的依赖管理很死板,只能依赖于标准的maven artifact,不能依赖本地的某个jar文件或者其它的源码 gradle则可以混合地同时支持这些依赖方法,让旧项目的迁移容易得多

默认就具有丰富的功能

装好gradle,默认就支持java,war,ear项目,单元测试,生成jar包,上传jar包到maven服务器等等

优点


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

apply plugin: “java"
group = “com.mycompany.app"
archivesBaseName = “my-app"
version = “1.0-SNAPSHOT"
repositories {
    mavenCentral()
}
dependencies {
    testCompile “junit:junit:3.8.1″
}

task

ant


<?xml version="1.0″?>
<project name="foo">
<target name="hello">
   <echo>Hello World</echo>
</target>
</project>

gradle

task hello << {
   println “Hello World"
}

Ant是使用定义好的task来做要做的事情,而Gradle则是使用Groovy动态脚本来实现

灵活性

task time <<{
    int hours = new Date().hours
    switch (hours) {
        case 0..11:
            println "Good Morning! It’s ${hours} am."
        break
        case 12..17: // noon to 5pm
            println " Good Afternoon! It’s ${hours > 12 ? hours -12 : hours} pm."
            break
        default: // 6pm to 11pm
            println "Good Evening! It’s ${hours-12}pm. Shouldn’t you be going home?"
    }
}
C:\Users\Administrator>gradle -v

------------------------------------------------------------
Gradle 1.5
------------------------------------------------------------

Gradle build time: 2013年3月27日 星期三 下午01时51分06秒 UTC
Groovy: 1.8.6
Ant: Apache Ant(TM) version 1.8.4 compiled on May 22 2012
Ivy: 2.2.0
JVM: 1.7.0_17 (Oracle Corporation 23.7-b01)
OS: Windows 7 6.1 amd64

gradle 插件

http://wiki.gradle.org/display/GRADLE/Plugins

代理

gradle 配置NTLM proxy authentication

在build.gradle中加入

System.setProperty('http.proxyHost', '') 
System.setProperty('http.proxyPort', '')
System.setProperty('http.auth.ntlm.domain', '')
System.setProperty('http.proxyUser', '') 
System.setProperty('http.proxyPassword', '') 
System.setProperty('http.nonProxyHosts', '') //用|隔开多个

Read More


good stuff 2016-01-01

小型bbs http://bbs.xiuno.com/

google web 性能优化点 https://developers.google.com/web/fundamentals/performance/?hl=zh-cn

Read More


golang 2016-01-01

互联网时代的C

  • 充分利用多核
  • 软件品质和团队协作
  • 编程哲学重塑

并发与分布式

执行体 多数语言语法层不支持协程,通过库支持也不完整,仅创建/销毁/切换,协程中调用同步IO会阻塞其他协程 执行体间通信需要有互斥/同步,消息传递,多数语言提供了库支持,也看不到协程

软件工程

  • 代码风格
  • 错误处理
  • 接口
  • 单元测试

免public/private/protected关键字,以大小写统一作用域 花括号统一 错误处理引入deffer,免嵌套try catch

编程哲学

变革派,非改良派

语言特性

  • gc
  • 丰富内置类型
  • 多返回值
  • 错误处理
  • 匿名函数与闭包
  • 类型和接口
  • gorouting
  • 反射
  • 语言交互

闪光点 - 函数多返回值 - 花括号统一 - 非侵入性接口 OOP仅接口和封装是精髓,继承就花拳绣腿?鸟会飞,麻雀是鸟,所以会飞之类的看上去头头是道的学院派例子。 - defer -- 并发相关 go chan select,语言层直接支持并发 go 简化并行运行某函数需要的各种库和继承代码 chan 类比为线程安全的blockingqueue select 即select/poll/epoll 同类产品 三个关键字大大简化并发编程心智负担

NB哄哄的作者

Ken Thompson:与Dennis Ritchie两人开发的Unix Rob Pike:Bell Labs Unix团队,Plan 9 ,UTF-8设计者 Robert Griesemer:Java的HotSpot编译器,Chrome的JS引擎V8 Russ Cox:Plan 9 Ian Taylor:GCC Brad Fitzpatrick: memcached

类型和接口

关键字沿用c struct,不沿袭超级复杂的类型系统,不支持继承和重载,只支持最基本的类型组合 不光是对OOP做减法,引入无比强大的非侵入式接口

以前实现接口前要先定义接口,类型与接口紧密绑定

type Bird struct{ } func (b *Bird) Fly(){ } 可之后定义IFly接口 type IFly interface { Fly() }

func main(){ var fly IFly=new(Bird) fly.Fly() }

工程管理

消除工程文件(makefile,pom.xml) 云风-Go 语言初步 http://blog.codingnow.com/2010/11/go_prime.html

为什么除了Go语言, 其他类C语言都是垃圾

Node.js单线程事件循环机制和非阻塞的编程模式让人不太满意。还有,所有的东西都要用Javascript的回调函数

说说Golang的使用心得 http://blog.jobbole.com/84554/

为什么我不喜欢Go语言式的接口(即Structural Typing) http://blog.zhaojie.me/2013/04/why-i-dont-like-go-style-interface-or-structural-typing.html

iota: Golang 中优雅的常量 https://segmentfault.com/a/1190000000656284

初入坑golang,感觉良好 http://tnt.wicast.tk/2015/05/04/golang-is-my-choice/

http://tonybai.com/

http://www.xiaozhou.net/

Read More


windows git 邮件通知 2016-01-01

准备工具

开源mail server

hmail

命令行发邮件的工具

blat

初始化下blat,注意域名的修改 blat -install localhost admin@localmail.ddatsh.com

blat -body test -to admin@localmail.ddatsh.com -s "miss you" -u admin -pw admin blat -base64 -charset GB2312 -body 中文 -to admin@localmail.ddatsh.com -s "测试" -debug

建空仓库

git init --bare

设置接收人员

git config hooks.mailinglist "admin@localmail.ddatsh.com "

git config hooks.envelopesender "admin@localmail.ddatsh.com"

本质是

.git\config

[hooks]
    mailinglist = admin@localmail.ddatsh.com
    envelopesender = admin@localmail.ddatsh.com
    emailprefix = "[GIT] "
    showrev = "git show -C %s; echo"

post-receive.sample

搜索post-receive.sample,打开后内容有 post-receive-email,在gitster里有 内容COPY过来,改名为post-receive

send_mail()
{
    if [ -n "$envelopesender" ]; then
        #sendemail -t admin@localmail.ddatsh.com -f admin@localmail.ddatsh.com -m
        blat -to admin@localmail.ddatsh.com -s "git"  -q
        #/usr/sbin/sendmail -t -f "$envelopesender"
cmd /c "echo 1 >> init.txt"
cmd /c "git add ."
cmd /c "git commit -m 1"
cmd /c "git push origin master"
To: admin@localmail.ddatsh.com
Subject: [GIT] test branch master updated. a63fc11d7c086f719c8d08d36df78c0cf7cbb6bf
X-Git-Refname: refs/heads/master
X-Git-Reftype: branch
X-Git-Oldrev: 45384097f58cfae3c65a1b43aaa295d11615fdf8
X-Git-Newrev: a63fc11d7c086f719c8d08d36df78c0cf7cbb6bf

This is an automated email from the git hooks/post-receive script. 
It was generated because a ref change was pushed to the repository containing the project
 "test".

The branch, master has been updated
       via  a63fc11d7c086f719c8d08d36df78c0cf7cbb6bf (commit)
      from  45384097f58cfae3c65a1b43aaa295d11615fdf8 (commit)

Those revisions listed above that are new to this repository have not appeared on 
any other notification email; so we list those revisions in full, below.

- Log -----------------------------------------------------------------
commit a63fc11d7c086f719c8d08d36df78c0cf7cbb6bf
Author: ddatsh 
Date:   Thu Feb 9 14:17:29 2012 +0800

    1

diff --git a/init.txt b/init.txt
index 77fe233..eccec7d 100644
--- a/init.txt
+++ b/init.txt
@@ -11,3 +11,4 @@
 1
 1
 1
+1

-----------------------------------------------------------------------

Summary of changes:
 init.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)


hooks/post-receive
-- 

test

Read More


理解Git的工作流程 2016-01-01

不理解Git的设计动机,就会处处碰壁。到知道足够多的命令和参数后,就会强行让Git按你想的来工作,而不是按Git自己的方式来。这就像把螺丝刀当锤子用,也能把活干完,但肯定干的差极了,花费很长时间,还会弄坏螺丝刀

常见的Git工作流程是怎么失效的

从Master创建一个分支,写代码,然后把这个分支合并回Master

多数时候这样做的效果会如你所愿,因为从你创建分支到合并回去之间,Master一般都会有些变动 然后,有一天当你想把一个功能(feature)分支合并进Master的时候,而Master并没有像以往那样有变动,问题来了:这时Git不会进行合并commit,而是将Master指向功能分支上的最新commit(fast forward) 不幸的是,你的功能分支有用来备份代码的commit(作者称之为checkpoint commit),这些经常进行的commit对应的代码可能处于不稳定状态!而这些commit现在没法和Master上那些稳定的commit区分开来了。当你想回滚的时候,很容易发生灾难性后果

于是你就记住了:“当合并功能分支的时候,加上 -no-ff 选项强制进行一次全新的commit。”嗯,这么做好像解决问题了,那么继续

然后一天你在线上环境中发现了一个严重bug,这时你需要追溯下这个bug是什么时候引入的。你运行了bisect命令,但却总是追溯到一些不稳定的commit。因此你不得不放弃,改用人肉检查

最后你将bug范围缩小到一个文件。你运行blame命令查看这个文件在过去48小时里的变动。然后blame告诉你这个文件已经好几周没有被修改过了——你知道根本不可能没有变动。哦,原来是因为blame计算变动是从第一次commit算起,而不是merge的时候。你在几周前的一次commit中改动了这个文件,但这个变动今天才被merge回来

用no-ff来救急,bisect又临时失效,blame的运作机制又那么模糊,所有这些现象都说明一件事儿,那就是你正在把螺丝刀当锤子用

反思版本控制

版本控制的存在是因为两个原因

首先,版本控制是用来辅助写代码的。因为你要和同事同步代码,并经常备份自己的代码。当然了,把文件压缩后发邮件也行,不过工程大了大概就不好办了

其次,就是辅助配置管理工作。其中就包括并行开发的管理,比如一边给线上版本修复bug,一边开发下一个版本。配置管理也可以帮助弄清楚变动发生的具体时间,在追溯bug中是一个很好的工具

一般说来,这两个原因是冲突的。

在开发一个功能的时候,你应该经常做备份性的commit。然而,这些commit经常会让软件没法编译。

理想情况是,你的版本更新历史中的每一次变化都是明确且稳定的,不会有备份性commit带来的噪声,也不会有超过一万行代码变动的超大commit。一个清晰的版本历史让回滚和选择性merge都变得相当容易,而且也方便以后的检查和分析。然而,要维护这样一个干净的历史版本库,也许意味着总是要等到代码完善之后才能提交变动。

那么,经常性的commit和干净的历史,你选择哪一个?

如果你是在刚起步的创业公司中,干净的历史没有太大帮助。你可以放心地把所有东西都往Master中提交,感觉不错的时候随时发布。

如果团队规模变大或是用户规模扩大了,你就需要些工具和技巧来做约束,包括自动化测试,代码检查,以及干净的版本历史。

功能分支貌似是一个不错的折中选择,能够基本的并行开发问题。当你写代码时候,可以不用怎么在意集成的问题,但它总有烦到你的时候。

当你的项目规模足够大的时候,简单的branch/commit/merge工作流程就出问题了。缝缝补补已经不行了。这时你需要一个干净的版本历史库。

Git之所以是革命性的,就是因为它能同时给你这两方面的好处。你可以在原型开发过程中经常备份变动,而搞定后只需要交付一个干净的版本历史。

工作流程

考虑两种分支:公共的和私有的。

公共分支是项目的权威性历史库。在公共分支中,每一个commit都应该确保简洁、原子性,并且有完善的提交信息。此分支应该尽可能线性,且不能更改。公共分支包括Master和发行版的分支。

私有分支是供你自己使用的,就像解决问题时的草稿纸。

安全起见,把私有分支只保存在本地。如果你确实需要push到服务器的话(比如要同步你在家和办公室的电脑),最好告诉同事这是私有的,不要基于这个分支展开工作。

绝不要直接用merge命令把私有分支合并到公共分支中。要先用reset、rebase、squash merges、commit amending等工具把你的分支清理一下。

把你自己看做一个作者,每一次的commit视为书中的一章。作者不会出版最初的草稿,就像Michael Crichton说的,“伟大的书都不是写出来——而是改出来的”。

如果你没接触过Git,那么修改历史对你来说好像是种禁忌。你习惯于认为提交过的所有东西都应该像刻在石头上一样不能抹去。但如果按这种逻辑,我们在文本处理软件器中也不应该使用“撤销”功能了。

实用主义者们直到变化变为噪音的时候才关注变化。对于配置管理来说,我们关注宏观的变化。日常commit(checkpoint commits)只是备份于云端的用于“撤销”的缓冲。

如果你保持公共历史版本库的简洁,那么所谓的fast-forward merge就不仅安全而且可取了,它能保证版本变更历史的线性和易于追溯。

关于 -no-ff 仅剩的争论就只剩“文档证明”了。人们可能会先merge再commit,以此代表最新的线上部署版本。不过,这是反模式的。用tag吧。

规则和例子

根据改变的多少、持续工作时间的长短,以及分支分叉了多远,我使用三种基本的方法。

1)短期工作

绝大多数时间,我做清理时只用squash merge命令。

假设我创建了一个功能分支,并且在接下来一个小时里进行了一系列的checkpoint commit。

git checkout -b private_feature_branch touch file1.txt git add file1.txt git commit -am "WIP"

完成开发后,我不是直接执行git merge命令,而是这样:

git checkout master git merge --squash private_feature_branch git commit -v

然后我会花一分钟时间写个详细的commit日志。

2)较大的工作

有时候一个功能可以延续好几天,伴有大量的小的commit。

我认为这些改变应该被分解为一些更小粒度的变更,所以squash作为工具来说就有点儿太糙了。(根据经验我一般会问,“这样能让阅读代码更容易吗?”)

如果我的checkpoint commits之后有合理的更新,我可以使用rebase的交互模式。

交互模式很强大。你可以用它来编辑、分解、重新排序、合并以前的commit。

在我的功能分支上:

git rebase --interactive master

然后会打开一个编辑器,里边是commit列表。每一行上依次是,要执行的操作、commit的SHA1值、当前commit的注释。并且提供了包含所有可用命令列表的图例。

默认情况下,每个commit的操作都是“pick”,即不会修改commit。

pick ccd6e62 Work on back button pick 1c83feb Bug fixes pick f9d0c33 Start work on toolbar

我把第二行修改为“squash”,这样第二个commit就会合并到第一个上去。

pick ccd6e62 Work on back button squash 1c83feb Bug fixes pick f9d0c33 Start work on toolbar

保存并退出,会弹出一个新的编辑器窗口,让我为本次合并commit做注释。就这样。

舍弃分支

也许我的功能分支已经存在了很久很久,我不得不将好几个分支合并进这个功能分支中,以便当我写代码时这个分支是足够新的的。版本历史让人费解。最简单的办法是创建一个新的分支。

git checkout master git checkout -b cleaned_up_branch git merge --squash private_feature_branch git reset

现在,我就有了一个包含我所有修改且不含之前分支历史的工作目录。这样我就可以手动添加和commit我的变更了。

总结

如果你在与Git的默认设置背道而驰,先问问为什么。

将公共分支历史看做不可变的、原子性的、容易追溯的。将私有分支历史看做一次性的、可编辑的。

推荐的工作流程是:

基于公共分支创建一个私有分支。 经常向这个私有分支commit代码。 一旦你的代码完善了,就清理掉下私有分支的历史。 将干净的私有分支merge到公共分支中。

Read More


TopGit 2016-01-01

Topgit 原理

下面的分支图,是一个近似的 Topgit 实现图(略去了重要的 top-bases 分支)


                    +---b1--M1---M3--- (特性分支B: refs/heads/t/B)
                    |              /        /
   +---a1---a2-------a3----M2---   (特性分支A: refs/heads/t/A)
   |                                  /
---V1--------------------V2---     (主线/卖主分支: master)
   |                                  \
   +---------------c1---M4---   (特性分支C: refs/heads/t/C)

参见 http://www.worldhello.net/2010/10/28/2033.html

主线上的 V1 是上游的版本(master)的一次提交 特性分支 A 和 C 直接依赖主线 master,而特性分支 B 依赖特性分支 A 提交 M1 是特定分支 B 因为特性分支 A 更新(a3)而做的一次迁移 提交 M2 和 M4,分别是特性分支 A 和 C 因为上游(master)出现了新版本 V2 而做的迁移 当然特性分支 B 也要做相应的迁移-- M3

上述的描述非常粗糙,因为这样的设计很难实现特性分支导出为补丁文件。例如特性分支B的补丁,实际上应该是 M3 和 M2 之间的差异,而绝不是 M3 到 a2 之间的差异

Topgit 为了能够实现分支导出为补丁,又为每个特性的开发引入了第二个分支,追踪分支依赖的“变基”


   +---a1---a2
   |                 |
   |                 +---a3---M2--- (特性 B 的变基分支: refs/top-bases/t/B)
   |                                 /
   +-------------------V2---   (特性 A 的变基分支: refs/top-bases/t/A)
   |                              /
---V1----------------V2---     (主线/卖主分支: master)
   |                              \
   +-------------------V2---   (特性 C 的变基分支: refs/top-bases/t/C)

把上面的两张分支图重合,实际上就可以获得各个特性分支在任一点的特性补丁文件 上面的特性分支 B 还只是依赖一个分支,如果出现一个分支依赖多个特性分支的话,情况就会更加的复杂,更会体现出这种设计方案的精妙 Topgit 还在每个特性分支工作区的根目录引入两个文件,用以记录分支的依赖以及关于此分支的说明

  • 文件 .topdeps 记录该分支所依赖的分支列表
    该文件通过 tg create 命令在创建特性分支时自动创建,或者通过 tg depend add 命令来添加新依赖
  • 文件 .topmsg 记录该分支的描述信息
    该文件通过 tg create 命令在创建特性分支时创建,也可以手动编辑

Topgit 在Win下安装

topgit完全就是Linux 里的shell脚本 像linux中的源码安装一样,要用到make等工具,把源码里的部分内容,如路径,替换,然后复制到目标路径 win下模拟linux环境的主要有cygwin,msys,mingw 简单脚本,我不喜欢cygwin,在外面套了层,下载还麻烦,虽然用cygwin没以下这几步操作 我要直接在win下的cmd里进去,敲命令就行 win下jekyll安装的时候,用到的railsinstallert里面自带了msys环境

要正常使用,需要做几步操作

修改tg-create文件(回车换行问题)

line 119 echo "$deps" | sed 's/$/\r/g' >"$root_dir/.topdeps" line 137 } | sed 's/$/\r/g'>"$root_dir/.topmsg" 参考 linux shell 用sed命令在文本的行尾或行首添加字符

建fgrep(报错问题)

railsinstaller安装后的devkit\bin有fgrep.exe topgit脚本里会用到fgrep命令,调用到fgrep.exe会报错 解决: 建个fgrep,无扩展名,放git\bin目录(从msys提取的) #!/bin/sh grep -F "$@"

下载新版mktemp.exe,放git\bin(报错问题)

mktemp -v mktemp version 1.6

不修改tg-create Z:\dd>sh sh-3.1$ tg create t/hack1 tg: Automatically marking dependency on master tg: Creating t/hack1 base from master... Switched to a new branch 't/hack1' warning: LF will be replaced by CRLF in .topdeps. The file will have its original line endings in your working directory. warning: LF will be replaced by CRLF in .topmsg. The file will have its original line endings in your working directory. tg: Topic branch t/hack1 set up. Please fill .topmsg now and make initial commit. tg: To abort: git rm -f .top* && git checkout master && tg delete t/hack1

不建fgrep 0 [main] fgrep 4492 open_stackdumpfile: Dumping stack trace to fgrep.exe.stackdump mv: cannot move `.git/hooks/pre-commit+' to `.git/hooks/pre-commit' .git/hooks/pre-commit: line 2: /pre-commit: No such file or directory .git/hooks/pre-commit: line 2: exec: /pre-commit: cannot execute: No such file or directory

msysgit自带mktemp在sh里被执行时,同上,会报错,生成dump 1.6版的mktemp执行时,会输出一些版权之类说明,我用ollydbg去掉了,不影响

TopGit的使用技巧

TopGit的使用技巧1 TopGit的使用技巧2 TopGit的使用技巧3

TopGit是Git的一个分支及补丁管理工具,可以很好的管理基于分支的开发模式,尤其对于二次开发人员来说,就是杀手锏 首先原型版本的代码始化成Git库,然后每给原型增加一个功能或者修改一个Bug都用TopGit创建一个分支(tg create t/feture1 或者 tg create t/bug1-fix),这样每一个功能或者Bug修复都是一个TopGit分支

原型版本升级后,只要切换到原型分支(master),然后导入原型的新版本代码,最后将TopGit的分支一个一个迁移到新的原型之上,就完成了一次版本的升级

关于分支update

  • 没有冲突的update操作

git init ... git add . git commit -m init

tg create t/hack1 ... git add . git commit -m init tg summary #查看当前TopGit分支的状态 t/hack1 [PATCH] t/hack1 git rev-parse master top-bases/t/hack1 t/hack1 #查看master top-bases/t/hack1 t/hack1指向的提交号 前两个相同,最后一个不同(master和top-base相同,t/hack1修改过)

修改master,致使 t/hack1过期

tg summary #D 代表t/hack1已经过期了,需要update D t/hack1 [PATCH] t/hack1 git rev-parse master top-bases/t/hack1 t/hack1 三个都不同(master修改了,top-base不变,指向原有master,t/hack1不变)

更新t/hack1分支 git checkout t/hack1 tg update 先是master Fast-forward 然后 Merge made by recursive.

  • 含有冲突的update操作
  • 含有分支依赖且产生冲突的update操作

Read More


一些git常用方法 2016-01-01

ssh 连github

ssh-keygen -t rsa -C "admin@ddatsh.com" 生成id_rsa和id_rsa.pub 然后到github ,SSH Keys加入公钥

cmd下 ssh -v -T git@github.com 查找密钥路径要设置 set HOME=%USERPROFILE%

分支管理一例

Git 的分支不需要文件目录。从一个分支转到另外一个分支的时候,自动重建现场 例如,在一个分支里先有3个文件,然后开了一个新的分支,并提交了几个新文件以及修改了2个文件。当切换到第一个分支的时候,工作目录里恢复到原先的3地个文件,其内容也是先前的内容,没有改动过

分支不仅仅用来管理程序的版本,而且可以用来辅助尝试性程序开发,称为主题分支/专题分支(Topic Branch)

因此用熟了 Git 的人常常会在做任何事情以前开个分支,如果成功就合并到主分支,如果不成功则简单删了这个试验分支

有时候也会忘记先开分支,直到改了一批文件后才发现应该先开个分支 当没有提交时,只要开个新分支。等提交修改的内容的时候,它们会在新开的分支里出现,而不是在老的分支里

即使已提交过时,也很容易修正过来 例如master 当前最新的提交准备放在新分支 git branch xx #开一个新的 git reset --hard head~1

空分支(没有parent)

三种方法 1. git symbolic-ref head refs/heads/empty rm .git/index git clean -fdx 2. echo ref: refs/heads/newbranch > .git/HEAD git rm -rf . 3.Git 1.7.2后 –orphan git checkout --orphan newbranch git rm -rf .

git cherry-pick 和 rebase 混合

假设master分支commit两次(A,B)之后建了topic1分支 在topic1分支提交了C-D-E-F-G

然后只想把 D,F 加到 master ,其他的留在 topic ,而且又不想把 history 搞得很乱 最好是这样:

A-B-D-F master \ C-E-G topic

git checkout master git cherry-pick D git cherry-pick F git checkout topic git rebase master

使用 git rebase 避免无谓的 merge

git pull 默认行为是将远端的 repo与本地repo合并,这也是DVCS的初衷 但是,很多时候会发生以下这种情形 merge

原因: 本地branch与远端branch同步非常频繁(通常是同名的branch,如 master) 这两个branch几乎完全同步,那这些merge动作其实没必要,造成历史线索无谓的复杂 这种情况推荐用 git pull --rebase

1.把本地 repo从上次 pull 之后变化暂存 2.回到上次pull点 3.应用远程变更 4.再应用暂存的变更

假设合并前:

D---E master / A---B---C---F origin/master merge 合并后: D--------E / \ A---B---C---F----G master, origin/master

如果是 rebase 的方式,就不会有 G 点!

A---B---C---F---D'---E' master, origin/master

注意,其中 D’, E’ 的 commit SHA 与本來 D, E 是不同的,因为算是砍掉重新 commit 了

有conflict怎么办? rebase 跟 merge类似,出现 conflict 一样会暂停 rebase 动作,需要手动修复后,才能继续 这也是rebase比merge复杂一点的地方:merge发生conflict,只要解决冲突一次,然后commit出去就完成了 而 rebase 的 conflict 可能会发生在上述步骤4的每一步重新套用上,所以可能需要解决冲突好几次 (rebase时所谓的解决冲突,其实是直接修改之前的变更内容,所以上图变成D'和E')

修改比较多,预期会有较多conflict,可以用merge,不过如果是多次大范围的主题式修改,应该一开始就开一个branch 如果修改范围较小,不太会产生conflict,可以用rebase

避免每次git pull 都要 加 --rebase .git/config 加 [branch "master"] remote = origin merge = refs/heads/master rebase = true

也可以直接加到 ~/.gitconfig 设全局 [branch] autosetuprebase = always

git rebase 冲突了, 又不喜欢一步一步的 git rebase 怎么办?

git rebase --abort git reset --hard HEAD git pull git status

Read More


版本管理的发展史 2016-01-01

http://coolshell.cn/articles/3288.html

git-scm

分成四个时期:

史前时期:1982年的RCS

古典时期:1990年的CVS(经典的SCM管理器,不能track目录和文件名的改变),1985年的PVCS,1992年的clearcase(价格贵,功能复杂,今天也有很多公司在用),微软的VSS,90年代中期的Perforce(P4,这个工具今天都还在被广泛地使用,尤其是那些中等大小却有着大量开发团队的公司,现在是Google内部最大的代码管理器)

中世纪时期:SVN(Linus也很不喜欢SVN,2006年引入了Git),AccuRev(强力支持branch和merge,其扮演了一个很重要角色-帮助社区脱离clearcase和CVS)

文艺复兴时期:BitKeeper——Sun的内部管理工具,Linux的内核代码2002年用它

Linus改变了历史——中世纪已经过去了,现在的世界由分布式系统主宰,以及消除branch和merge的恐惧

Git 基于 DAG 结构 (Directed Acyclic Graph),其运行起来相当的快。在Git发布后的来年,几乎所有的大型的开源项目全部从Subversion迁移到了Git上,www.github.com真是很大,这可能是这具星球上最强大最牛最酷的SCM系统了。Git可能并不是最简单的,但它一定会是未来十年的主流。(有空读读这本书——Git Internals)

Mercurial (Hg) 第一次出现在2005年4月,也是因为BitKeeper不免费了。Hg可以和Git在一起使用,见:http://mercurial.selenic.com/wiki/HgGit。但是Hg和Git在设计上不一样,他们对提交/变更的概念是一样的,只不过Git用tree来实现,而Hg则是用扁平的文件和目录来实现(revlog),设计细节可参看:http://mercurial.selenic.com/wiki/Design和 http://mercurial.selenic.com/wiki/DeveloperInfo

Darcs (Darcs Advanced Revision Control System)是另一个让你摆脱Subversion和CVS的工具 它的优势是性能,以及他与众不同的历史版本管理——管理patches而不是snapshot(提交/修改),当然,这样一来,历史改变看上去很不好懂

Bazaar (bzr) 是另一个开源的 DVCS,它试图给SCM的世界里带来一些新的东西。其由Canonical(Ubuntu那公司),2008年成为GNU

Plastic在2006年出现,强力地支持branch和merge,其还提供了强大的图示,包括3D的版本树,Plastic主要是为了让中等开发团队使用,介于大型的团队(ClearCase)和小型的团队(Subversion)之间

Team Foundation Server (TFS),微软的新一代SCM工具,主要是为了VSS的失败负责,但是他还不是版本管理上还是很强,只不过,他集成了一大堆各种各样的工具,比如:issue tracking,test management等

评论

Mensch88 2010年11月17日17:37

把Linus造成神并进行崇拜对我们的学习并没有帮助。 Git的第一个版本是Linux之父Linus Torvalds亲手操刀设计和实现的,但Linus不是凭空设计出来的,他是站在巨人的肩膀上,很大程度上借鉴了BitKeeper和Monotone的设计。Git的开发从2005年4月3日开始,2005年4月7日完成大约1300行代码,实现基本功能并selfhosting http://marc.info/?l=git&m=117254154130732

“无论是反对还是喜欢,Linus的确是改变了历史——中世纪已经过去了,现在的世界由分布式系统主宰,以及消除branch和merge的恐惧。” 没有Linus,历史依然向前进,分布式系统依然会占上风。Bazaar在2005年3月立项,Mecurial在2005年4月19日也已经完成第一个版本

“Git 基于 DAG 结构 (Directed Acyclic Graph),其运行起来相当的快。” Bazaar和Mecurial也都基于DAG结构。另外DAG有中文名称,叫有向无环图

“在Git发布后的来年,世界上所有的大型的开源项目全部从Subversion迁移到了Git上,www.github.com真是很大,这可能是这具星球上最强大最牛最酷的SCM系统了。Git可能并不是最简单的,但它一定会是未来十年的主流。” 兄弟,你在说些什么?世界上所有的大型的开源项目全部从Subversion迁移到了Git上?www.github.com真是很大?最强大最牛最酷的SCM系统? 我承认由于Linus个人因素的影响,Linux kernel和不少Linux下的开源项目都使用了Git,但请不要忽视别的DVCS的存在。 Ubuntu在哪里?Octave在哪里?Emacs在哪里?Vim在哪里? 另外请注意:www.github.com只是ruby搭起来的一个使用Git版本控制的商业网站,它属于GitHub公司,与Git和Linus没有内在的联系。而我还真没看见过有多少大型开源项目把代码托管到了github上

Read More


git权威指南 笔记 2016-01-01

git权威指南

!!!!!!!!!!还没弄完,慢慢更新!!!!!!!!!!!!!

请买 蒋鑫 的《Git权威指南》,不啰嗦 此文大部分内容是对此书内容的摘录、纪要 期间参考了许多站上的文章

自由軟體的故事 http://www.inmediahk.net/node/299503

版本管理器的发展史 http://coolshell.cn/articles/3288.html

前言

版本控制(scm)是管理数据变更的艺术,数据的变更会来自个人,不同的人(团队) 要忠实地记录每一次变更,还要帮助还原任何一次历史变更,以及团队协作

传统集中式版本控制只有唯一的分支命名空间 分支间合并可能因缺乏追踪导致重复合并,引发冲突 或者分支合并时效率低下和陷阱重重(git灵活的设计可以摆脱分支管理的梦魇)

最终容易导致:

  • 项目延期
  • 历史修正了的BUG重现
  • 问题不方便在代码中定位出

基于开源软件的二次开发,最初用SVN,使用卖主分支模型管理代码 随着增加和修改的代码的增长,与上游偏离越来越远 上游新版本发布后,最初可能几小时就完成迁移,但上游改动数量庞大后,迁移过程就极痛苦 最终不再与上游合并,不再追踪上游改动,导致无法发动全球智慧为客户创造价值

第一篇 Git由来

Git,Linux Kernel 发起人 Linus Torvalds 设计的版本管理系统,继Linux 内核后的第二个伟大作品 官方网站是 http://git-scm.com (已被GFW墙掉)

git-scm 新站截图 git-scm

Linus 由开始一直没有使用版本控制系统去处理由社群贡献而来的源码,在开发社群中有人用 CVS 版本控制系统来增加效率,不过 Linus 始终不愿意使用 CVS 的单一中央储存库的架构不能完美地配合 Linux Kernel的分布式开发 2002 年 10 月在站上只有 FTP 和 HTTP 下载和电子邮件通信论坛 那时谁去 SourceForge 登记,都可免费使用版本控制和待办事项等开发工具 不少其他的开源项目的网站都设计得比 http://www.kernel.org 好

Linus 为了改善效率,使用了非开源的分布式版本控制系统-BitKeeper 除了源码不开放外,BitKeeper 作者 Larry McVoy 更加入了好几条特别条款 最受争议的是 BitKeeper 用户不能参与 BitKeeper 以外任何版本控制系统的开发,也不能做 BitKeeper 的逆向工程

Stallman 对 Linus 的决定作出严厉的批评,当然还有开源运动人士也不同意,以实务为考虑的 Linus 继续使用 BitKeeper

2005 年 Samba 开发领袖 Andrew Tridgell 开发了一个连接 BitKeeper 储存库的程序,被 McVoy 指控为逆向工程。McVoy 因此停止 BitKeeper 对 Linux的支持。批评者之前的论点结果成为了事实:

1.Linux 的开发工具被他人控制了 2.开发社群因为特别条款,好几年也不能开发如 Git 合适社群使用的版本控制系统 3.开发社群因为没有 BitKeeper 的源码而不能增强这个版本系统的功能

而且 McVoy 也不是容易跟人合作,他也有很强的控制欲,连已付钱的用户也不可以参加开发其他版本控制系统 Linus 小看了软件自由的重要性,结果作出了错误决定,而且社群讨论BitKeeper 时常引起争论,增加了内耗

第1章 版本控制前世今生

另参考 版本管理的发展史

CVS出现前,黑暗的史前时代

秩序和工具的匮乏 代码分散地存在不同软盘里,不知道哪个盘的代码是最优的,最新的可能并非最优,失败的重构会毁掉原来正确的代码

从未使用甚至听说版本控制的人(团队),就像停留在史前时代,任数据自生自灭

CVS

为SCM确立了标准,commit log,check in(out),tag,branch cvs基于rcs(针对单独文件的版本管理工具)

SVN

集中式版本控制集大成者 全局版本号,相对cvs的每文件独立版本 除集中式版本控制固有问题,如

单点故障后,无法提交,最坏情况是丢失所有历史,因为客户端只有前一次快照

SVN的里程碑和分支设计被证明是一个错误 虽然这个错误设计使SVN有快速创建里程碑和分支能力 但导致更多问题

  • 版本库目录结构要固定

要建类似 /trunk,/branch,/tag目录,否则无法建立分支和tag

  • 破坏授权

创建分支或tag时,针对/trunk及子目录的授权要重建

  • 分支混乱

可以基于/trunk建分支,也可以基于任何目录建分支,难画出有意义的分支图 svn 1.5虽有了合并追踪,但会被混乱的分支管理而被抵消


linus是CVS反对者,也同样反对SVN 直到2002的十余年,宁愿用diff,patch,tar包维护内核代码,也不用CVS 2002-2005用商业分布式SCM-BitKeeper,到搞出GIT间的故事 linus以文件系统专家和内核设计者角度对GIT设计,使其性能和存储能力都优秀

越来越多令人激动的使用GIT的重量级项目

开发人员不会再因频繁遭遇提交冲突而中断 经过Linux这样庞大的项目考验后,被证明可以胜任任何规模的团队 不用把“鸡蛋”放在有单点故障,可能崩溃的“篮子” (集中式SCM服务器)

第2章 爱上GIT的理由

避免引用辅助目录

GIT不像svn,cvs会在每个目录下生成辅助目录,grep时结果不会加倍(svn 1.7只有一个.svn目录了)

工作进度保存(stash)

临时有紧急任务,可以开辟出干净工作区开始工作,和保存当前未完成工作的进度

更好的差异比较

git diff(水很深,涉及暂存区,GIT最精华设计之一)

其他

本地查看日志,不再忍受网络状况不佳 后悔药:重写提交说明,最近一次(及历史)提交内容 --amend 和 rebase 便宜的分支

太多太多....

第3章 安装相关

git源自 Linux ,版本更新较快 1.7.7 支持stash 未追踪文件(--include-untrack或-u) 1.7.10 国际化,merge时提示输入信息

我还是主要在WIN7下用

msysgit

msysgit挺好使,不喜欢在cygwin里再装 1.7.10 还未包含国际化文件 msysGit 正式支持 unicode,版本库彻底告别乱码。控制台录入中文有问题,最好配合Unicode版的TortoiseGit git config --global core.quotepath false cmd 里git status,git log 中文文件名,LOG 都没问题

tortoisegit

图形化的tortoisegit依赖msysgit 1.7.8 的64位版支持了ceth

第4章 初始化

最重要的

git config --global user.name xxx git config --global user.email xxx

最常用别名

git config --global alias.ci commit git config --global alias.st status git config --global alias.co checkout git config --global alias.br branch 自己弄了几个批处理更省事 如 l.cmd

@echo off
if "%1"=="" (
  git log --oneline
  goto end
)

if "%1"=="s" (
  git log --oneline --stat
  goto end
)

if "%1"=="g" (
  git log --oneline --graph
  goto end
)

if "%1"=="gs" (
  git log --oneline --graph --stat
  goto end
)

if "%1"=="f" (
  git log --pretty=fuller
  goto end
)

if "%1"=="r" (
  git log --pretty=raw
  goto end
)
:end

三步曲

git init
git add
git commit

--global 和--system

--global改的C:\Users\Administrator.gitconfig --system改的git\etc.gitconfig

android更好的管理,弄了Gerrit审核服务器管理GIT提交

第二篇 git独奏

git有陡峭的学习曲线,对有其它版本控制工具使用经验的老手也不例外 不能按照其他版本控制系统遗留习惯操作git,努力在git寻找对应物 而 最终因为git的“别扭”而放弃

第5章 暂存区

git最成功的设计之一,也是最难理解的 同一文件可能有三种状态:工作区,暂存区,版本库 git diff git diff --head git diff --cached

git reset HEAD 暂存区目录树被master分支指向目录树重写 工作区不受影响
git rm --cached 直接从暂存区删除文件 工作区不受影响
============ 危险的操作 ==============
git checkout .或
git checkout --
用暂存区替换工作区 清除工作区未暂存操作
git checkout head .或
git checkout head -- <file>
HEAD指向的分支的文件替换暂存和工作区 清除工作区未暂存工作和
暂存区未提交改动

第6章 git对象

什么是head,master? 什么时候可以互换使用? git很多对象以40字节的sha1表示

git log -1 --pretty=raw 除了初始提交,都有三个sha1值 commit 393c463fba7ab6868c49d9a48bad8d500d6a8566 tree 4617a709b688fabb8cf181b50f7ceac079547aee parent f513abd772716211baacf12ff1a5c6d1a08bb528

git cat-file -t sha1 看对象类型 git cat-file -p sha1 看对象内容

.git\objects目录 sha1前两位作目录名,后38位作文件名

git ls-file,cat-file,ls-tree,rev-parse等几个命令要多理解理解 生成sh1相关的几个命令 commit,文件(blob),tree的sha1生成

git rev-parse head git rev-parse blob head:welcome.txt git rev-parse blob :welcome.txt 与上面相比,上面是库中,这个是查暂存区 git rev-parse head head^{tree} 这里的^特别,就是个语法,不是表示父一级

在cmd中不行,要在bash中

git cat-file commit head |wc -c git cat-file blob head:welcome.txt |wc -c git cat-file tree head^{tree} |wc -c

分别输出大小

(print "commit 大小\000"; git cat-file commit head)| sha1sum

(print "commit 大小\000"; git cat-file blob head:welcome.txt)| sha1sum

(print "commit 大小\000"; git cat-file tree head^{tree})| sha1sum

第7章. reset git reset --hard hed^ 在CMD下的BUG,无法回退到前一版本,在BASH中就行

如何拘留由于此步操作导致的提交丢失

reset 默认mixed方式,注意和--soft,--hard对工作区,暂存区的影响,以及在什么时候用

Read More


goagent代理github 2016-01-01

github在镇外,镇内访问慢,goagent直接https访问较快

_netrc文件(*nix下是.netrc)

win7 %userprofile%\_netrc

machine github.com login zhangthe9 password xxxxxxxx

machine github.com login ddatsh password xxxxx

git remote add

git remote add origin git@ddatsh.github.com:ddatsh/blog.git 改成 git remote add origin https://ddatsh@github.com/ddatsh/blog.git

git config http.proxy

git config --global http.sslVerify false git config http.proxy http://127.0.0.1:8087/ 或者直接加进全局 git config --global http.proxy http://127.0.0.1:8087/

另一个逆天的命令,某些工具检出项目时,配的是git协议,到了只支持http代理的地方 git config --global url."https://".insteadOf git://

ssh -T -v git@github.com

ProxyCommand C:/git/bin/connect.exe -H 127.0.0.1:9257 %h %p Host github.com User git Port 22 Hostname github.com IdentityFile "C:\Users\dd.ssh\id_rsa" TCPKeepAlive yes IdentitiesOnly yes

Read More


github API v3 使用 2016-01-01

想用github api提供的功能建个 repo http://developer.github.com/v3/repos/

命令格式: curl -d "post的数据" url

windows版curl引号处理要把 " 换成 \",而且整个命令要写成一行

curl -d "{\"name\": \"库名\" }"  -u "用户名:密码" https://api.github.com/user/repos -v

删除

curl -X DELETE -u "xx:xx" https://api.github.com/repos/用户/库 -v

git clone https://github.com/用户名/库名.git

Read More


git-svn 2016-01-01

Nginx与github

nginx目前源码还是SVN管理,假设把它源码用git svn 弄过来 然后自己在github建个库,存放修改

git svn clone 默认从svn第一个版本开始同步,太慢 svn info http://your-svn 先看最后版本

svn info svn://svn.nginx.org/nginx/trunk 路径: trunk RL: svn://svn.nginx.org/nginx/trunk 版本库根: svn://svn.nginx.org/nginx 版本库 UUID: 73f98a42-aea0-e011-b76d-00259023448c 版本: 4600 节点种类: 目录 最后修改的作者: mdounin 最后修改的版本: 4600 最后修改的时间: 2012-04-18 22:47:10 +0800 (周三, 2012-04-18)

git svn clone -r版本号:HEAD --prefix=svn/ http://your-svn git svn clone -r4600:HEAD --prefix=svn/ svn://svn.nginx.org/nginx/trunkgit svn clone -s -r4600:HEAD --prefix=svn/ svn://svn.nginx.org/nginx

这两个命令区别,前者目录叫trunk,后者就叫nginx

使本地svn分支对应svn的远程仓库,本地master分支对应github的远程仓库 默认在master分支,改名为svn,本地再建个master分支 git branch -m svn git remote add origin git@github.com:user/repoXX.git git checkout -b master

现在可以按照平常的习惯在git下更改代码,然后用git push到github上 如果需要更新代码到svn上,按这个流程操作就可以了: git checkout svn git merge master git svn dcommit

例子2

用户bob

z:
cd \
md repos
cd repos
svnadmin create hello

cd \
svn co file:///z:/repos/hello hello-svn
cd hello-svn
svn mkdir branches tags trunk
svn ci -m init

cd trunk
echo "printf("hello\n");"> hello.c
svn add hello.c
svn ci -m "My first program"

cd ..
svn cp trunk tags/v0.1
svn ci -m "tag v0.1"

Bob 已经建好v0.1 版

Alice 用git-svn 取出 repository

git svn clone --stdlayout file:///z:/repo/hello hello-git 会报错 Couldn't open a repository: Unable to open an ra_local session to URL: Unable to open repository 'file:///z:/repo/hello': Expected FS format '2'; found format '4' at c:\git/libexec/git-core\git-svn line 1343 直接以file:/// 访问不行,启个svn服务,就行了

svnserve -d -r z:\repos\hello

cd \ git svn clone --stdlayout svn://127.0.0.1 hello-git cd hello-git git reset --hard remotes/trunk

使用 stdlayout,所以 tag/branch 等会自动对应 remote branch

cd hello-git git branch -r tags/v0.1 trunk

Alice 更新bob代码后 可以用git add -p 选择要commit的部分,剩下部分stash起来 然后 git svn dcommit提交给bob的svn repository

edit hello.c git add -p git commit -m'Fix everything' git stash git svn rebase 这等于在svn update 检查一下远端是否有其他人checkin了新东西 git svn dcommit

过几天,发现Bob有更新,要同步 同时,有一个local branch,所以在更新完master后,还要把fancy branch跟master rebase

git svn fetch

git svn rebase First, rewinding head to replay your work on top of it... Fast-forwarded master to refs/remotes/trunk.

git checkout fancy git rebase master First, rewinding head to replay your work on top of it... Applying: Inital fancy output branch

Read More


git 设置 2016-01-01

git config --global user.name xx git config --global user.email xx@xx.xx

中文文件名或者路径被转义成\xx\xx\xx之类 git config --global core.quotepath false

no fast forward 提交 git config --global merge.ff false

走goagent代理 git config --global http.proxy http://127.0.0.1:8087 git config --global http.sslVerify false

Read More


git永久删除文件(包括历史记录) 2016-01-01

注意Windows下用双引号

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch path/to/your/file' HEAD
git push origin master --force
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

参考

GitHub的帮助页面Removing sensitive data

http://yihui.name/cn/2010/12/animation-update-1-1-5

http://blog.csdn.net/meteor1113/article/details/4407209

两者区别是 --index-filter 比--tree-filter 效率更高

Read More


git merge –squash介绍 2016-01-01

Git相对于CVS和SVN的一大好处就是merge非常方便,只要指出branch的名字就好了,如:

$ git merge another $ git checkout another modify, commit, modify, commit ... $ git checkout master $ git merge another

但是,操作方便并不意味着这样操作就是合理的 在某些情况下,我们应该优先选择使用--squash选项,如下:

$ git merge --squash another $ git commit -m "message here"

--squash选项的含义是:本地文件内容与不使用该选项的合并结果相同,但是不提交、不移动HEAD,因此需要一条额外的commit命令。其效果相当于将another分支上的多个commit合并成一个,放在当前分支上,原来的commit历史则没有拿过来

判断是否使用--squash选项最根本的标准是,待合并分支上的历史是否有意义

如果在开发分支上提交非常随意,甚至写成微博体,那么一定要使用--squash选项 版本历史记录的应该是代码的发展,而不是开发者在编码时的活动

只有在开发分支上每个commit都有其独自存在的意义,并且能够编译通过的情况下(能够通过测试就更完美了),才应该选择缺省的合并方式来保留commit历史

from

Read More


git只保留最新代码,删除历史记录 2016-01-01

git cat-file commit master^X | sed -e '/^parent/ d' > tmpfile
git rebase --onto $(git hash-object -t commit -w tmpfile) master
rm -f tmpfile

其中X是要保留的记录条数 这个时候,你的log里已经没有历史的提交了,但是历史的数据还存在于本地,| 要想完全删除的话,执行以下代码

rm -rf .git/logs
git gc 

注意,这里只对master进行了操作,如果你还有其它branch或tag,都需要类似于这样地处理一遍.

实战的时候,貌似有点不对的地方 填了数值后,是回退到master ^n 的地方了

Read More


git flow 2016-01-01

http://zeroq.me/p/451 http://www.cnblogs.com/wubaiqing/archive/2011/12/18/2271724.html

git flow 安装

git clone --recursive git://github.com/nvie/gitflow.git gitflow 此命令连同包含的submodule一同下载

http://gnuwin32.sourceforge.net/packages/util-linux-ng.htm 下载util-linux package,包含需要用的getopt.exe,libintl3.dll,直接copy到git\bin

gitflow\contrib\msysgit-install.cmd

git flow使用经验小记 http://www.ooso.net/archives/588

实际使用过程中有一些小小的意外流程,完全照搬git flow的模型不太容易处理好 好在git本身就很灵活,碰到问题基本上都有办法绕过去

一些特例情况下的处理办法

测试/共享单独一个feature

有时候我们需要将一个feature独立测试,或者share给多人一块开发,那么可以将这个feature推到远程git库上,这可以利用git flow的publish功能搞定:

git flow feature publish my_cool_feature

这会将 feature/my_cool_feature 分支push到远程git库,多人开发或者单独测试毫无压力

feature在development分支测试完成,准备release的时候有另外一个未经测试的feature合并进来

已经完成测试的development被未经测试的提交污染了,这时候可以先本地回滚development分支,然后再进行git flow的release流程

git checkout development git reset --hard 测试通过的git rev git flow release start v1.0.1 git flow release finish v1.0.1

development上有个feature需要测试比较长时间,影响了一些耗时较短的feature发布

development分支上有个feature测试时间比较长一直释放不了,怎么办?—— 果断采用hotfix功能

git branch -m feature/another_cool_feature hotfix/another_cool_feature

把耗时短的feature直接转换为hotfix,然后采用git flow的hotfix流程可以直接合并到master分支发布

another

http://ihower.tw/blog/archives/5140/comment-page-1#comment-60322

push 一个feature branch 到远端: git flow feature publish some_awesome_feature 本质是git push origin feature/some_awesome_feature

追踪一个远端branch: git flow feature track some_awesome_feature 本质是git checkout -b feature/some_awesome_feature -t origin/feature/some_awesome_feature

刪除远端branch: git push origin :feature/some_awesome_feature

git flow feature finish 出现错误:

warning: not deleting branch 'feature/some_awesome_feature' that is not yet merged to 'refs/remotes/origin/feature/some_awesome_feature', even though it is merged to HEAD. error: The branch 'feature/some_awesome_feature' is not fully merged. If you are sure you want to delete it, run 'git branch -D feature/some_awesome_feature'.

原因是这个 feature branch 一开始是从远端 checkout 出来的,以及这个 feature branch 有 commit 没有 push 回去 所以 git flow 不敢帮你删除 local branch,这时候其实 merge 动作已经完成了 所以可以手动输入 git branch -D feature/some_awesome_feature 强制删除 local branch 即可 (小结论:git-flow 只是个辅助工具,了解 git 还是必要的)

关于 feature branch 的合并

如果是开发时间比较久的 feature branch,很可能会因为 1. 不定时的 merge develop 与新版同步 2. 实验性质的修改 3. 需求的变更 等等因素,而让这个 feature branch 的 commit 记录变成脏的,这时候用以下的方式来做 merge 动作:

  1. 先对 feature branch 做 git rebase develop 会很苦,但是弄完会很有成就感,整个 branch commit history 会变成很干净 请学 interactive mode,可以让你拿掉一些 commit、合并或修改,可以 rebase 多次直到满意为止

  2. 在从 develop bracnh 做 git merge feature/some_awesome_feature –no-ff,–no-ff 的意思是会强制留一个 merge commit log 记录 这可以让 commit tree 看清楚发生了 merge 动作 (因为刚做了 rebase,而 git 默认的合并模式是 fast-forward,所以如果不加 –no-ff 是不会有 merge commit 的) 这个 merge commit 的另一个额外方便之处是,如果想要 reset/revert 整个 branch 只要 reset/revert 这个 commit 就可以了

  3. 如果此 feature branch 有 remote branch,要先砍掉 git push origin :feature/some_awesome_featuregit push origin develop (这是因为 rebase 一个已经 push 出去的 repository,然后又把修改的 history push 出去,会造成超级大灾难啊~) 先 rebase 再 merge –no-ff 这样做的好处到底是什么?

merge

每一次的 merge 就代表了一个 feature 完成,也可以很清楚看到这个 feature branch 底下包含哪些 commit

Git and Github 演講投影片

http://ihower.tw/blog/archives/5391

Read More


CentOS git 编译 2016-01-01

rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm cd /etc/yum.repos.d/ wget http://wing-repo.net/wing/6/EL6.wing.repo yum --enablerepo=wing install -y git

CentOS 6.5 官方yum库git版本较老 1.7.7 stash 才支持untracked 的文件 git stash -u

wget https://www.kernel.org/pub/software/scm/git/git-2.1.2.tar.gz

参见源码中INSTALL文档 $ make prefix=/usr all doc info ;# as yourself # make prefix=/usr install install-doc install-html install-info ;# as root

LINUX系统安装时不要选上git 安装几个依赖包,否则make 时报错 yum install -y gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker

完整安装还需要asciidoc,docbook2X

其中make info 时需要的docbook2X,不在centos iso中

http://fedoraproject.org/wiki/EPEL

wget http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

rpm -Uvh epel-release*rpm

yum install docbook2X

安装好了docbook2x

cd /usr/bin

ln -s db2x_docbook2texi docbook2x-texi

不然make info时

/bin/sh: line 1: docbook2x-texi: command not found

无需设置环境变量,git 命令直接能用

编译时不要在中文目录

参考

为CentOS/RHEL添加EPEL软件仓库

http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-2.noarch.rpm

Read More


使用Git的分支和合并功能来进行版本管理的开发模型 2016-01-01

git-workflows-and-tutorials https://github.com/oldratlee/translations/tree/master/git-workflows-and-tutorials

分支的使用策略和发布管理

Git从根本上改变了开发人员对分支和合并的使用 传统的CVS/SVN,分支和合并都是高级话题,而且使用起来稍显麻烦,隔一段时间才会用一次 但是有了Git,这些操作就成了家常便饭

由于使用简单,方便重复操作,分支和合并不再是让人望而生畏的操作,版本管理工具应该尽可能地对分支/合并提供最好的支持

此模型其实是一些每个开发人员都应该遵守的步骤,如果想管理好软件的开发流程的话

分布式但集中化

git技术层面没有中心库,实际管理时有被公认的"中心库"origin,Git用户都熟悉这个名字

每个开发者pull和push到origin,但除了中心化的push-pull关系外,每个开发者还可以从其他开发者那pull changes 比如说,对于一个比较大的新特性,在把代码提交到origin之前,很可能会安排2个或多个开发者 上图中有几个小团队:Alice和Bob,Alice和David,Clair和David

从技术角度来说,其实就是Alice定义了一个叫Bob的Git remote,指向到Bob的仓库

main分支

中心仓库有两个分支:

  • master
  • develop

origin上的master分支,Git用户应该很熟悉,跟master并行的有一个develop分支

origin/master作为主要分支,源码的HEAD总是表示production-ready(可随时部署)状态 而origin/develop上的代码是为下一次的代码发布准备的。每日构建也是基于此分支

develop分支达到稳定状态并准备发布时,所有的改变都要合并到master分支,并标上版本号。如何实现的下面细说

这样每次与master合并都会有新的部署发布。这点可以自动化,如使用Git hook脚本来实现自动部署代码到线上

支持(supporting)分支

开发模型使用了一些支持分支放在master和develop分支的旁边,方便开发小组之间的并行开发 不像main分支,这些分支是有时间限制的,因为他们最终都会被移除

会使用到的不同的分支

  • Feature branches
  • Release branches
  • Hotfix branches

每个分支都有各自的作用,并且有严格的规定,如:只能从哪个分支上去新开分支,只能合并到那个分支。这个待会细说

Feature branches

继承分支: develop 合并分支:develop 命名规范:除了master,develop,release-,hotfix-

Feature branches是用来开发新特性的(短期,远期都可以)。当开始开发新特性时,很可能不知道这个特性会出现在哪个目标版本。一旦开发完成就可以合并到develop,当然如果开发失败,就可以抛弃

创建一个 Feature branch

当要创建一个新特性时,从develop分支上再进行分支

git checkout -b myfeature develop Switched to a new branch "myfeature"

新特性完成时,可以合并到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 (译者注:no fast foward)标签,使得每一次的合并都创建一个新的commit记录。即使这个commit只是fast-foward,这样可以避免丢失信息

不幸的是,我没有找到让—no-ff成为commit默认参数的方法(译者注:修改.consolerc?),但确实应该提供一个方法

Release branch

继承分支: develop 合并分支:develop 和 master 命名规范:release-*

Release branch 是为新的production release准备的(译者注:相当于RC版),可以有一些小的bug,并为发布准备一些元数据(版本号,构建日期等等)。把所有的这些工作都放到 Release branch,develop branch就能更清晰地知道下一个版本要开发哪些特性

从develop分支合并到release分支的关键因素是:develop分支达到了release分支所要求的状态。至少所有针对该release的特性要被合并。至于那些将来会有的特性可以先放一放。然后就是为接下来即将要发布的版本分配一个版本号

创建一个Release branch

Release branch是通过develop分支而创建。举个例子,假如1.1.5是当前的production release,然后会有一个比较大的版本发布。develop的状态已经可以发布版本了,经过商榷后,决定发布为1.2版本(而不是1.1.6或2.0),所以我们创建一个release分支,并给这个分支一个新的版本号

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(-)

这个新分支可能会存在一定的时间,直到可以被合并到production branch。这段时间内,bug修补可以在这个分支上进行(而不是develop分支)。添加新特性(尤其比较大的)是不允许的。最后还是要被合并到develop,然后继续在develop分支上开发,直到下一个版本

完成一个release branch

当release branch已经准备就绪,需要做几件事。首先,release分支被合并到master分支上(每一个提交到master上的commit都是一个新版本,切记)。然后master上的commit都要添加tag,方便将来查看和回滚。最后release上所做的修改必须合并到develop分支上,保证bug已被修补

前两个步骤: 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上的改变保存到develop,我们需要合并到develop git checkout develop Switched to branch 'develop' git merge --no-ff release-1.2 Merge made by recursive. (Summary of changes)

这个步骤可能会导致冲突,如果这样的话,解决冲突,然后再提交

现在一切都完成了,可以把release branch干掉了 git branch -d release-1.2 Deleted branch release-1.2 (was ff452fe).

Hotfix branch

继承分支: master 合并分支:develop 和 master 命名规范:hotfix-*

Hotfix branch和Release branch有几分相似,都是为了新的production release而准备的。比如运行过程中发现了bug,就必须快速解决,这时就可以创建一个Hotfix branch,解决完后合并到master分支上。好处是开发人员可以继续工作,有专人来负责搞定这个bug

创建Hotfix branch

Hotfix是从master分支上创建的。假如当前运行版本是1.2,然后发现有bug,但是develop还在开发中,不太稳定,这时就可以新开一个Hotfix branch,然后开始解决问题 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(-)

解决问题,一次或几次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 branch

当结束时,bugfix要被合并到master,同时也要合并到develop,保证下个版本发布时该bug已被修复。这跟release branch完成时一样

首先更新master和tag 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

接下来与develop合并 git checkout develop Switched to branch 'develop' git merge --no-ff hotfix-1.2.1 Merge made by recursive. (Summary of changes)

有一个例外,就是当一个release branch存在时,bugfix要被合并到release而不是develop,因为release最终会被合并到develop

最后移除branch git branch -d hotfix-1.2.1 Deleted branch hotfix-1.2.1 (was abbe5d6).

总结

这个开发模型其实没有什么新颖的。这是很优雅的一个模型,很容易实现,也容易在团队成员之间达成一致

Read More


git 2016-01-01

proxy

git socket 代理 git config http.proxy = socks5://127.0.0.1:1080

msysgit git pull/push底层用的curl 设置环境变量  export all_proxy=socks5://127.0.0.1:1080 也可

git config --global url.https://.insteadOf git://.

[core] quotepath = false whitespace = cr-at-eol

git config --global alias.acp "! git commit -a -m "commit" && git push"

https://github.com/geeeeeeeeek/git-recipes/wiki/2.7-%E9%87%8D%E5%86%99%E9%A1%B9%E7%9B%AE%E5%8E%86%E5%8F%B2

Read More


gc 2016-01-01

gc是一种失败的内存管理模式? https://www.avboost.com/t/gc/256

C时代编译器水平低下,要程序员掌握内存管理细节. 虽获得最大灵活性, 但手工管理不好管控 编译器技术发展, 从程序员手中接管内存管理任务

如何接管内存管理

垃圾收集 RAII (Resource Acquisition Is Initialization) + 智能指针 在有GC的语言里, 程序员只管分配内存, 无需操心释放. 垃圾收集器间歇性运作, 释放无用内存 "没有哪一种垃圾收集策略是适合所有程序的, 所以各种语言都发展出多套垃圾收集器, 供运行时选择"

许多语言里, 编译器不实现垃圾收集,语言附带的运行时环境实现, 编译器为运行时提供附加的信息 导致语言和运行时的强耦合,让人无法分清语言的特性和运行时的特性 垃圾收集不是完美的, 使用垃圾收集并不意味着就可以高枕无忧了. 垃圾收集并不意味着内存泄漏成为过去式, 倒是野指针确实成为了过去式, 因为只要还有指针引用一个对象, 这个对象就绝对不会被释放. ( 带有垃圾收集的语言或多或少都废除了指针, 用引用替代了指针)

有很多很多复杂的原因丢会导致垃圾收集器无法回收特定的内存, 导致这部分内存泄漏. 更严重的是, 很难将内存泄漏和还未被清除的内存完全区别开来. 到底是延迟收集策略 还是真的发生了内存泄漏 ? 永远都无法正确分辨

结果是, 程序员最终不得不回到 C 语言的老路上, 小心的检查所有的内存分配, 确保没有触发垃圾收集器的bug或者特定的一些策略 几乎所有使用带垃圾收集的语言开发的程序, 在其开发的后期都要经历惨痛的 "内存检查" , 回顾所有可能导致内存泄漏的代码

垃圾收集器的另一个问题是, 除了内存, 它无法对程序使用的其他资源执行垃圾收集 垃圾收集是以内存管理为目标产生的, 只能收集不再使用的内存, 而不能收集程序使用的其他资源, 如消息列队, 文件描述符, 共享内存, 锁.等等 程序员不得不对其他资源执行手工管理, 像 C 程序员那样小心翼翼的操作

最终垃圾收集仍然没有解决 "人容易犯错" 的问题,还是把其他资源的泄漏问题丢给了程序员


C 派因为喜欢 "自己控制一切" (天生的 M 属性) C++ 派同样认为,要把程序员从资源管理的重担里解放出来,C++ 做了很多思考, 资源管理逐步发展,并最终经历了 30年的时间终于找到了解决的办法(智能指针). 写入了 C++11 标准 "投机取巧" 的 GC派(java)

C++ 要想实现 RAII + 智能指针, 两大技术缺一不可 1. 自动确定并调用 构造函数和析构函数 2. 模板

C++ 的第一步试图解放资源管理重任, 是为 C 加入了构造函数和析构函数 构造函数和析构函数由编译器调用, 生命期终止的对象会自动调用析构函数. 不管生命期终止的原因是 return 返回, 还是 抛出了异常, 编译器总是保证, 生命期终止的对象一定会被调用析构函数

以 "编译器自动保证对象生命期" 的技术依托下, C++ 发明了 RAII 技术, 将资源的管理变成了对象的管理,而自动变量 (创建在栈上的对象, 类的成员变量) 的生命期由编译器自动保证, 只要在构造函数里申请资源, 在析构函数里正确的释放资源, RAII 技术解决了一大部分的资源管理问题

模板的引入使得 RAII 技术得以 "一次实现到处使用". 如实现一次 std::vector 就可以到处使用在需要数组的情况下, 而无需为每种类型的分别实现数组RAII类. STL 内置了大量的容器, 几乎满足了所有的需求. STL的容器无法满足需求的情况下, 程序员仍然能借用 STL 的理念实现自己的 RAII 容器

但是, 如果对象分配于堆上, 程序员不得不手工调用 delete 删除对象. 忘记将不用的对象 delete 就成了头号资源泄漏原因

如果指针也是自动对象就好了

C++ 标准的第一次尝试是纳入 std::auto_ptr . 但是效果并不好, 不是所有指针都可以为 auto_ptr 所代替. 最要命的是, STL 容器无法使用 auto_ptr

C++ 标准的第二次尝试就是纳入了 std::shared_ptr , shared_ptr 在进入 C++11 标准之前, 已经在 Boost 库里实践了相当长的时间

首先得益于 C++ 的模板技术, shared_ptr 只需实现一次, 即变成可用于任何类型的指针 其次, 得益于 C++ 的自动生命期管理, 智能指针将需要程序员管理的堆对象也变成了能利用编译器自动管理的自动变量

也就是, 智能指针彻底的将 delete 关键字 变成了 shared_ptr 才能使用的内部技术 编译器能自动删除 shared_ptr 对象, 也就是编译器能自动的发出 delete 调用

模板是智能指针技术必不可少的一部分, 否则要利用 RAII 实现智能指针就只能利用 "单根继承" 这一老土办法了. 没错, 这也是 MFC 使用的. ( MFC 诞生在 模板还没有加入 C++ 的年代.)

直到 1998 年 C++ 标准纳入了模板, C++ 才最终具备了实现自动内存管理所必须的特性 但是准备好这些特性, 到利用这些特性发明出真正能用的智能指针, 则又花了13年的时间.( 2011年加入了 shared_ptr) 发明出编译器实现的自动内存管理需要时间, C++ 花了 30年的时间. 没有耐心的语言走了捷径, GC 就是这条捷径

读后感

语言的发展是一个实践+平衡的过程 通过实践,才能知道设计的功能别人是会怎么用,实际效果如何 通过平衡,才能让大家都接收认可并共同执行这一标准

c++相对c,为实现提供了很多支持,但设计中很多oop的支持,在实际应用中最终几乎成了噩梦,感觉永远无法达到完美或实现理想

"人容易犯错",无法保证团队中每个人都能充分理解C++那庞大琐碎的OOP实现,无法保证每个人都小心使用每个看起来很炫的语法糖

经过这么多年的实践,最终,人们发现C++的模板才是最适合C++实现解决未来应用的。虽然设计之初的功能其实很朴素

gc也是一种尝试,它有效缓解了开发人员水平不一致的问题,随着gc技术的发展,人们似乎找到了灵丹妙药 最终我们发现,它看起来很美好,但仍然无法彻底解决资源管理问题 人们为了避免那隐蔽的泄漏,不得不去了解gc的实现底层,在开发时,无意中仍然有可能引发资源的长期占用得不了释放的问题 而且gc的研究似乎进入到了一个瓶颈,无法再有大的发展了,所能进步的也只是少许改良

C++中的自动内存管理最终能发一展到什么地步,现在不能下定论,但目前来看,能比gc走得更远?

使用现代C++方式编程,了解shared_ptr指针用法,内存泄露将成为历史 深有感触,boost智能指针家族的成员shared_ptr, scoped_ptr, weak_ptr, 需要好好学习下 我觉得GC是一个pythonic的设计,有着pythonic的优势(简洁)和pythonic的缺陷(在大单位上破坏设计)。smart_ptr优点多多,可是要参与一个项目的成员都使用自如,应该不简单 C++11 支持度还有限, 还是 boost::shared_ptr 靠谱. 哪哪都能用

忍不住吐槽一下……

只说了reference counting的优点,缺点也是非常明显的。选择GC并非仅仅是“捷径”,而是reference counting需要太多的人为控制才能work

环形引用。这时候reference counting彻底失效。要人为构造weakref来解决。 效率问题。每次ref()/unref()(也就是shared_ptr构造/析构的时候),需要atomic_inc()一个counter,这个操作在NUMA上会有效率问题 你说的resource leak在GC下是个大问题是因为

GC和RAII是冲突的 当resource leak发生时,在GC的环境下大多都是implicit的,而在reference counting的时候都必须是explicit。其实只是让问题更容易呈现而已 reference counting是折中的方案,而且最容易实现。没什么“伟大”的地方。而且,据我知道的来说,boost::shared_ptr/std::shared_ptr除了其中atomic operation可能会用到barrier以外,剩下的都是C++/C++-11标准语法,和编译器没有任何关系。

GC的效率问题很难说。要看你的程序是怎么样的 GC的overhead是clean up的时候的overhead。而不是accessing overhead。access是critical path。所以相比之下,reference counting未必就有优势。比如我创建了一个图,然后在上面跑两天的挖掘。这时候如果你都用C++(比如bhgc),那么GC的次数是0,如果reference counting的话,就很吃亏了 GC的主要问题不是overhead而是pause。如果说overhead,那么malloc和free也有overhead GC的最大问题,除了你说的resource leak以外,我觉得应该还有不知什么时候pause。但是相比之下,reference counting也没有你说的那么完美。GC的实现难度比reference counting要难很多,对这个问题的解决也进一步,只是藏起来了更多问题,让程序员失去了控制。而C++是一种强调可控性的语言,所以选择了reference counting。但这并不意味着强调不可控的语言不需要GC

C/C++中直接操作内存出现的问题如,内存泄漏,访问越界,一般都是可以根除的,即使没有根除,也可以不断的FIX中接近于没有内存问题 基于GC的语言(如Java),在语言层回避内存管理问题,丢给jvm处理,因为GC的不确定性,所以GC中出现的问题你基本无法解决,任何的FIX只是止痛药式的补丁,不能从根本上解决GC带来的问题 内存管理是"本质复杂的问题",最好的解决办法是直接面对(也就是手动管理内存),这样的可控性和灵活性更高

Read More


从自由门改成GAE翻墙 2016-01-01

用的好好的自由门,突然无法搜索服务器了 GAE很早前就注册了,一直没有用,今天终于派上用场了

访问https时注意两点 浏览器中的证书导入ca.crt java编程涉及https访问也要导入ca.crt到ava系统的证书库,否则The site's security certificate is not trusted!

keytool -import -alias cacerts -keystore "C:\Program Files\Java\jdk1.7.0_03\jre\lib\security\cacerts" -file D:\soft\goagent\local\ca.crt -trustcacerts

默认密码是changeit

输入密钥库口令: 密钥库口令太短 - 至少必须为 6 个字符 输入密钥库口令: keytool 错误: java.io.IOException: Keystore was tampered with, or password was incorrect C:\Users\Administrator>keytool -import -alias cacerts -keystore "C:\Program Files\Java\jdk1.7.0_03\jre\lib\security\cacerts" -file D:\soft\goagent\local\ca.crt -trustcacerts 输入密钥库口令: 所有者: L=Cernet, C=CN, ST=Internet, CN=GoAgent CA, O=GoAgent, OU=GoAgent Root 发布者: L=Cernet, C=CN, ST=Internet, CN=GoAgent CA, O=GoAgent, OU=GoAgent Root 序列号: 0 有效期开始日期: Thu Apr 21 01:37:35 CST 2011, 截止日期: Mon Apr 21 01:37:35 CST 2031 证书指纹: MD5: 56:B1:20:86:1B:0A:B0:61:38:00:1B:C3:67:CF:0C:CC SHA1: AB:70:2C:DF:18:EB:E8:B4:38:C5:28:69:CD:4A:5D:EF:48:B4:0E:33 SHA256: F1:61:5F:9A:1C:7D:53:05:B9:63:B9:82:9B:03:AD:46:6B:66:D9:B9:07:F0:01:B0:F3:B0:6A:C8:A5:CF:B4:1C 签名算法名称: SHA1withRSA 版本: 1 是否信任此证书? [no]: yes 错误的答案, 请再试一次 是否信任此证书? [no]: y 证书已添加到密钥库中

Read More