Becomin' Charles

算法 | LNMP | Flutter | Mac

Becomin' Charles

说实在的,Git我已经学习了很久很久,从最开始的,零星的学习,然后偶尔使用,到现在成为公司的唯一版本控制方式,断断续续没有两年,也有三年。Git官网上写着,简单易用(easy to learn),我就是被这四个字骗了,开始用上了Git。现如今,它已经成为一种避无可避的工具了,必须学会。

要开始上手使用Git,可能真的是很简单的事情,如果你翻开一些成体系的文档或者书籍,可能上来会教你使用git init命令。这个命令的目的是创建一个新的git版本库。但是,就我个人的经验来看,一个新人,接触版本控制系统,可能第一件事情,往往是融入一个开发团队,接手一块业务,然后开始贡献代码,不太会是上来先建立一个版本库。

阅读全文 »

Git是目前世界上最为炙手可热的版本控制系统。它是如此的流行和重要,以至于全世界程序员的工作和生活,都可能因之而改变。

Git是一个版本控制系统,帮助程序员管理自己的源代码的版本变化,保证它们不会丢失。它只是开发工作中的一个工具。但是,一个“工具”,为什么可以重要到足以改变这个“工作”本身?我们可以以政治经济学范畴的概念,来理解一下这个问题,Git就相当于是生产工具,生产工具的发展,可以进一步解放生产力,从而推动了社会的进步。Git不是历史上第一个版本控制工具,显然也不会是最后一个,为什么到了Git出现的时候,就产生了近乎变革般的变化?

阅读全文 »

学习使用PHP怎么也有7年的时间了,竟然也没有注意到有个函数是array_replace_recursive,之前只知道array_merge_recursive,而且,这两个函数的返回结果,都非常地出人意料,不怎么符合直觉,而使用了Yii框架若干年,竟然也不知道有个CMap::mergeArray()方法,这个方法,如果跟前面两个函数混同起来看,竟然也显得有点离奇。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

[
'apple',
'orange'
],
'nested' => [
'A' => 'xx',
'B' => 'yy'
],
'single' => 1,
];
$b = [
'fruite' => [
'penapple',
],
'nested' => [
'A' => 'zz',
],
'single' => [
'string'
],
];
echo "array_merge_recursive: ", PHP_EOL;
var_export(array_merge_recursive($a, $b));
echo PHP_EOL, "array_replace_recursive: ", PHP_EOL;
var_export(array_replace_recursive($a, $b));
echo PHP_EOL, "CMap::mergeArray: ", PHP_EOL;
var_export(CMap::mergeArray($a, $b));

然后,我们来看一下这个用例输出的结果,相当神奇哦~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

array_merge_recursive:
array (
'fruite' =>
array (
0 => 'apple',
1 => 'orange',
2 => 'penapple',
),
'nested' =>
array (
'A' =>
array (
0 => 'xx',
1 => 'zz',
),
'B' => 'yy',
),
'single' => //注意这个东西的处理,简直莫名其妙!
array (
0 => 1,
1 => 'string',
),
)
array_replace_recursive:
array (
'fruite' =>
array (
0 => 'penapple',
1 => 'orange',
),
'nested' =>
array (
'A' => 'zz',
'B' => 'yy',
),
'single' =>
array (
0 => 'string',
),
)
CMap::mergeArray:
array (
'fruite' => //这里选择了array_merge_recursive的行为
array (
0 => 'apple',
1 => 'orange',
2 => 'penapple',
),
'nested' =>
array (
'A' => 'zz',
'B' => 'yy',
),
'single' => //这里却选择了array_replace_recursive的行为
array (
0 => 'string',
),
)

实在是没有心力去把所有的用例想完整,只能随便写几个,从中可以看出来,这些函数的处理规则,不是简单可以说清楚的。

  1. 对于包含了字符串键的数组,是逐个键去做merge或者replace的
  2. merge:对于键的值都是纯数组的情况,单纯合并
  3. merge:对于键的值都是非数组的情况,创建数组添加两者作为元素
  4. merge:对于键的值一边是数组,另一边是非数组,结果就匪夷所思了,将数组降维后,按上一条规则处理
  5. 相比之下,replace的行为更具被一致性,就是纯替换
  6. CMap::mergeArray 则跟两者的行为都有所不同,我也是跪了

当然,问题绝不止这么几个,只是我懒得想全所有用例而已。

之前已经写了蛮多的跟这个事件驱动特性有关的内容了。比如我今天发现,Yii 框架支持的事件驱动,只能支持对对象实例进行事件绑定。简单来说,当你要绑定某个事件的时候,这个事件的触发者的对象,必须已经建立好了。

阅读全文 »

Yii框架的特性列表里,有一项叫“Event Driven”,所谓的事件驱动,但是在实际业务开发实践中,我发现即便有应用场景,大家也倾向于不用,究其原因,还是不熟悉。

当然,我认为,作为Yii框架的官方开发团队,没有在这方面给出充足的,合适的例子,来说明这个特性的使用场景,也有着不可推卸的责任。

生搬硬套,为了使用event而去使用event,这种是我们坚决要杜绝的行为,那么到底在哪里使用这个特性才显得恰如其分呢?当然,笔者也没有一个非常好的标准去判断,只是偶有所得,在此跟大家分享一下。

阅读全文 »

故事是这样的,在我的业务模型里,有些东西是常态并且固定化的,比如User,这是一个网站的用户,有一张用户表与其对应。但是有些东西,却不是那么常态,比如我们的网站要经常搞活动,搞活动的时候,临时开发一个功能,加一个表来存储活动数据,日后,活动下线了,这个表也就没有用了,可能会被删除。活动频次非常高,所以不同的开发都会快速地进行开发,于是出现了命名不一致的情况,比如大家都是要关联User(活动跟用户有关,非常自然),在表A中,代表用户的字段叫user_id,表示User的主键,但是在表B里面,因为习惯使然,代表用户的字段叫uid,然后问题就产生了,当表A和表B需要联表查询时候,怎么设置这个relations呢?

阅读全文 »

今天闲着没事,我将手机升级到iOS 8.1后,尝试了下国人出的越狱,据说,这是第一次由中国人率先出品了越狱的版本,盘古。

越狱过程流畅无比,但是看完越狱后的界面,一朵阴云已经浮上我的心头。在越狱的开始界面上,我明显去掉了“支持盘古,安装PP助手”的选项,然而越狱结果页面,赫然安装了PP助手。接着,我开始在Cydia中安装软件,莫名奇妙,突然打开了App Store,是游戏刀塔传奇的页面。

10分钟后,当App Store再次无故启动,定位到携程的页面的时候,我知道,我在自己的手机里,安装了一个巨大的流氓软件,而且还获得了root权限。我还傻傻的使用它的重置OpenSSH密码功能,我重置的任何密码,都会被它知道,不是么?

技术的运用,真的是存乎一心之间。盘古这种丧心病狂的行为,我已经不能多说什么了,你们势必在自取灭亡的道路上越走越远。以这种方式去运用技术来谋取商业利益,我也只能说,我羞于与这样的人为伍。

祝盘古早日倒闭。

我所在的企业,是一家中国知名互联网企业。我的团队,专门从事企业产品的研发。

说起大数据、机器学习,就像老师说的那样,这几个概念,真是一座山峰的不同侧面。其实从学术上看,这些东西难免高深难懂,有很多严谨的数学定义。但是,在企业的实践中,往往并没有那么神秘,也没有多难,我加入课程群,看到很多同学,都是在校学生,所以,我就在这里分享一下,我所在的企业,到底在哪些地方,用怎样的方法,运用了机器学习和数据挖掘。因为我也没有成体系学习过,只是入门选手,我就想到什么说什么了。

阅读全文 »

其实,这个是一类问题,在SVN里面,处理这个问题相对简单和直观,因为SVN的结构简单,只有工作副本和版本库副本两个副本,你要做的事情,无非就是处理工作副本,要么就是处理版本库副本。关于位置的概念,只有两个,工作副本,都是本地镜像,版本库副本,都在中心服务器上。commit能且只能保存在服务器上。回滚,本质上,只有两个情况,一个是没有commit,怎么回滚,一个是已经commit了,怎么回滚。

所以,在SVN中,回滚一个代码,事实上只有一个命令而已。svn revert。不指定版本号作为参数的话,就是放弃未提交的修改,指定版本号的话,就是回滚到指定的版本号(rev),但是实际上已经提交的更改,是不可以抹除的,你只能将一个文件回滚到某个过去的版本,然后重新提交来覆盖之前的变更。

到了Git里面,回滚这个事情,变复杂了。首先,我认为,工作副本的概念没有了,因为去中心化的设计,每个人本地硬盘上拥有的,都直接是版本库副本,没有所谓的本地副本的概念了,所有的人持有的都是版本库本身。就算在研发实践中,设置了中央版本库,但是中央版本库和本地版本库,在逻辑概念上是完全对等的。谁是中央,谁不是中央,其实是人为赋予的抽象地位。

另外一个显著的不同是,Git里的commit,都是直接提交到本地版本库的,这也就是commit速度飞快的原因。

在Git里,回滚这件事情,就一下子多出来好多的情况。

如果是一个人,直接通过git init在本地建立repository,不与任何人协作,有且仅有一个版本库。那么,情况只有两种:

  1. 未提交的变更
  2. 已经提交的变更

第一种情况,可以使用命令 git clean -fd ,来实现直接将所有的未提变化(untracked files)删除,-f的含义是force,强制,-d的意思是未提交的目录和文件都删除。

第二种情况,在单人开发的时候,也比较简单。很多处理方法,比如完全抹去这个commit(这在SVN里是做不到的),就像历史上从未发生过此事一样。要做到这一点,估计有好多种做法,但是我只掌握了一种,就是使用 git rebase -i HEAD2,这个命令的含义本身不是干这个事情的,只是我碰巧发现这么可以。-i的含义是interact,交互式,HEAD2的含义是,从最新的提交开始,显示两次提交的内容,然后你会在编辑页面看到两行提交的log,旧的在上面,新的在下面。如果你需要完全抹去最新的提交,就把下面的那行给删掉就行了。

这种情况下,根据我的实践经验来看,如果是一系列提交,要抹去中间的某个提交,十有八九不会顺利成功,原因也很简单,比如某a文件,你提交了10次,然后你想抹去第6次提交,那么git将极有可能不知道原来的第7次到第10次提交,应该怎么作用在a文件上,这时候,rebase过程被打断,你要手动处理冲突,才能完成任务。但是我想,一般我们不会干这种事情,因为,多数情况下,我们需要处理的都是最新几次连续的提交,比如想都放弃掉。

如果是跟他人协作开发的话,比如有一个管理员,设置了一个中央版本库,然后大家都以中央版本库的代码为基准,事情就将变得更加复杂一点,在上述两种情况之外,又会多出一些情况:

  1. 已经提交了,但是没有push到中央版本库
  2. 已经提交了,但是已经push到中央版本库

第一种情况,跟单人开发时候,第二种情况处理方法一样的。但是,你要非常小心的确认这件事情。该commit确实没有被push过。这种情况下,你甚至可以抹掉该commit,或者合并几个同样没有push的commit,都行。

第二种情况,你就要非常谨慎了。因为即使已经push过了,git不会阻止试图抹掉一个commit的行为,也不会阻止试图合并一些commit的行为。但是,你可能为此付出非常惨痛的代价,光是merge,就能累死你。这种情况下,我的建议,就是根本不要尝试去做。老老实实去checkout一个你认为没问题的版本,通过diff工具,将你想删掉的变更去掉,然后将这个作为一个新的commit附加到历史的洪流中。