2012年8月29日星期三

阮一峰的网络日志

阮一峰的网络日志


读懂diff

Posted: 29 Aug 2012 04:20 AM PDT

diff是Unix系统的一个很重要的工具程序。

它用来比较两个文本文件的差异,是代码版本管理的基石之一。你在命令行下,输入:

  $ diff <变动前的文件> <变动后的文件>

diff就会告诉你,这两个文件有何差异。它的显示结果不太好懂,下面我就来说明,如何读懂diff。

一、diff的三种格式

由于历史原因,diff有三种格式:

  * 正常格式(normal diff)

  * 上下文格式(context diff)

  * 合并格式(unified diff)

我们依次来看。

二、示例文件

为了便于讲解,先新建两个示例文件。

第一个文件叫做f1,内容是每行一个a,一共7行。

  a
  a
  a
  a
  a
  a
  a

第二个文件叫做f2,修改f1而成,第4行变成b,其他不变。

  a
  a
  a
  b
  a
  a
  a

三、正常格式的diff

现在对f1和f2进行比较:

  $ diff f1 f2

这时,diff就会显示正常格式的结果:

  4c4
  < a
  ---
  > b

第一行是一个提示,用来说明变动位置。

  4c4

它分成三个部分:前面的"4",表示f1的第4行有变化;中间的"c"表示变动的模式是内容改变(change),其他模式还有"增加"(a,代表addition)和"删除"(d,代表deletion);后面的"4",表示变动后变成f2的第4行。

第二行分成两个部分。

  < a

前面的小于号,表示要从f1当中去除该行(也就是第4行),后面的"a"表示该行的内容。

第三行用来分割f1和f2。

  ---

第四行,类似于第二行。

  > b

前面的大于号表示f2增加了该行,后面的"b"表示该行的内容。

最早的Unix(即AT&T版本的Unix),使用的就是这种格式的diff。

四、上下文格式的diff

上个世纪80年代初,加州大学伯克利分校推出BSD版本的Unix时,觉得diff的显示结果太简单,最好加入上下文,便于了解发生的变动。因此,推出了上下文格式的diff。

它的使用方法是加入c参数(代表context)。

  $ diff -c f1 f2

显示结果如下:

  *** f1 2012-08-29 16:45:41.000000000 +0800
  --- f2 2012-08-29 16:45:51.000000000 +0800
  ***************
  *** 1,7 ****
   a
   a
   a
  ! a
   a
   a
   a
  --- 1,7 ----
   a
   a
   a
  ! b
   a
   a
   a

这个结果分成四个部分。

第一部分的两行,显示两个文件的基本情况:文件名和时间信息。

  *** f1 2012-08-29 16:45:41.000000000 +0800
  --- f2 2012-08-29 16:45:51.000000000 +0800

"***"表示变动前的文件,"---"表示变动后的文件。

第二部分是15个星号,将文件的基本情况与变动内容分割开。

  ***************

第三部分显示变动前的文件,即f1。

  *** 1,7 ****
   a
   a
   a
  ! a
   a
   a
   a

这时不仅显示发生变化的第4行,还显示第4行的前面三行和后面三行,因此一共显示7行。所以,前面的"*** 1,7 ****"就表示,从第1行开始连续7行。

另外,文件内容的每一行最前面,还有一个标记位。如果为空,表示该行无变化;如果是感叹号(!),表示该行有改动;如果是减号(-),表示该行被删除;如果是加号(+),表示该行为新增。

第四部分显示变动后的文件,即f2。

  --- 1,7 ----
   a
   a
   a
  ! b
   a
   a
   a

除了变动行(第4行)以外,也是上下文各显示三行,总共显示7行。

五、合并格式的diff

如果两个文件相似度很高,那么上下文格式的diff,将显示大量重复的内容,很浪费空间。1990年,GNU diff率先推出了"合并格式"的diff,将f1和f2的上下文合并在一起显示。

它的使用方法是加入u参数(代表unified)。

  $ diff -u f1 f2

显示结果如下:

  --- f1 2012-08-29 16:45:41.000000000 +0800
  +++ f2 2012-08-29 16:45:51.000000000 +0800
  @@ -1,7 +1,7 @@
   a
   a
   a
  -a
  +b
   a
   a
   a

它的第一部分,也是文件的基本信息。

  --- f1 2012-08-29 16:45:41.000000000 +0800
  +++ f2 2012-08-29 16:45:51.000000000 +0800

"---"表示变动前的文件,"+++"表示变动后的文件。

第二部分,变动的位置用两个@作为起首和结束。

  @@ -1,7 +1,7 @@

前面的"-1,7"分成三个部分:减号表示第一个文件(即f1),"1"表示第1行,"7"表示连续7行。合在一起,就表示下面是第一个文件从第1行开始的连续7行。同样的,"+1,7"表示变动后,成为第二个文件从第1行开始的连续7行。

第三部分是变动的具体内容。

   a
   a
   a
  -a
  +b
   a
   a
   a

除了有变动的那些行以外,也是上下文各显示3行。它将两个文件的上下文,合并显示在一起,所以叫做"合并格式"。每一行最前面的标志位,空表示无变动,减号表示第一个文件删除的行,加号表示第二个文件新增的行。

六、git格式的diff

版本管理系统git,使用的是合并格式diff的变体。

  $ git diff

显示结果如下:

  diff --git a/f1 b/f1
  index 6f8a38c..449b072 100644
  --- a/f1
  +++ b/f1
  @@ -1,7 +1,7 @@
   a
   a
   a
  -a
  +b
   a
   a
   a

第一行表示结果为git格式的diff。

  diff --git a/f1 b/f1

进行比较的是,a版本的f1(即变动前)和b版本的f1(即变动后)。

第二行表示两个版本的git哈希值(index区域的6f8a38c对象,与工作目录区域的449b072对象进行比较),最后的六位数字是对象的模式(普通文件,644权限)。

  index 6f8a38c..449b072 100644

第三行表示进行比较的两个文件。

  --- a/f1
  +++ b/f1

"---"表示变动前的版本,"+++"表示变动后的版本。

后面的行都与官方的合并格式diff相同。

  @@ -1,7 +1,7 @@
   a
   a
   a
  -a
  +b
   a
   a
   a

七、阅读材料

  * diff - Wikipedia

  * How to read a patch or diff

  * How to work with diff representation in git

(完)

文档信息

2012年8月25日星期六

阮一峰的网络日志

阮一峰的网络日志


搭建一个免费的,无限流量的Blog----github Pages和Jekyll入门

Posted: 25 Aug 2012 03:32 AM PDT

喜欢写Blog的人,会经历三个阶段。

  第一阶段,刚接触Blog,觉得很新鲜,试着选择一个免费空间来写。

  第二阶段,发现免费空间限制太多,就自己购买域名和空间,搭建独立博客。

  第三阶段,觉得独立博客的管理太麻烦,最好在保留控制权的前提下,让别人来管,自己只负责写文章。

大多数Blog作者,都停留在第一和第二阶段,因为第三阶段不太容易到达:你很难找到俯首听命、愿意为你管理服务器的人。

但是两年前,情况出现变化,一些程序员开始在github网站上搭建blog。他们既拥有绝对管理权,又享受github带来的便利----不管何时何地,只要向主机提交commit,就能发布新文章。更妙的是,这一切还是免费的,github提供无限流量,世界各地都有理想的访问速度。

今天,我就来示范如何在github上搭建Blog,你可以从中掌握github的Pages功能,以及Jekyll软件的基本用法。更重要的是,你会体会到一种建立网站的全新思路。

一、Github Pages 是什么?

如果你对编程有所了解,就一定听说过github。它号称程序员的Facebook,有着极高的人气,许多重要的项目都托管在上面。

简单说,它是一个具有版本管理功能的代码仓库,每个项目都有一个主页,列出项目的源文件。

但是对于一个新手来说,看到一大堆源码,只会让人头晕脑涨,不知何处入手。他希望看到的是,一个简明易懂的网页,说明每一步应该怎么做。因此,github就设计了Pages功能,允许用户自定义项目首页,用来替代默认的源码列表。所以,github Pages可以被认为是用户编写的、托管在github上的静态网页。

github提供模板,允许站内生成网页,但也允许用户自己编写网页,然后上传。有意思的是,这种上传并不是单纯的上传,而是会经过Jekyll程序的再处理。

二、Jekyll是什么?

Jekyll(发音/'dʒiːk əl/,"杰克尔")是一个静态站点生成器,它会根据网页源码生成静态文件。它提供了模板、变量、插件等功能,所以实际上可以用来编写整个网站。

整个思路到这里就很明显了。你先在本地编写符合Jekyll规范的网站源码,然后上传到github,由github生成并托管整个网站。

这种做法的好处是:

  * 免费,无限流量。

  * 享受git的版本管理功能,不用担心文章遗失。

  * 你只要用自己喜欢的编辑器写文章就可以了,其他事情一概不用操心,都由github处理。

它的缺点是:

  * 有一定技术门槛,你必须要懂一点git和网页开发。

  * 它生成的是静态网页,添加动态功能必须使用外部服务,比如评论功能就只能用disqus

  * 它不适合大型网站,因为没有用到数据库,每运行一次都必须遍历全部的文本文件,网站越大,生成时间越长。

但是,综合来看,它不失为搭建中小型Blog或项目主页的最佳选项之一。

三、一个实例

下面,我举一个实例,演示如何在github上搭建blog,你可以跟着一步步做。为了便于理解,这个blog只有最基本的功能。

在搭建之前,你必须已经安装了git,并且有github账户。

第一步,创建项目。

在你的电脑上,建立一个目录,作为项目的主目录。我们假定,它的名称为jekyll_demo。

  $ mkdir jekyll_demo

对该目录进行git初始化。

  $ cd jekyll_demo

  $ git init

然后,创建一个没有父节点的分支gh-pages。因为github规定,只有该分支中的页面,才会生成网页文件。

  $ git checkout --orphan gh-pages

以下所有动作,都在该分支下完成。

第二步,创建设置文件。

在项目根目录下,建立一个名为_config.yml的文本文件。它是jekyll的设置文件,我们在里面填入如下内容,其他设置都可以用默认选项,具体解释参见官方网页

  baseurl: /jekyll_demo

目录结构变成:

  /jekyll_demo
    |-- _config.yml

第三步,创建模板文件。

在项目根目录下,创建一个_layouts目录,用于存放模板文件。

  $ mkdir _layouts

进入该目录,创建一个default.html文件,作为Blog的默认模板。并在该文件中填入以下内容。

  <!DOCTYPE html>

  <html>

  <head>

    <meta http-equiv="content-type" content="text/html; charset=utf-8" />

    <title>{{ page.title }}</title>

  </head>

  <body>

    {{ content }}

  </body>

  </html>

Jekyll使用Liquid模板语言,{{ page.title }}表示文章标题,{{ content }}表示文章内容,更多模板变量请参考官方文档

目录结构变成:

  /jekyll_demo
    |-- _config.yml
    |-- _layouts
    |   |-- default.html

第四步,创建文章。

回到项目根目录,创建一个_posts目录,用于存放blog文章。

  $ mkdir _posts

进入该目录,创建第一篇文章。文章就是普通的文本文件,文件名假定为2012-08-25-hello-world.html。(注意,文件名必须为"年-月-日-文章标题.后缀名"的格式。如果网页代码采用html格式,后缀名为html;如果采用markdown格式,后缀名为md。)

在该文件中,填入以下内容:

  ---
  layout: default
  title: 你好,世界
  ---

  <h2>{{ page.title }}</h2>

  <p>我的第一篇文章</p>

  <p>{{ page.date | date_to_string }}</p>

每篇文章的头部,必须有一个yaml文件头,用来设置一些元数据。它用三根短划线"---",标记开始和结束,里面每一行设置一种元数据。"layout:default",表示该文章的模板使用_layouts目录下的default.html文件;"title: 你好,世界",表示该文章的标题是"你好,世界",如果不设置这个值,默认使用嵌入文件名的标题,即"hello world"。

在yaml文件头后面,就是文章的正式内容,里面可以使用模板变量。{{ page.title }}就是文件头中设置的"你好,世界",{{ page.date }}则是嵌入文件名的日期(也可以在文件头重新定义date变量),"| date_to_string"表示将page.date变量转化成人类可读的格式。

目录结构变成:

  /jekyll_demo
    |-- _config.yml
    |-- _layouts
    |   |-- default.html
    |-- _posts
    |   |-- 2012-08-25-hello-world.html

第五步,创建首页。

有了文章以后,还需要有一个首页。

回到根目录,创建一个index.html文件,填入以下内容。

  ---
  layout: default
  title: 我的Blog
  ---

  <h2>{{ page.title }}</h2>

  <p>最新文章</p>

  <ul>

    {% for post in site.posts %}

      <li>{{ post.date | date_to_string }} <a href="{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a></li>

    {% endfor %}

  </ul>

它的Yaml文件头表示,首页使用default模板,标题为"我的Blog"。然后,首页使用了{% for post in site.posts %},表示对所有帖子进行一个遍历。这里要注意的是,Liquid模板语言规定,输出内容使用两层大括号,单纯的命令使用一层大括号。至于{{site.baseurl}}就是_config.yml中设置的baseurl变量。

目录结构变成:

  /jekyll_demo
    |-- _config.yml
    |-- _layouts
    |   |-- default.html
    |-- _posts
    |   |-- 2012-08-25-hello-world.html
    |-- index.html

第六步,发布内容。

现在,这个简单的Blog就可以发布了。先把所有内容加入本地git库。

  $ git add .

  $ git commit -m "first post"

然后,在github上创建一个名为jekyll_demo的repo。再将本地内容推送到github。注意,下面命令中的username,要替换成你的username。

  $ git remote add origin https://github.com/username/jekyll_demo.git

  $ git push origin gh-pages

上传成功之后,等10分钟左右,访问http://username.github.com/jekyll_demo/就可以看到Blog已经生成了(将username换成你的用户名)。

首页:

文章页面:

第七步,绑定域名。

如果你不想用http://username.github.com/jekyll_demo/这个域名,可以换成自己的域名。

具体方法是在repo的根目录下面,新建一个名为CNAME的文本文件,里面写入你要绑定的域名,比如example.com或者xxx.example.com。

如果绑定的是顶级域名,则DNS要新建一条A记录,指向204.232.175.78。如果绑定的是二级域名,则DNS要新建一条CNAME记录,指向username.github.com(请将username换成你的用户名)。此外,别忘了将_config.yml文件中的baseurl改成根目录"/"。

至此,最简单的Blog就算搭建完成了。进一步的完善,请参考Jekyll创始人的示例库,以及其他用Jekyll搭建的blog

(完)

文档信息

2012年8月21日星期二

阮一峰的网络日志

阮一峰的网络日志


斑头雁守护日记(转载)

Posted: 20 Aug 2012 10:08 AM PDT

前几天,我在网上看到一个环保志愿者的《斑头雁守护日记》,很受感染。

征得作者"lola罗拉罗拉"的同意,我把日记转贴过来,希望更多的人看到。

================================================

先是一些背景介绍

【起因】

中华环境保护基金会主办,绿色江河承办的"让我飞得更高----2012斑头雁守护行动"于4月22日地球日在海拔4600米的长江源班德湖正式启动,并将持续到6月5日环境日。

由鸟类专家、摄影师、高原病专家、记者等各行业的专业志愿者组成的18人队伍组成斑头雁守护队,经过2小时的无人区颠簸抵达班德湖,开始为期45天的守护行动。据悉,斑头雁的全球种群数为7万只,而仅长江源每年被偷捡的鸟蛋就有近2000枚。志愿者们坚持三天做一次鸟类调查,一旦发现捡拾鸟蛋及其他危害斑头雁生存的行为,他们将立即上前制止。

【斑头雁】

斑头雁被誉为世界飞得最高的鸟类,它仅用八小时就能飞越海拔近9000米的喜马拉雅山脉。大部分的斑头雁在印度等海拔较低的地方过冬,春季翻越喜马拉雅山脉到青藏高原繁殖。

【班德湖】


位于长江源沱沱河的班德湖是斑头雁在世界上最高的繁殖地之一,面积约8平方公里,海拔4600米。班德湖与其环绕的星罗棋布的小湖汇集周边的冰雪融水,再由北流入长江正源沱沱河。

==================================================

【日记】

2012年5月31日

前面的"营地日记",都是麦茬在记,她走后就由我记。

由于没有凳子,午饭时大家爱跳上新买的皮卡。

吃完饭,就这样坐在湖边。

班德湖边不仅斑头雁产蛋了,其他鸟儿也都在产蛋。孙爷说他昨晚散步时发现个细嘴短趾百灵的巢,今天带大家去看看。我们走近,就见湖边沙滩上一个精致的鸟窝,比女生的拳头略小,溜圆光滑,里面垫着羽毛和编织整齐的稻草,最令人赞叹的是窝周围还围了一圈小石头。里面两颗小小的蛋。大家连连赞叹这真是天生的建筑师的杰作。

回来后又和王铭、祝迅往帐篷西边走,一只细嘴短趾百灵不像平常那样见到我们就避走,等我们只离几米了才飞。

我们纳闷着走过那个草丛,发现里面居然还有一个巢,垫满了温暖的羽毛,漂亮极了,把我们给乐坏了。

拍了照赶紧撤走,回程路上又远远避开,尽量不影响母鸟继续孵蛋。

大家想象着六七月班德湖遍地毛茸茸小鸟的样子,觉得世界真是无限美好。

2012年6月1日

今天是儿童节,一早起来天气就出奇地好,孙爷特赦大家出门放风,佘晖他们决定去东边的电信基站看班德湖全貌,我、王铭和杨哥去湖西石头山看鸟。孙爷和陶轲、祝迅留守营地。

石头山上次麦茬和卢高峰去探过,是看猛禽的好地方,囿于时间有限,他们只翻了两个垭口就止步了,我们这次一早出发,料定时间有余,就小心翼翼爬上垭口旁陡峭的山头。

站在山顶,天地尽收眼底,除了偶尔出现的一两顶牧民帐篷,极目天边再无人迹。

各拉丹冬雪山在西方地平线隐隐闪耀。

坐在山顶避风处,三人边抬头看猛禽缓缓滑翔,边吃着简单的午饭。一只硕大的胡兀鹫蓦地从山腰窜起,飞速掠过眼前。

岩羊在岩壁上灵巧跳跃。

感谢三哥提供我的照片。

2012年6月2日

中午帐内温度飙至30多,已隐隐有了夏天的样子。吃完饭,其余人都扛不住午睡去了,我和王铭去湖边看鸟。新来一只牛背鹭,通体洁白,老神在在地涉水捕鱼,一会儿一条,一会儿一条,稳、准、狠,再看看旁边可怜的普通燕鸥,俯冲十几二十回好不容易弄到条鱼,还得巴巴地孝敬女友,而且人家吃完可能连嘴都不抹就走人了。不禁摇摇头,不同鸟不同命。

细嘴短趾百灵一天产一枚卵,看来这位才刚开始。当时只差一脚我就踩上去了。

起身往东继续走去。还没走到东边的小山包呢,刚才还毒辣辣的太阳突然不知去向,风云聚合,天地瞬间黑了下来。

几乎是一瞬之间,雪子噼里啪啦往下砸。雪子学名叫霰,是一种介于雪和冰雹之前的降水,比雪的个头粗壮,比冰雹略小,白色不透明,被它砸着的感觉,你可以想象天上突然无数把霰弹枪往你身上打......

2012年6月3日

上午8点多,湖西方向来了两个黑点,众人轮流拿起望远镜观看,这到底是羊,是马,还是人呢?哦,两条腿的,应该是人。那是男是女?干啥的?陈诚下了断言,是两个挖矿的!

吃完晚饭,照例是饭后散步,大家决定走去营地西北的点点山。路上捡到一把非常好的藏刀,正舞得开心,猛然看到一颗子弹静静躺在沙地上,是步枪子弹,不知道是打什么留下的。大家顿时不出声了,默默把子弹收起来。

点点山看着很近,其实死远,杨哥他们看天快黑就先走了,留我、陈诚和王铭继续往前,约定七点一刻往回走。我们最终也没走到那座山,雪云从四面八方乌压压地压过来。

2012年6月4日

早上起来好大的雪,一副准备下个三天三夜不停的架势,孙爷让统计了下食物,做好补给上不来,勒紧裤腰带八年抗战的准备。

下午却突然放晴了。

厚厚的积雪哗哗化成水。

傍晚,杨老师和才仁开着丰田爷爷过来了,按照原定计划,明天是项目结束的日子,要现场直播小斑头雁出壳。

他们还带来了一位特殊的客人----一只翅膀受伤的斑头雁。这是沱沱河火车站派出所的公安干警在河边捡到的,他们简单救治后就把它送到了保护站。大家把斑头雁放到湖边,它愣了下,本能地往湖里走。心中充满了一种温柔的感情,所有人静静地目送它下湖。没一会儿,这厮就只剩下一个屁股在湖面了,正欢快地把脑袋扎进湖里狂吃猛喝。

雪后的黄昏美丽得紧,夕阳金黄斜照着,湖水蔚蓝,群山雪白,鸟儿啾啾。脑海中只跳出四个字:田园牧歌。

所有人都如脱缰野马般四散在各处,完全迷醉了。

突然有人大喊一声,月亮!赶紧扭头,一轮圆月从东边冉冉升起。

在群山衬托下,新升的月亮黄艳艳的,好大的个儿,配着磁蓝的湖面,宁静无匹。一个人在湖边站了好久,世间所有的纷繁喧杂似乎一下子都不存在了。只觉个体渺小,但渺小的个体和这有着无穷力量的自然是合而为一的。

2012年6月5日

七点,所有人都起来了,穿救生衣,拿浆,推船入湖。

船上六人,杨老师负责指挥,孙爷摄影,杨哥摄像,王铭微博直播,才仁和陈诚推船划船。

八点半,消息传来,小岛上没有发现小斑头雁,九点,大岛上也没发现,好消息是大岛上数到800多只斑头雁,比我们平常在岸边数时多出了300多只。

吃过饭杨老师就带着陈诚、才仁赶回保护站工地了。晚上他又兴奋地给我们电话,在一群志愿者的共同努力下,央视的《东方时空》在最后一刻上了我们项目的新闻,前面都是各种国家大事,我们是今天----世界环境日当天,唯一一例环保类的!真是高兴坏了,看着镜头里熟悉的斑头雁飞翔的镜头,感动莫名。

【附录】

  * 《新闻晨报》专访作者(

(完)

文档信息