Inspiration from life

about

rebase之后如何避免使用git push -f来提交代码的规范流程

10 Apr 2014

这篇blog的内容是我在segmentfault上的一个回答,转帖过来。

提问者提问的大意是,他们团队成员为了提交历史的干净整洁,所以经常会git rebase,但是这样在提交代码的时候,就会频繁的发现冲突,只能使用git push -f来强制提交,不知道是否合理。

以下是我的回答:

git rebase是对commit history的改写。当你要改写的commit history还没有被提交到远程repo的时候,也就是说,还没有与他人共享之前,commit history是你私人所有的,那么想怎么改写都可以。

而一旦被提交到远程后,这时如果再改写history,那么势必和他人的history长的就不一样了。git push的时候,git会比较commit history,如果不一致,commit动作会被拒绝,唯一的办法就是带上-f参数,强制要求commit,这时git会以committer的history覆写远程repo,从而完成代码的提交。虽然代码提交上去了,但是这样可能会造成别人工作成果的丢失,所以使用-f参数要慎重。

楼主遇到的问题,就是改写了公有的commit history造成的。要解决这个问题,就要从提交流程上做规范。

举个正确流程的栗子:

假设楼主的team中有两个developer:tom和jerry,他们共同使用一个远程repo,并各自clone到自己的机器上,为了简化描述,这里假设只有一个branch:master

这时tom机器的repo有两个branch master, origin/master 而jerry的机器上也是有两个branch master, origin/master

均如下图所示

tom和jerry分别各自开发自己的新feature,不断有新的commit提交到他们各自私有的commit history中,所以他们的master指针不断的向前推移,分别指向不同的commit。而又由于他们都没有git fetchgit push,所以他们的origin/master都维持不变。

jerry的repo如下

tom的repo如下,注意T1和上图的J1,分别是两个不同的commit

这时Tom首先把他的commit提交的远程repo中,那么他本机origin/master指针则会前进,和master指针保持一致,如下

远程repo如下

现在jerry也想把他的commit提交到远程repo上去,运行git push,毫无意外的失败了,所以他git fetch了一下,把远程repo,也就是之前tom提交的T1给拉到了他本机repo中,如下

commit history出现了分叉,要想把tom之前提交的内容包含到自己的工作中来,有一个方法就是git merge,它会自动生成一个commit,既包含tom的提交,也包含jerry的提交,这样就把两个分叉的commit重新又合并在一起。但是这个自动生成的commit会有两个parent,review代码的时候必须要比较两次,很不方便。

jerry为了保证commit history的线性,决定采用另外一种方法,就是git rebase。jerry的提交J1这时还没有被提交到远程repo上去,也就是他完全私有的一个commit,所以使用git rebase改写J1的history完全没有问题,改写之后,如下

注意J1被改写到T1后面了,变成了J1`

git push后,本机repo

而远程repo

异常的轻松,一条直线,没有-f

所以,在不用-f的前提下,想维持树的整洁,方法就是:在git push之前,先git fetch,再git rebase

git fetch origin master
git rebase origin/master
git push

强烈推荐阅读

Google chromecast伪开箱

23 Aug 2013

这是一篇伪开箱,因为这个chromecast不是我买的,而是公司老大的,借我用段时间。Google刚发布这货的时候,老大就买了,再后来Google Play,Amazon,Bestbuy全线缺货,看样子挺火。

Chromecast是Google于今年7月24号发布的一个小型流媒体播放器,它可以把电脑、手机、平板上的Web和视频内容同步到电视上播放。它接入WIFI后,插入电视的HDMI接口,播放同步过来的多媒体内容。相比之前发布的Google TV,它体积小巧,没有多余的功能,售价仅35刀。Google野心不死,就是想占领大家的客厅。

老婆喜欢在优酷上看美剧,所以我要用电脑的时候,只有一台Macbook就有点捉襟见肘了。之前我一直在纠结,到底是买一个chromecast,还是chromebook,又或Mac Mini。Mac Mini有点奢侈,chromebook的显示器就是多余,chromecast又担心不支持chrome Unblock Youku的插件,所以一直没下手。这次能先试用一下chromecast,真是一个非常不错的机会。

Chromecast的包装很简单,小小的白色盒子再加上一些蓝色的点缀,非常符合Google一向简约的风格。

背面上的四个图标表示Chromecast支持Chrome浏览器,Youtube,Netflix,Google Play。

Chromecast静静地躺在盒子里

使用Mini USB接口供电,包装里有附带一根USB转Mini USB线

安装好后,首先需要设置一下,安装Chrome extension或者Android app来设置,步骤很简单,跟着提示一步一步来即可。

经过一段时间的使用,我发现Android手机上的Youtube的同步播放很流畅,Chrome的Tab casting则有些延迟,而且对电脑的性能和WIFI要求比较高。只要我在电脑上运行大型软件,电视上的视频就会有些卡顿,而且若WIFI信号不好或者本身带宽不足的话,则也会有些卡顿。所以猜想,Android上只是把视频的地址信息同步到Chromecast,然后它自己去下载,播放。而Tab casting则是在电脑上渲染,然后镜像过去的。

Chromecast在Android上同步的话,功能也很弱,比如没法显示字幕,选择视频质量等。我播放了大概30多分钟,手机的电量没有明显的下降,手机也没有发烫,进一步印证了刚才手机不是镜像播放的猜想。

我个人感觉,Chromecast还不太完善,我可能不会考虑它了,不过想想它的售价,貌似也是物有所值,哎,看来还是要继续纠结下去了。

佳能430ex II闪光灯开箱

20 Aug 2013

我又败家了,入了个佳能430ex II。美国物流速度奇快无比,11号下的单,过了9天居然就收到了,欣慰。。。

送过来的包裹

打开

Canon 430ex II和Invoice

打开包装,有闪光灯,说明书,保修卡,底座,袋子

背面

正面

红外线接收??

只有扩散板,木有眼神板了

灯头

灯头旋转按钮,按下去了才能转

操作盘,比600ex手感差些,操作也稍显繁琐

旋转刻度

闪光灯固定锁,好像一代是旋钮

和热靴接触的地方是金属,好像一代是塑料的

电池仓

电池伺候

点亮

小试一张双闪

最后,和大哥来张合影

推荐我所用的Google Chrome扩展

14 Aug 2013

Chrome是一个伟大的浏览器,若再加上适合自己使用的扩展,那更是如虎添翼。这里就介绍一些我正在使用的Chrome Extension:

Block Yourself from Analytics

我的blog目前使用Google Analytics来监控网站的访问量,此扩展可以让Google Analytics忽略自己的Page View。也支持定义URL规则,只有在规则中的URL才会忽略。

Change Colors

有些网站的配色实在是废眼神,此扩展可以自定义页面的背景和字体颜色,并且会记住自定义内容,再次访问时会自动应用。

Don't track me Google

Google的搜索结果页面,搜索结果链接会指向Google自身的URL,然后再导向目标页面。Google通过这个中间URL,来对用户的行为做记录分析。此扩展可去掉中间URL,让用户直接跳转到目标页面。其实我并不是怕Google监控我的隐私,只是在天朝,GFW大部分时间墙的是这个中间URL,若搜索了敏感关键字,用户就会挂在这个中间URL中,根本跳转不过去;去掉了中间URL的话,Google使用起来会顺畅的多。

Google Calendar Checker (by Google)

此扩展会在Chrome右上角显示Google Calendar中最近的事件。点击它会打开Google Calendar。

Google Keep

云笔记,笔记还支持图片,背景颜色。Android也有客户端可以下载。

Google Mail Checker

此扩展会在Chrome右上角显示Gmail中未读邮件数。点击它会打开Gmail收件箱页面。

Google Tasks (by Google)

简单方便的To Do List。

PageRank Status

浏览网站时,我有一个习惯,就是查看它的Page Rank,这样我可以大概判断出此网站的权威性等。此扩展可在Chrome的右上角显示当前Tab页面的Page Rank,点击按钮还可在弹出窗口里查看更多的信息,比如各个搜索引擎的收录情况,Alexa流量排名等。

Proxy SwitchySharp

天朝上网很悲剧,国外“反动”网站动不动就被GFW墙掉。此扩展可以自定义规则,满足A规则的页面用Proxy 1,满足B规则的页面用Proxy 2,其它页面则不用Proxy而是直接访问。这样,既可以用Proxy翻墙,国内网站也不用到Proxy上转一圈再回来了。

Secure Shell

此扩展可以在Chrome中直接用SSH访问Linux,在Windows平台上的Chrome比较有用。

Tampermonkey

此扩展可以说它是一个扩展的扩展,它可以加载第三方的脚本,来完成不同的功能。比如改变Facebook的背景颜色,天涯只看楼主等等。

Unblock Youku

优酷有些视频(比如很多美剧)只针对中国大陆用户访问,若身在墙外,又想看这些视频的话,装上此扩展再配合Tampermonkey,既突破了这个限制,又可以把优酷恶心的30秒广告屏蔽了。

Joshua tree国家公园

04 Aug 2013

准备了一个月,终于等到了这次出游。下午3点30从家出发,总共开了130mi,终于到了公园的北入口,大概是5:50的样子,门口收费的人居然下班了,大开着门,没人收钱,我们只好大摇大摆的开进去,开始寻找camping的地点。


View Larger Map

把tent等搞定后,已是太阳落山之时,于是我们抗起相机去拍日落。沙漠里的日落很好看,只可惜稍微晚了点,太阳已经被山石给挡住了,只能拍一些晚霞,下面的这张照片就是这时拍的。

joshua tree national park

太阳完全落山之后,回营地吃东西,不知不觉星星就出来了,公园远离人类居住区,没有光污染,空气也特别的干净,再加上特意选了一个月亮小的周末,所以星星看的特别清楚,还有银河,朋友不说的话,我还以为那只是几片薄云。躺在地上,看着天上满头密密麻麻的星星,还有大片的银河,真是心旷神怡的感觉。只可惜忘了带快门线,无法用B门,所以星星和银河都没有拍下来,相当的郁闷。

星星看够了,睡觉吧,隔壁的老美邻居们素质奇低,闹到凌晨1点,早上7点又开始放音乐,害得我们一晚上都没休息好。

起床,收拾,吃东西。我们又去走trail,这沙漠风格的公园很有特色,太阳毒,天空蓝,石头山上光秃秃,偶尔的几颗植物也是濒临干死的那种状况。我们没走多远,水到是喝了不少。这种地方看看新鲜还是不错,但是应该不会来第二次了,我还是比较喜欢山清水秀的那种。

再去一个山顶远眺,哇塞,视野真好。据介绍的牌子上面说,在山顶可以看到美墨边境。。。这是我用手机在山顶拍的panorama。

joshua tree national park

下了山,走南门上10号高速回家,路上还顺路看了一个全是仙人掌的地方,那些仙人掌,居然都有很多被晒死了。。。

回家又开了2个小时,累死哥了。。

把blog迁移到jekyll

02 Aug 2013

jekyll

自从上次看小舟的blog,说他抛弃了wordpress,我就开始心痒痒了。折腾是我的一大爱好,我用的blog平台,从最开始blogcn,到自建movable type,再到wordpress,现在又到jekyll了。

和wordpress不同的是,jekyll是一个使用ruby开发的静态文件生成程序,理论上它可以用来生成任何类型的网站,更别说blog了。每次内容有更新时,我们就用jekyll按照自定义的格式重新生成一次静态的html,然后再把这些html文件部署到host上,访客就可以看到最新的内容了。

相比于wordpress,jekyll的优势

  • 不用数据库,内容是基于文件的
  • 页面是预先生成好的静态html,无需动态渲染,因此加载速度快
  • 对host要求不高,只需能发布html即可
  • 备份方便,因为只是一堆的文件,想怎么备份就怎么备份,打包zip,存git等,悉听尊便

劣势

  • 折腾,不适合大众
  • 页面内容的展现形式,灵活度大大降低
  • 默认主题略显简陋,也没有什么可以拿来就可以用的主题,更别要说美观了
  • 发布麻烦,不像wordpress,在后台写完了保存就可以了

本来打算直接host在github pages上的,结果github的jekyll版本是1.0.3,这个版本在分页上有个重大bug,而且不支持plugins,所以只能部署在自己的vps上了。

花了几天的时间,折腾了不少内容:把文章和图片从wordpress导过来,维持post之前的permalink不变,分页,tag,代码高亮显示等。接下来还有不少可以折腾的空间,比如弄一个git hooks,内容有变化就自动build、发布,写一个responsive的主题,tag cloud,SEO等。很多很多,就怕想不到,这下有意思了。

昨晚,我把域名指向了这个blog,而这,就是我用markdown写的第一篇blog。

再见wordpress,你好jekyll。

最后说一下,这个blog备份在github上。

多线程写代码的时候要注意安全

27 Jul 2013

今天在做项目时候,发现获取当前登录user信息的rest service,偶尔会报错

An item with the same key has already been added.

只是偶尔报错,大部分时间是正常的,很奇怪。查了半天才查出原因,记录下来:

打开service代码,如下

public static class UserInfoProcess
{
  private static DataAccessDataContext db = new DataAccessDataContext();

  public static EmployeeInfo GetEmployeeInfoById(string employeeId)
  {
    return db.EmployeeInfos.SingleOrDefault(c => c.EmployeeId == employeeId);
  }
}

注意,这里的DataContext是static的,这里有个问题等会儿说。

查一下MSDN:

http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx

Thread Safety


Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

MSDN说,DataContext只保证它static member的线程安全,而instance member则不保证。

那么什么叫线程安全呢,我觉得简单的说,就是多个线程同时访问一个object或者method的时候,内部的值要处理正确。

上面的service代码,调用了DataContextEmployeeInfos属性,这个属性是instance member,所以它的线程安全得不到保证。

线程不安全,是怎么体现的呢?我用reflector里反编译了EmployeeInfos属性,下面摘一点重要的出来

private ITable GetTable(MetaTable metaTable)
{
  ITable table;
  if (!this.tables.TryGetValue(metaTable, out table))
  {
    ValidateTable(metaTable);
    table = (ITable) Activator.CreateInstance(typeof(Table<>).MakeGenericType(new Type[] { metaTable.RowType.Type }), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new object[] { this, metaTable }, null);
    this.tables.Add(metaTable, table);
  }
  return table;
}

这个方法中,先看看tables这个dictionary里有没有对应的key,没有就反射create一个,再把它加到dictionary里。

如果只有一个线程,那么这里永远不会有问题,但是,rest service是一个多线程的环境。每一个request进来,都是一个线程,他们共享的是同一个DataContext(因为上面的service代码中,DataContext定义成static了),两个线程有一定的几率同时add一个key(这里没有lock,有lock,就可以线程安全了),导致开头说的那个错误。

所以,解决方法找到了:

  1. 把这个代码加上lock的机制,但是由于这是.Net Framework的代码,我们改不了,所以只能

  2. 这里DataContext不定义成static,换成instance的,每个线程访问自己独有的DataContext

PS:说句题外话,换成instance后,每个请求都要反射,可见Linq to sql性能不怎么样

一个轻量级的Android App: Tip Calculator

10 Jun 2013

在外面吃饭,经常为了算小费而头痛,昨天突然想到,应该有这种帮助算小费的App,上Google Play找了一圈,却没有看着顺眼的。

于是着手自己写一个,花了一晚上的时间做了一个非常简单的App,几个月没碰Android,忘了不少东西,不过好歹是弄出来了,发布在Google Play上。

基于自己是一个毫无美术功底的程序猿,所以界面有点简陋:)自己大概测了下,应该还有一些bug。

  • 算小费可以基于税前和税后的金额
  • 可自定义税率
  • 可以把金额分到不同的人头上(方便AA)
  • 安全,不需求任何特殊的权限
  • 完全免费,不含广告
  • 异常轻量,仅仅40k

同时,这个App是开源的,代码放在Github上。

如果你对这个App有任何意见或者建议,欢迎告诉我。

Android Studio:为Android定制的IDE

17 May 2013

原文:http://android-developers.blogspot.com/2013/05/android-studio-ide-built-for-android.html

Xavier Ducrohet, Tor Norbye, Katherine Chou发表

今天在Google I/O上,我们发布了一个专为Android开发者准备的新IDE:Android Studio,它可以免费使用,你现在就可以试试它的早期预览版。

我们与最流行的Java IDE之一,IntelliJ IDEA 社区版,的开发公司JetBrains合作,开发了Android Studio。在强大的IntelliJ IDEA 社区版的基础上,我们添加了一系列针对Android开发的功能,可以提高你的工作效率。

可扩展的构建工具

我们知道你需要一个适合项目的构建系统,Android Studio的新构建系统基于Gradle,它不仅提供了灵活、定制化的构建方式,还有依赖管理等更多的特性。

这个新的构建系统不仅可以让你在IDE中构建项目,它同样也支持持续集成服务器。在整个工作流程中,N多杂七杂八的工具旁,你可以更方便的管理你复杂的构建配置项。如想了解更多,请查看预览版文档

强大的代码编辑

Android Studio包括一个强大的代码编辑器,这个编辑器基于IntelliJ IDEA,提供了诸如智能提示,高级代码重构,深度静态代码分析等功能。

智能提示中的查找内嵌资源,可以让你在编辑后台资源的时候,更快速的阅读资源对应的代码。高级代码重构可以让你快速的,放心的优化整个项目中代码。

静态代码分析可以让你更快的定位bug。在由IntelliJ IDEA提供的成千上万inspection的基础上,我们又加了一些定制化的inspection。比如,我们为Android API添加了一些元数据,这些元数据指示哪些方法可以返回null,而哪些方法又不可以;某个方法接受哪些常量值,等等。Android Studio就可以通过它们来分析你的代码,找出潜在的错误。

更平滑、更丰富的图形界面

过去几年,我们为ADT添加了一些拖放控件的UI功能,现在我们正在努力为Android Studio移植它们。这个Android Studio版本,你可以在IDE中预览你的布局文件在不同尺寸,语言和版本的设备中的显示效果。下面会展示XML编辑页面中预览不同的配置。

方便的使用Google services

我们想让你更方便的在IDE中使用Google Service的强大功能。首先,我们可以让你直接在IDE中将诸如Google Cloud Messaging(GCM)的Google Service集成到App中。

我们还添加了一个名为ADT Translation Manager Plugin的插件来帮助你本地化你的App。通过这个插件,你可以把字符串导出到Google Play Developer Console中翻译,然后下载并导入这些翻译好的字符串回项目中。

开源

下周开始我们的所有开发工作就会开源,到时候你就可以下载之,然后向我们提交你的代码。Android Studio的代码放在AOSP中。

试用Android Studio并给我们反馈

开始试用Android Studio 并给我们反馈吧!它是免费的,并且下载包中包括你所需要的一切:IDE,最新的SDK以及最新的Android平台等。

注意:这只是一个早期预览版,只适合喜欢尝鲜,或想决定Android工具未来发展方向的人。如果你的App有大量用户使用的话,现在还不是时候迁移到新工具上。我们还将继续支持Eclipse作为首要的开发平台。

如果你有任何意见或建议,请通过Android Studio issue tracker告诉我们。

Join the discussion on

+Android Developers

本文由Roy最初发表于:http://blog.chengbo.net/2013/05/17/android-studio-ide-built-for-android.html,你可以在保持文章完整和保留本声明的情况下转帖、分发和印刷等。

使用Gitolite来对Git的repository实现权限控制

20 Apr 2012

我们项目组打算从svn向git迁移,前几天我搭建了git环境,把代码从svn转移过来,然后所有成员都通过server上的git账号来做pull和push,一切都安置妥当,没有问题。但是后来其它项目组也打算使用这个git server,那么问题来了,之前那种授权的方式肯定是不够的,因为只要能连上server,那么他对这个server上所有的repository都有完全的读写权限,这显然是不可接受的。

所以打算使用Gitolite这个组件来做权限控制,搜索了下,找到的文章貌似都是老版本的,所以有了写这篇文章的想法。

Gitolite其实也是一个git repository,首先在server上安装好后,在client上把server上的repository clone下来,在本地做一些更改,再push回server,server端的hooks会根据push上来的配置来更新权限。

接下来,介绍下安装和配置步骤

准备工作

如果你之前是用git账号来做权限控制的话,记得把/etc/passwd里git用户的shell换回/bin/bash,然后把~git/.ssh/authorized_key里不再需要的key移除。

ssh-kengen生成一对key,比如your-name和your-name.pub(下文均以此为例)

拷贝私钥到本用户的.ssh文件夹中

mv your-name ~/.ssh/

拷贝公钥到git server上

scp you-name.pub git@your.server.name.or.ip.address:~

为了以后方便,这里可以做一个server别名,指定连接所需的用户名,server的地址、端口以及私钥

vim ~/.ssh/config

输入以下内容

host githost
user your-name
hostname your.server.name.or.ip.address
port 22
identityfile ~/.ssh/your-name

安装Gitolite

登录git server

ssh git@your.server.name.or.ip.address

下载最新的Gitolite

git clone git://github.com/sitaramc/gitolite

安装,这里说明下,安装方式有3种,区别在与指定生成gitolite可执行文件的路径,这里采用Gitolite作者推荐的第二种,也就是把文件生成到$HOME/bin中,这样可以在接下来的bash中直接执行gitolite命令而不用指定路径(如果你的~/bin目录不存在记得先mkdir ~/bin)

gitolite/install -ln

设置,由于是第一次运行这个命令,所以这里指定的key是拥有Gitolite管理员权限的

gitolite setup -pk your-name.pub

此命令会在你的~/repositories/目录生成两个repository:gitolite-admin.git和testing.git

配置权限

退到你的workstation上

exit

clone刚才生成的gitolite-admin.git

git clone githost:gitolite-admin

注意这里用的是刚才准备好的server别名来连接的,其中最重要的区别是使用your-name.pub这个key,并且没有采用绝对路径来指定想要clone的repository,而是直接使用名称,并且这个名称也没有包括.git这个后缀。这一点很重要,因为这是用Gitolite的机制来clone,如果你跳过它直接使用git来,那么它的一些功能就无法实现了。以后clone, push其它需要受Gitolite权限控制的repository都必须这样做。

clone完后会有个新的目录gitolite-admin,里面有两个文件夹confkeydir,第一个目录中包含的是配置文件,里面就是记录权限配置的地方,第二个目录中则包含所有用户的pub key。

现在我们打开配置文件,按照我们的权限配置需要进行设置

vim gitolite-admin/conf/gitolite.conf

我期望的配置如下,你也可以根据你的需要做更改

@repos_a @proj1 @proj2
@repos_b @proj3 @proj4 @proj5

@team_a @user1 @user2
@team_b @user3 @user4

repo gitolite-admin
RW+ = your-name

repo @repos_a
RW+ = @team_a
R = @all

repo @repos_b
RW+ = @team_b

这个配置很简单,首先定义了两个repository group,再又定义了两个user group,group的好处就是以后添加repository和user的时候,不需要再单独配置,只需加入到对应的group中即可。

  • 添加全新的repository,在上面提到的gitolite.conf文件中配置好对应的名称和权限,再push到server即可,server会自动帮你创建一个empty的bare repository。
  • 如果你已经有一个repository,想把它加进来的话,那就把它拷贝到git server上的~/repositories文件夹里,记得文件夹名要以.git结尾,并且这个repository一定要是bare的,(你可以通过拷贝repository里的.git文件夹,然后运行git config --bool core.bare true,也可以运行git clone --bare your-repository来得到bare repository)。这种方式还有一个额外的操作就是在server上运行一次gitolite setup
  • 移除repository,在配置文件中移除对应的repo,然后push,接着再删除server上对应的文件夹即可。
  • 添加user,把pub key拷贝到keydir文件夹里
  • 删除user,一样,移除keydir里对应的pub key

注意,上面说的操作,都必须在clone的gitolite-admin里做更改,然后push,千万别在server上自己来,那样是没用的,因为这些权限配置、repository管理都有一些额外的操作,gitolite-admin会帮你搞定一切。

把你的更改push回server上,试试clone,pull,push,看看权限是否正确。比如

git clone githost:proj3

更多的内容,可以参考官方文档

本文由Roy最初发表于:http://blog.chengbo.net/2012/04/20/use-gitolite-to-manage-git-repositories.html,你可以在保持文章完整和保留本声明的情况下转帖、分发和印刷等。