2013年10月

git 的使用(转)

Git 是用于Linux 内核开发的版本控制工具。与常用的CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持,使源代码的发布和交流极其方便。

Ubuntu上可以在“system -> 系统管理 -> 新立得软件包管理器”中搜索到git-core, 这个下载下来即可很方便的开始使用git了。

下面从用户角度分两部分讲述,首先是单个用户独立使用git,其次是多个用户一起使用git进行团队开发。

一:

单个用户

先在自己的 /home/chenchi/ 目录下开始操作

$ mkdir gittest

$ cd gittest

$ git init

然后会自动输出类似的语句 :

Initialized empty Git repository in /home/chenchi/testgit/.git/

这时候我们输入下面的命令

$ ls  -a

可以看到当前目录下创建了一个隐藏的 .git 目录,它就是所谓的Git 仓库,不过现在仓库还是空的。另外当前目录也不再是普通的文档目录了,今后我们将其称为工作树。有兴趣的话,可以先到.git 目录下看看都有哪些文件。

一个叫 HEAD 的文件,我们现在来查看一下它的内容:

$ cat .git/HEAD

现在 HEAD 的内容应该是这样:

ref: refs/heads/master

我们可以看到,HEAD 文件中的内容其实只是包含了一个索引信息,并且,这个索引将总是指向你的项目中的当前开发分支。

一个叫 objects 的子目录,它包含了你的项目中的所有对象,我们不必直接地了解到这些对象内容,我们应该关心是存放在这些对象中的项目的数据。

一个叫 refs 的子目录,它用来保存指向对象的索引。

具体地说,子目录 refs 包含着两个子目录叫 heads 和 tags,就像他们的名字所表达的意味一样:他们存放了不同的开发分支的头的索引, 或者是你用来标定版本的标签的索引。

请注意:master 是默认的分支,这也是为什么 .git/HEAD 创建的时候就指向 master 的原因,尽管目前它其实并不存在。 git 将假设你会在 master 上开始并展开你以后的工作,除非你自己创建你自己的分支。

另外,这只是一个约定俗成的习惯而已,实际上你可以将你的工作分支叫任何名字,而不必在版本库中一定要有一个叫 master 的分支,尽管很多 git 工具都认为 master 分支是存在的。

现在已经创建好了一个 git 版本库,但是它是空的,还不能做任何事情,下一步就是怎么向版本库植入数据了。

为了简明起见,我们创建两个文件作为练习:

$ echo "Hello world" > hello

$ echo "Silly example" > example

这样在当前的testgit目录下就生成了两个文件hello和example,如果我们想将它们加入到库中,该怎么做呢?

首先利用 git add 命令将这两个文件加入到版本库文件索引(一个临时的存储区域,Git 称该区域为索引,如果不理解,先跳过去)当中:

$ git add hello example

现在让我们看看版本库的状态:

$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached ..." to unstage)
#
#       new file: example
#       new file: hello
#

我们能看到 git 的状态提示。提示信息告诉我们版本库中加入了两个新的文件,并且 git 提示我们提交这些文件,我们可以通过 git-commit 命令来提交,这里我们有两种输入方式,

(1)带参数m

$ git commit -m "Initial commit of gittest reposistory"

//引号中的内容就是类似于SVN中每次提交代码前,都要填写的message信息,以便于以后自己或者别人能明白你这次更改的原因,及相关改动的信息。

(2)不带参数m

$ git commit

//系统会自动调用一个叫GNU nano的编辑器,来让你输入与上面类似的信息,在编辑器的下面会出现^G ^X ^O ^J等等,^表示ctrl键,所以他们分别是“ctrl键+G”(获取帮助),“ctrl键+X”(Exit 退出),“ctrl键+O”(WriteOut 写保存),“ctrl键+J”(Justify),具体就不详述了。

Created initial commit 7ffe128: Initial commit of gittest reposistory
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 example
create mode 100644 hello

来看看自己的劳动成果吧:

$ git  log

commit 7ffe1285bda6febfa8d24f8d366ad97c97f3b550
Author: chenchi << span="">[email protected]>
Date: Tue Feb 24 17:54:46 2009 +0800
Initial commit of gittest reposistory

这里我们已经知道如何创建版本库,并向库中添加数据。

再往下走:

$ vim hello

添加如下语句:

today is Tuesday, 20090224, rain.
Are you happy?

利用git diff 查看改了什么:

$ git diff            //注意:该命令只比较已经在index索引中存在的文件,也即如果是一个新加的文件。git diff是不会产生比较的。

diff --git a/hello b/hello
index 802992c..e042b07 100644
--- a/hello
+++ b/hello
@@ -1 +1,4 @@
 Hello world
+
+today is Tuesday, 20090224, rain.
+Are you happy?

现在让我们看看版本库的状态:

$ git status
# On branch master          //表示master分支
#   (use "git add ..." to update what will be committed)
#
#       modified:   hello
#
no changes added to commit (use "git add" and/or "git commit -a")

它表示hello文件已经被更改了,但没有更新(update)。

$ git add hello
$ git diff                                //此时已经没有不同的内容了
$ git status              //再看看版本库状态,有什么区别呢?
# On branch master                 //表示master分支
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#       modified:   hello
#

它表示hello文件已经被更改了,但没有提交(committed)到本地的仓库中

$ git commit -m "1---add date to hello file"

这里我们其实已经完成了又一次的更改和提交了。

二:多个用户,团队开发

实际开发中,我们通常是直接用别人的仓库来进行进一步的开发,那么下面的流程就是必须要看的了:

因为是团队合作,所以每个人在使用git时,要先进行类似的配置。

$ git config --global user.name "chenchi"$ git config --global user.email "[email protected]"

[email protected]

回到自己的 /home/chenchi/ 目录下

$ mkdir project
$ cd  project

首先从服务器上克隆(clone出完整的工作树到本地):

$ git [email protected]:/home/chenchi/git1   (内网用户)
$ git clone  ssh:[email protected]:9092/home/chenchi/git1   
(外网用户)
Initialized empty Git repository in [email protected]'s password:    输入服务器上chenchi用户的密码(123456),然后输出下面的内容
remote: Counting objects: 94, done.
remote: Compressing objects: 100% (67/67), done.
remote: Total 94 (delta 12), reused 0 (delta 0)
Receiving objects: 100% (94/94), 7.91 KiB, done.
Resolving deltas: 100% (12/12), done.

注意:
1:git clone 命令是将别的仓库克隆(clone)过来,后面是别人仓库的源地址,你可以直接用上面的地址试试。这时如果一切顺利,系统会自动在你的当前目录下建立一个git1的目录;如果你想弄一个linux的源代码来玩玩,可以试试这个:

$ git-clone  git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git  linux-2.6

2:因为git clone在默认时用的是ssh协议,所以用git clone前应该确保我们当前机器机器上已经安装了openssh,详见wiki中: 开发环境 ------> Openssh服务 。

这样在当前目录中就生产了一个git1的工作树,它的内容和服务器中是一样的。你可以在自己的机器上进行各种更改了,具体用法和前面单机时类似(add -> commit ->OK )。只是当你想将自己的工作放到服务器中时(也就是SVN中的提交代码),建议按如下步骤:

$ git  pull //先更新本地代码,使其是服务器中最新的代码,防止提交时有冲突,如果没有,继续下一步;如果有冲突,则先在编辑器中修改掉冲突部分,再继续下面的步骤
$ git  push  //将本地的更改最后push到服务器上,使服务器上也同步保留了自己的更改

这里一次代码更改并最后提交就完成了。

这个时候在服务器端可以用 git log 来看看是否有更新,同时可以通过在服务器端输入
git checkout 将Git仓库中更新的内容导到当前工作目录中,显现出来。

当服务器上有新的代码(其他人git push 上去的),而某个开发者在本地忘记了git pull,直接git push 时,会出现类似下面的错误:

[email protected]:~/chenchi/git1/drv-pxa310$ git [email protected]'s password:
 To ssh://210.22.155.236:9092/home/chenchi/git1
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to 'ssh://210.22.155.236:9092/home/chenchi/git1'

这时需要git pull 更新本地代码,系统会在这个过程中自动的进行merger操作,如果本地更改和服务器上最新代码没有冲突,则merger操作自动完成,如果有冲突,则出现类似下面的输出:

[email protected]:~/chenchi/git1/drv-pxa310$ git [email protected]'s password:
 remote: Counting objects: 15, done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 12 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
From ssh://210.22.155.236:9092/home/chenchi/git1
   a3803c8..9ceb8ee  master     -> origin/master
Auto-merged drv-pxa310/t3
CONFLICT (content): Merge conflict in drv-pxa310/t3
Automatic merge failed; fix conflicts and then commit the result.

这时需要我们自己手动更改文件,再提交到本地仓库,最后 git push 到服务器上去。
如果在push过程中有类似下面的错误:

[email protected]:~/chenchi/git1/drv-pxa310$ git [email protected]'s password:
 Counting objects: 14, done.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (8/8), 755 bytes, done.
Total 8 (delta 4), reused 0 (delta 0)
error: unable to create temporary sha1 filename ./objects/ca: File exists
fatal: failed to write object
error: unpack failed: unpacker exited with error code
To ssh://210.22.155.236:9092/home/chenchi/git1
 ! [remote rejected] master -> master (n/a (unpacker error))
error: failed to push some refs to 'ssh://210.22.155.236:9092/home/chenchi/git1'

三:库的逆转与恢复

-- git-reset
-- git-checkout

库的逆转与恢复除了用来进行一些废弃的研发代码的重置外,还有一个重要的作用。比如我们从远程clone了一个代码库,在本地开发后,准备提交回远程。但是本地代码库在开发时,有功能性的commit,也有出于备份目的的commit等等。总之,commit的日志中有大量无用log,我们并不想把这些log在提交回远程时也提交到库中。 因此,就要用到git-reset。

Git-reset的概念比较复杂。它的命令形式:git-reset [--mixed | --soft | --hard] []
命令的选项:

--mixed 这个是默认的选项。 如

$ git reset --mixed dev1^ / /dev1^ 其实就是某个具体的commit ID,也就是那一串数字,如 a3803c862eb73b3a7a61b356e3fb5c7e95a17bfd

它的作用仅是重置分支状态到dev1^, 但是却不改变任何工作文件的内容。即,从dev1^到dev1的所有文件变化都保留了,但是dev1^到dev1之间的所有commit日志都被清除了,而且,发生变化的文件内容也没有通过git-add标识,如果您要重新commit,还需要对变化的文件做一次git-add。简单点说就是:将前面几次log信息清除,而将相应的更改一次性进行 add --> commit。 这样,add --> commit后,就会得到了一份非常干净的提交记录。

$ git   reset   --mixed    7fde43204 //相当于做了git-reset –mixed,后,又对变化的文件做了git-add。如果用了该选项, 就可以直接commit了。

$ git   reset   --hard    7fde43204 //这个命令就会导致所有信息的回退, 包括文件内容。 一般只有在重置废弃代码时,才用它。 执行后,文件内容也无法恢复回来了。慎用。

当我们改了大量的文件以后,既没有 git add 也没有git commit,这时我们想将其中部分文件的更改撤销掉,即想做SVN中的revert类似操作,这时可以:
$ git checkout HEAD -- filename1 filename2 filename3 dir1/filename4 dir1/filename4

四 .Git pull、 push 操作无需输密码的方法编辑本段回目录

在本地使用git与服务器进行pull、push操作时,每次都要输入密码,比较麻烦,我们可以用ssh密钥来进行验证。这样git使用过程中ssh就会自动登录而无需输入密码。方法如下:

首先用自己账号登录到server上,输入ssh-keygen -t rsa

[email protected]:~$ ssh-keygen -t rsaGenerating public/private rsa key pair.
Enter file in which to save the key (/home/chenchi/.ssh/id_rsa):
 Enter passphrase (empty for no passphrase):
 Enter same passphrase again:
 Your identification has been saved in /home/chenchi/.ssh/id_rsa.
Your public key has been saved in /home/chenchi/.ssh/id_rsa.pub.
The key fingerprint is:
79:8a:78:8f:c6:12:12:3c:fd:40:84:7b:e3:fc:60:[email protected]
The key's randomart image is:
.........

输入ssh-keygen -t rsa后,出现各个提示,不用管,一直按回车。这样密钥对就生成完了。其中公共密钥保存在 ~/.ssh/id_rsa.pub ( ~ 表示自己账号的home目录,像我的就是/home/chenchi目录);
私有密钥保存在 ~/.ssh/id_rsa 文件中。

编译php时gd库不支持jpeg

  记得去年遇到过学校一个网站项目上传jpeg格式的图片后不能进行处理,但是查看有gd库,看了错误日志后发现gd库里面jpeg相关的函数不能执行,然后找出原因是jpeg没有支持。但是,我查看系统,的确是装了jpeg的库,然后重新编译了gd库就好了,但是不得原因。

  今天在php手册上看到一个评论,原文如下:

  When using --with-gd, jpeg support is not enabled by default even if the libs and headers are in /usr/lib and /usr/include. Yet, if they are missing, GD will not build.
Oddly, examine the output from configure will show that libjpeg was actuly found and tested.

  This happens with v4.4.4 of php on these platforms: RHEL v2.1, RHEL v3, CentOS v2.1 or CentOS v4.3.

  On those platforms, it's important that --with-jpeg-dir be before --with-gd. If it's not, GD won't build with jpeg support as if --with-jpeg-dir had never been specified.
It's also a "feechure" that gd_info() doesn't list jpeg support if it wasn't built with it. However, if it is built with it, it will list it as "enabled" of course :-/

  意思是在低版本的php中需要把 --with-jpeg-dir 放在 --with-gd的前面,高版本不用,至少php5.4+的确是不用。 

  这里贴出编译安装php的参数:

./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-mysql=/usr/local/mariadb --with-mysqli=/usr/local/mariadb/bin/mysql_config --with-pdo-mysql --with-mcrypt=/usr/local/libmcrypt/ --enable-fpm --with-fpm-user=www --with-fpm-group=www --with-curl --with-zlib --with-jpeg-dir --with-png-dir --with-gd --with-iconv-dir --enable-bcmath --enable-shmop --enable-sysvsem  --enable-inline-optimization --enable-mbregex --enable-mbstring --enable-ftp --enable-gd-native-ttf --with-openssl --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip  --enable-soap --without-pear --with-gettext --enable-session --with-freetype-dir --with-pcre-dir --with-libxml-dir (--with-mhash --enable-opcache)

golang入门1

1、第一个Go程序,经典的hello world

package main

import "fmt"

func main() {
    fmt.Println("hello world")
}

说明:

每一个Go源代码文件的开头都是一个package声明,表示该Go代码所属的包。

main函数不能带参数,也不能有返回值。

func 函数名(参数列表)(返回值列表){

函数体(不用;结尾)

}

2、简单的命令行计算器代码

calc.go

//calc.go
package main

import "os" //用于获取命令行参数
import "fmt"
import "simplemath" //自定义的算法库
import "strconv"

var Usage = func() {
    fmt.Println("Usage:calc command [arg] ...")
    fmt.Println("\nThe commands are:\n\tadd\tAddtion of two values.\n\tsqrt\tSquare root of a non-negative value.")
}

func main() {
    args := os.Args[1:]
    if args == nil || len(args) < 2 {
        Usage()
        return
    }

    switch args[0] {
    case "add":
        if len(args) != 3 {
            fmt.Println("加法需要3个参数")
            return
        }
        v1, err1 := strconv.Atoi(args[1])
        v2, err2 := strconv.Atoi(args[2])
        if err1 != nil || err2 != nil {
            fmt.Println("加法需要3个参数")
            return
        }
        ret := simplemath.Add(v1, v2)
        fmt.Println("Result:", ret)
    case "sqrt":
        if len(args) != 2 {
            fmt.Println("开方需要2个参数")
            return
        }
        v, err := strconv.Atoi(args[1])
        if err != nil {
            fmt.Println("开方需要2个参数")
            return
        }
        ret := simplemath.Sqrt(v)
        fmt.Println("Result:", ret)
    default:
        Usage()
    }
}

add.go

//add.go
package simplemath

func Add(a int, b int) int {
    return a + b
}

sqrt.go

//sqrt.go
package simplemath
import "math"
func Sqrt(i int) int {
    v := math.Sqrt(float64(i))
    return int(v)
}

3、构建或执行

直接运行

  go run hello.go

构建不自动运行

  go build calc.go

然后运行

  ./calc
go.png

PHP 5.3 和 PHP 5.4 在字符串的区别

自 PHP 5.4 起字符串下标必须为整数或可转换为整数的字符串,否则会发出警告。之前例如 "foo" 的下标会无声地转换成 0。

<?php
$str = 'abc';

var_dump($str['1']);
var_dump(isset($str['1']));

var_dump($str['1.0']);
var_dump(isset($str['1.0']));

var_dump($str['x']);
var_dump(isset($str['x']));

var_dump($str['1x']);
var_dump(isset($str['1x']));
?>

以上例程在PHP 5.3中的输出:

string(1) "b"
bool(true)
string(1) "b"
bool(true)
string(1) "a"
bool(true)
string(1) "b"
bool(true)

以上例程在PHP 5.4中的输出:

string(1) "b"
bool(true)
Warning: Illegal string offset '1.0' in /tmp/t.php on line 7
string(1) "b"
bool(false)
Warning: Illegal string offset 'x' in /tmp/t.php on line 9
string(1) "a"
bool(false)
string(1) "b"
bool(false)

Note:

用 [] 或 {} 访问任何其它类型(不包括数组或具有相应接口的对象实现)的变量只会无声地返回 NULL。

Note:

PHP 5.5 增加了直接在字符串原型中用 [] 或 {} 访问字符的支持。

记录两道新浪的笔试题

1、从某个文件中获取某个单词出现的行号,并把行号写入一个文件,最后统计出现了多少行。

cat -n php.ini-development | grep cat | awk '{print $1}' >> aa.txt
cat -n php.ini-development | grep cat | awk '{print $1}' |wc -l

2、从访问日志中吧ip地址提取出来,并存入文件。

awk '{print $4}'  xxx.log |uniq -c