Git and Github

2011-11-21  张林 

Why Git is Better than X
http://zh-tw.whygitisbetterthanx.com/
http://rogerdudler.github.com/git-guide/index.zh.html
Git and Github
http://ihower.tw/blog/archives/5391
Pro Git professional version control
http://progit.org/book/
http://progit.org/book/zh/
https://github.com/
http://help.github.com/win-set-up-git/
Git Reference
http://gitref.org/
git中文教程
http://fsjoy.blog.51cto.com/318484/75365
http://roclinux.cn/?p=605
http://roclinux.cn/?p=622
http://roclinux.cn/?p=647

《看日记学git》之随笔笔记

本文章就是我复习前三十集后的一个总结,把忘了的知识点写在这重新提一下。你也看看,正好复习复习:)

1 git clone只会提取远程git仓库里已提交的信息,working tree和index file里的信息,git clone根本不会理会。如果你是经理,你让你的手下用git clone path/to/your/repo newrepo来将你的代码下载到他的本地就可以了,注意,他用此命令下载的是path/to/your/repo仓库中的当前分支的内容。当你的手下开 发完他负责的那部分代码之后,通知你。你用git fetch path/to/his/newrepo hisbranch:yourbranch 来将你手下的工作成果抓取到你本地的yourbranch分支上。待审核合格之后,你就可以用git merge来合并他的工作了。

2 想查看两个分支的区别,就用git whatchanged -p branch1..branch2。注意,-p表示以易理解的字符方式表示两者区别。如果不加-p的话,git会输出一些内部格式的区别信息,你根本看不懂:)

3 git pull的用法至少有四种,一个是在git clone之后用git pull来同步;第二个用法是用git pull .来替代git merge命令;第三个用法是git pull /path/to/repo,将运程仓库的内容直接merge到当前分支中。第四种用法是在git pull /path/to/repo后面加上<src>:<dst>来指定源的分支和本地分支。

4 git里有四种对象:commit对象、tree对象、blob对象和tag对象

5 尽量不要用git-push,因为他类似于CVS的管理模式,而git的模式特点就是分布式,所以常用git pull代替git push。

6 git fetch <仓库路径> <分支信息>,用于从远程仓库获取代码。其中<分支信息>的格式是这样的 “<src>:<dst>”,<src>表示源的分支,而<dst>表示本地分支。如 果<dst>被省略,那么就默认为本地当前分支。

7 如果在当前分支有代码修改,但未commit,那么不允许git checkout到其他分支。

8 想从index file中删除一个登记文件,用git reset — filename

9 用git checkout -- filename来将此文件代码恢复到index中的状态。

http://roclinux.cn/?p=1239

 《看日记学git》系列文章汇总

http://roclinux.cn/?p=914

1

获得帮助可以使用类似man git-****的命令格式:

想获得关于commit命令的帮助,则man git-commit

想获得关于pull命令的帮助,则man git-pull

想获得关于merge命令的帮助,则man git-merge

以此类推

2

任何人在使用git之前,都要提交简单的个人信息,以便git区分不同的提交者身份。

#git config –global user.name “your name”

#git config –global user.email yourname@example.com

3

想新开启一个项目,应该先建立一个目录,例如名为myproject,然后所有的项目开发内容都在此目录下进行。

#cd myproject

#git init

#git add .

#git commit //这个步骤会自动进入编辑状态,要求提交者输入有关本次提交的“开发信息”

至此,一个新项目就诞生了,第一个开发信息(开发日志)也随之诞生。

4

如果改进了项目源代码,并且到了开发者认为“应该再次记录开发信息”的时候,则提交“工作成果”。

#git commit -a  //这是一个偷懒的命令,相当于git add .; git commit;

但是,此处有一点应该注意,那就是git commit -a无法把新增文件或文件夹加入进来,所以,如果你新增了文件或文件夹,那么就要老老实实的先git add .,再git commit喽。[此处非常感谢freeren的提醒]

5

想检查到目前为止对源码都做了哪些修改(相对于本次工作刚开始之时):

#git diff //这个命令只在git add之前使用有效。如果已经add了,那么此命令输出为空

#git diff –cached //这个命令在git add之后在git commit之前有效。

#git status  //这个命令在git commit之前有效,表示都有哪些文件发生了改动

6

想查看自项目开启到现在的所有开发日志

#git log

#git log -p //会输出非常详细的日志内容,包括了每次都做了哪些源码的修改

7

开启一个试验分支(experimental),如果分支开发成功则合并到主分支(master),否则放弃该试验分支。

#git branch experimental //创建一个试验分支,名称叫experimental

#git branch //显示当前都有哪些分支,其中标注*为当前所在分支

#git checkout experimental //转移到experimental分支

(省略数小时在此分支上的开发过程)…

如果分支开发成功:

#git commit -a //在experimental分支改进完代码之后用commit在此分支中进行提交

#git checkout master //转移回master分支

#git merge experimental //经证实分支开发成功,将exerimental分支合并到主分支

#git commit -a //彻底完成此次分支合并,即提交master分支

#git branch -d experimental //因为experimental分支已提交,所以可安全删除此分支

如果分支开发失败:

#git checkout master

#git branch -D experimental //由于分支被证明失败,因此使用-D来放弃并删除该分支

8

随时查看图形化分支信息。

#gitk

9

当合作伙伴bob希望改进我(rocrocket)的工作成果,则:

bob$git clone /home/rocrocket/project myrepo //此命令用于克隆我的工作到bob的myrepo目录下。请注意,此命令有可能会因为/home/rocrocket的目录权限问题而被拒绝,解决方法 是chmod o+rx /home/rocrocket。

(省略bob数小时的开发过程)…

bob$git commit -a //bob提交自己的改进成果到自己的git仓库中,并口头告知我(rocrocket)他已经完成了工作。

我如果非常非常信任bob的开发能力:

$ cd /home/rocrocket/project

$ git pull /home/bob/myrepo //pull命令的意思是从远端git仓库中取出(git-fetch)修改的代码,然后合并(git-merge)到我(rocrocket)的项目中 去。读者要记住一个小技巧,那就是“git pull .”命令,它和git merge的功能是一样的,以后完全可以用git pull .来代替git merge哦!请注意,git-pull命令有可能会因为/home/bob的目录权限问题而被拒绝,解决方法是chmod o+rx /home/bob。

如果我不是很信任bob的开发能力:

$ cd /home/rocrocket/project

$ git fetch /home/bob/myrepo master:bobworks //此命令意思是提取出bob修改的代码内容,然后放到我(rocrocket)工作目录下的bobworks分支中。之所以要放到分支中,而不是 master中,就是要我先仔仔细细看看bob的开发成果,如果我觉得满意,我再merge到master中,如果不满意,我完全可以直接git branch -D掉。

$git whatchanged -p master..bobworks //用来查看bob都做了什么

$git checkout master //切换到master分区

$git pull . bobworks //如果我检查了bob的工作后很满意,就可以用pull来将bobworks分支合并到我的项目中了

$git branch -D bobworks //如果我检查了bob的工作后很不满意,就可以用-D来放弃这个分支就可以了

过了几天,bob如果想继续帮助我开发,他需要先同步一下我这几天的工作成果,只要在其当初clone的myrepo目录下执行git pull即可:

#git pull //不用加任何参数,因为当初clone的时候,git已经记住了我(rocrocket)的工作目录,它会直接找到我的目录来取。


本次将重点关注历史记录查询。

1

git的历史记录是由一些列相关联的”commit”所组成的。每一次“commit”都会有一个唯一的名称。如下黑体字所示:

[rocrocket@wupengchong project]$ git log
commit 5b888402aadd3cd41b3fe8c84a8658da07893b20
Author: rocrocket <wupengchong@gmail.com>
Date:   Wed Sep 24 13:16:46 2008 +0800

after pull from rocrocket

Hello!!!!!

2

我们可以使用git show再加上述的commit名称来显式更详细的commit信息:

[rocrocket@wupengchong project]$ git show 5b888402aadd3cd41b3fe8c84a8658da07893b20

你完全可以用一个最短的且唯一的“名称前几个字符”来只待某次commit:

[rocrocket@wupengchong project]$ git show 5b888 //只要能区别与其他名称就足够了

使用git show加分支名称,亦可以显示分支信息:

[rocrocket@wupengchong project]$git show master

[rocrocket@wupengchong project]$git show experimental

使用HEAD字段可以代表当前分支的头(也就是最近一次commit):

[rocrocket@wupengchong project]$git show HEAD

每一次commit都会有”parent commit”,可以使用^表示parent:

[rocrocket@wupengchong project]$git show HEAD^ //查看HEAD的父母的信息

[rocrocket@wupengchong project]$git show HEAD^^ //查看HEAD的父母的父母的信息

[rocrocket@wupengchong project]$git show HEAD~4 //查看HEAD上溯4代的信息

要注意的是git-merge是会产生双父母的,这种情况这样处理:

[rocrocket@wupengchong project]$git show HEAD^1 //查看HEAD的第一个父母

[rocrocket@wupengchong project]$git show HEAD^2 //查看HEAD的第二个父母

3

你可以给复杂名称起个别名:

[rocrocket@wupengchong project]$ git tag V3 5b888 //以后可以用V3来代替复杂的名称(5b888…)

[rocrocket@wupengchong project]$ git show V3

[rocrocket@wupengchong project]$ git branch stable V3 //建立一个基于V3的分支

4

可以用git grep帮助我们搜索:

[rocrocket@wupengchong project]$ git grep “print” V3 //在V3中搜索所有的包含print的行

[rocrocket@wupengchong project]$ git grep “print” //在所有的历史记录中搜索包含print的行

5

定位具体的历史记录

[rocrocket@wupengchong project]$ git log V3..V7 //显示V3之后直至V7的所有历史记录

[rocrocket@wupengchong project]$ git log V3.. //显示所有V3之后的历史记录。注意<since>..<until>中任何一个被省略都将被默认设置为HEAD。所以如果使 用..<until>的话,git log在大部分情况下会输出空的。

[rocrocket@wupengchong project]$ git log –since=”2 weeks ago” //显示2周前到现在的所有历史记录。具体语法可查询git-ref-parse命令的帮助文件。

[rocrocket@wupengchong project]$ git log stable..experimental //将显示在experimental分支但不在stable分支的历史记录

[rocrocket@wupengchong project]$ git log experimental..stable //将显示在stable分支但不在experimental分支的历史记录

6

你最喜欢的gitk也可以定位具体的历史记录:

[rocrocket@wupengchong project]$ gitk –since=”2 weeks ago” drivers/ //将在GUI中显示自2周前到现在为止的且位于drivers目录下的分支记录信息


===

这次重点讲解索引文件(index file)的作用。

我们在提交工作时,使用最多的命令就是git commit -a了,但是这个将提交你所做的所有工作。其实,如果你了解commit的工作机制,你会知道我们可以自定义提交哪些部分到哪些工作树中,其实自由度很大的。

1

还记得之前我们建立的test-project工作目录么。我们继续在这个目录下演示讲解。

[rocrocket@wupengchong test-project]$ echo “hello world,again”>>file.txt

这次,我们不急着执行commit命令,而是先用git diff看看差别情况:

[rocrocket@wupengchong test-project]$ git diff
diff –git a/file.txt b/file.txt
index 60be00d..a559610 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
Hi,rocrocket!
+hello world,again
好了,我们可以看到git报告了我们刚才所做的修改。下面我们来add一下,然后再git diff,看看diff有什么变化呢:

[rocrocket@wupengchong test-project]$ git add file.txt
[rocrocket@wupengchong test-project]$ git diff
[rocrocket@wupengchong test-project]$
大家可以看到在add之后的git diff的输出竟然为空了,但是此时我们尚未执行commit阿。如果这个时候你执行git diff HEAD,你仍然会看到修改报告:

[rocrocket@wupengchong test-project]$ git diff HEAD
diff –git a/file.txt b/file.txt
index 60be00d..a559610 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
Hi,rocrocket!
+hello world,again

这就说明了一个问题:git diff不是在和HEAD比,而是和另外一个“神秘”内容在比,而这个“神秘”内容就是“索引文件”!

索引文件(index file)就是.git/index文件,它是二进制形式的文件。我们可以用ls-files来检查它的内容。

[rocrocket@wupengchong test-project]$ git ls-files –stage  //一定要记住,此命令是用于查看index file的!!
100644 a55961026a22bdd4e938dcc90a4a83823a81f776 0       file.txt
[rocrocket@wupengchong test-project]$ git cat-file -t a5596
blob
[rocrocket@wupengchong test-project]$ git cat-file blob a5596
Hi,rocrocket!
hello world,again

很明显,我们可以看到其内容已经是改进后的代码了,怪不得git-diff会输出空呢!

我们的结论就是git add的作用就是创建一个blob文件来记录最新的修改代码,并且在index file里添加一个到此blob的链接。

2

如果在git-diff后面加上参数HEAD,则git-diff会显示当前工作目录和最近一次提交之间的代码区别。

[rocrocket@wupengchong test-project]$ echo ‘again?’>>file.txt
[rocrocket@wupengchong test-project]$ git diff HEAD
diff –git a/file.txt b/file.txt
index 60be00d..dfb67dc 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,3 @@
Hi,rocrocket!
+hello world,again
+again?

如果使用参数–cached,则会比较index file和最近一次提交之间的代码区别。

[rocrocket@wupengchong test-project]$ git diff –cached
diff –git a/file.txt b/file.txt
index 60be00d..a559610 100644
— a/file.txt
+++ b/file.txt
@@ -1 +1,2 @@
Hi,rocrocket!
+hello world,again

按我的认识,更清楚且通俗的解释就是:git维护的代码分成三部分,“当前工作目录”<->“index file”<->git仓库。git commit会将index file中的改变写到git仓库;git add会将“当前工作目录”的改变写到“index file”;“commit -a”则会直接将“当前工作目录”的改动同时写到“index file”和“git仓库”。

而git diff的使用稍微有些复杂,大家可以看看Lee.MaRS对于这个命令非常精彩的分析(蓝色字部分):(在此非常感谢Lee.MaRS)

将 Current working directory 记为 (1)
将 Index file 记为 (2)
将 Git repository 记为 (3)

他们之间的提交层次关系是 (1) -> (2) -> (3)
git add完成的是(1) -> (2)
git commit完成的是(2) -> (3)
git commit -a两者的直接结合

从时间上看,可以认为(1)是最新的代码,(2)比较旧,(3)更旧
按时间排序就是 (1) <- (2) <- (3)

git diff得到的是从(2)到(1)的变化
git diff –cached得到的是从(3)到(2)的变化
git diff HEAD得到的是从(3)到(1)的变化

3

status命令会显示当前状态的一个简单总结:

[rocrocket@wupengchong test-project]$ git status
# On branch master
# Changes to be committed:
#   (use “git reset HEAD <file>…” to unstage)
#
#    modified:   file.txt
#
# Changed but not updated:
#   (use “git add <file>…” to update what will be committed)
#
#    modified:   file.txt
#
上面两行黑体字中的Changes to be committed表示在index和commit的区别状况。而Changed but not updated表示当前目录和index的区别状况。

412°/4121 人阅读/0 条评论 发表评论

登录 后发表评论