Chinaunix首页 | 论坛 | 博客
  • 博客访问: 264599
  • 博文数量: 52
  • 博客积分: 1379
  • 博客等级: 大尉
  • 技术积分: 525
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-18 17:34
文章分类

全部博文(52)

文章存档

2011年(48)

2010年(4)

分类: LINUX

2011-02-09 13:57:56

  •  创建一个git仓库
  1. $mkdir git-tutorial
  2. $cd git-tutorial
  3. $git init

当前目录会生成.git目录保存仓库的信息,包括HEAD文件,objects目录,refs等目录

refs/heads目录包含各个分支的当前位置

refs/tags包含轻量级标签,就是个别名,比如refs/tags/v2.6.36,其内容是个object

object用160位的SHA1 hash值表示,根据它能得到内容

HEAD的内容指名当前工作分支

  • 添加新内容到仓库

  1. $echo "Hello World" > hello
  2. $echo "Silly example" > example
这样生成两个文件,但是git并没有感知到它们
这需要通过两个步骤告诉git
  1. 更新索引文件,即INDEX文件,它描述当前工作目录的内容
  2. 提交该索引文件,得到一个新的object,该object代表了当前工作目录的内容
第一步比较简单
  1. $git update-index --add hello example
--add标志表明后面的文件可以是新添加的,否则git拒绝更新索引文件(避免误添加垃圾文件)
这样索引文件就包含了hello和example这两个文件的信息,具体的:

  1. $ ls .git/objects/??/*
  2. .git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238
  3. .git/objects/f2/4c74a2e500f5ee1332c86b94199f52b1d1d962

git把两个文件的内容添加到了.git/objects目录
可以使用cat-file命令查看object的内容
  1. $git cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
  2. blob
每个对象可以用hash值的前几个字符来表示
  1. $git cat-file -t 557d
  2. blob
-t 用于显示对象类型
blob表示该对象代表一个普通文件,其内容用如下命令显示

  1. $git cat-file blob 557d
  2. Hello World
557db03de997c86a4a028e1ebd3a1ceb225be238代表一个对象,它是hello文件的一个快照,对应内容为
"Hello World"
如果更改hello的内容,那么再次update-index的时候会再次生成一个object

  1. $echo "It's a new day for git" >>hello
再给hello添加"It's a new day for git" 之前,索引文件包含两个object,分别对应于hello和example的当前内容

添加一行到hello之后,我们可以对比修改的内容

  1. $git diff-files
  2. :100644 100644 557db03de997c86a4a028e1ebd3a1ceb225be238 0000000000000000000000000000000000000000 M hello
该命令比较索引和当前工作目录的不同

它显示hello文件被修改了,即和索引中的hello快照不一致

可以用如下命令显示具体修改

  1. $git diff-files -p
  2. diff --git a/hello b/hello
  3. index 557db03..263414f 100644
  4. --- a/hello
  5. +++ b/hello
  6. @@ -1 +1,2 @@
  7. Hello World
  8. +It's a new day for git
-p 代表patch
git diff-files -p 等价于 git diff
  • 提交
在让git感知到hello和example之后要进行第二步就是提交
把hello和example提交到git仓库, 提交的不是工作目录的内容,而是索引文件
所以我们最后一次对hello的修改并不会提交,因为我们只是修改hello文件了,而不是索引文件

这里我们引出两种object类型,tree和commit类型
tree代表目录,commit关联一个目录以及其它信息

为了提交到仓库,分两个步骤进行
首先生成tree对象,它类似一个目录,当前包含hello(修改之前的object)和example两个object

  1. $ git write-tree
  2. 8988da15d077d4829fc51d8544c097def6644dbb

  1. $ git cat-file -t 8988
  2. tree

  1. $ git cat-file -p 8988
  2. 100644 blob f24c74a2e500f5ee1332c86b94199f52b1d1d962 example
  3. 100644 blob 557db03de997c86a4a028e1ebd3a1ceb225be238 hello
接下来提交该tree对象

  1. $git commit-tree 8988
git会让输入日志, 因为该命令一般不由用户使用,所以它从标准输入获取输入,而不是弹出一个编辑器让编辑内容

输入 "Initial commlit"
Ctrl-d

git会输出如下内容(具体内容无须关注)
  1. aba02d6cb08a6c671e89d03ccfd46c893ba3279d
它代表一个commit对象
可以用cat-file显示其内容

  1. $ git cat-file -p aba02d6cb08a6c671e89d03ccfd46c893ba3279d
  2. tree 8988da15d077d4829fc51d8544c097def6644dbb
  3. author Changming Xu 1297260297 +0800
  4. committer Changming Xu 1297260297 +0800

  5. Initial commit
(邮件和名字信息是之前用git config 命令设置的,和这里内容不太相关)
commit-tree 命令也可以用-p参数指定父commit

接下来,我们希望紧接着该commit继续工作,我们希望当前分支的头为
aba02d6cb08a6c671e89d03ccfd46c893ba3279d

如下命令可以完成该工作
  1. $git update-ref HEAD aba02d6cb08a6c671e89d03ccfd46c893ba3279d
意义很明显,它把aba02d6cb08a6c671e89d03ccfd46c893ba3279d写入refs/heads/master(HEAD的内容)文件中

  • 做一些修改
diff-file 比较索引文件和工作目录, diff-index 比较commit tree和工作目录(或者索引文件)

  1. $ git diff-index -p HEAD --cached
    $ git diff-index -p HEAD
  2. diff --git a/hello b/hello
  3. index 557db03..263414f 100644
  4. --- a/hello
  5. +++ b/hello
  6. @@ -1 +1,2 @@
  7. Hello World
  8. +It's a new day for git

--cached指示比较索引文件
git diff-index -p HEAD 等价于 git diff HEAD

现在我们把添加过“It's a new day for git“的hello文件更新到索引文件中

  1. $ git update-index hello
这次不需要--add标志,因为hello已经在索引文件中了

再次比较修改
  1. $ git diff-index -p HEAD
  2. diff --git a/hello b/hello
  3. index 557db03..263414f 100644
  4. --- a/hello
  5. +++ b/hello
  6. @@ -1 +1,2 @@
  7. Hello World
  8. +It's a new day for git
  9. $ git diff-index -p HEAD --cached
  10. diff --git a/hello b/hello
  11. index 557db03..263414f 100644
  12. --- a/hello
  13. +++ b/hello
  14. @@ -1 +1,2 @@
  15. Hello World
  16. +It's a new day for git

可以看出--cached有无都一样,因为update-index hello使得索引文件和工作目录内容一致

现在我们可以再次write-tree, commit-tree -p HEAD,然而有一个更简单的命令可以替代这一切
$ git commit -m "add It's a new day for git to hello"
  1. Created commit d6ce3b9: add It's a new day for git to hello
     1 files changed, 1 insertions(+), 0 deletions(-)

  • 查看改动
如下命令可以查看最后依次改动了什么内容
  1. $ git diff-tree -p HEAD
  2. d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
  3. diff --git a/hello b/hello
  4. index 557db03..263414f 100644
  5. --- a/hello
  6. +++ b/hello
  7. @@ -1 +1,2 @@
  8. Hello World
  9. +It's a new day for git
diff-tree比较两棵树,如果只有一个参数,则比较它和它的父commit树

以下是几个diff*命令的区别

  1.           diff-tree
  2.            +----+
  3.            |    |
  4.            |    |
  5.            V    V
  6.         +-----------+
  7.         | Object DB |
  8.         |  Backing  |
  9.         |   Store   |
  10.         +-----------+
  11.            ^     ^
  12.            |     |
  13.            |     | diff-index --cached
  14.            |     |
  15. diff-index |     V
  16.            |    +-----------+
  17.            |    | Index     |
  18.            |    | "cache"   |
  19.            |    +-----------+
  20.            |     ^
  21.            |     |
  22.            |     | diff-files
  23.            |     |
  24.            V     V
  25.         +-----------+
  26.         | Working   |
  27.         | Directory |
  28.         +-----------+

--pretty 还显示作者和日期等信息
  1. $ git diff-tree --pretty -p HEAD
  2. commit d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
  3. Author: Changming Xu
  4. Date: Thu Feb 10 09:32:37 2011 +0800

  5. add It's a new day for git to hello

  6. diff --git a/hello b/hello
  7. index 557db03..263414f 100644
  8. --- a/hello
  9. +++ b/hello
  10. @@ -1 +1,2 @@
  11. Hello World
  12. +It's a new day for git

如下命令列出所有从HEAD可达的版本
  1. $ git rev-list HEAD
  2. d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
  3. aba02d6cb08a6c671e89d03ccfd46c893ba3279d
查看改动的文件
  1. l$ git whatchanged
  2. commit d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
  3. Author: Changming Xu
  4. Date: Thu Feb 10 09:32:37 2011 +0800

  5. add It's a new day for git to hello

  6. :100644 100644 557db03... 263414f... M hello

  7. commit aba02d6cb08a6c671e89d03ccfd46c893ba3279d
  8. Author: Changming Xu
  9. Date: Wed Feb 9 22:49:17 2011 +0800

  10. Initial commit

  11. :000000 100644 0000000... f24c74a... A example
  12. :000000 100644 0000000... 557db03... A hello

查看某个文件的修改情况

  1. $ git whatchanged example
  2. commit aba02d6cb08a6c671e89d03ccfd46c893ba3279d
  3. Author: Changming Xu
  4. Date: Wed Feb 9 22:49:17 2011 +0800

  5. Initial commit

  6. :000000 100644 0000000... f24c74a... A example



  7. $ git whatchanged hello
  8. commit d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
  9. Author: Changming Xu
  10. Date: Thu Feb 10 09:32:37 2011 +0800

  11. add It's a new day for git to hello

  12. :100644 100644 557db03... 263414f... M hello

  13. commit aba02d6cb08a6c671e89d03ccfd46c893ba3279d
  14. Author: Changming Xu
  15. Date: Wed Feb 9 22:49:17 2011 +0800

  16. Initial commit

  17. :000000 100644 0000000... 557db03... A hello

  • 打标签
打轻量级标签
  1. $ git tag lt
  2. $ cat .git/refs/tags/lt
  3. d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
  4. $ git cat-file -t d6ce
  5. commit
可以看出,它仅仅是把HEAD的内容写入.git/refs/tags/lt

带签名的标签:


  1. $ git tag -s at
  2. 您需要输入密码,才能解开这个用户的私钥:“Changming Xu
  3. 1024 位的 RSA 密钥,钥匙号 28198C20,建立于 2010-08-14

  4. gpg: gpg-agent 在此次会话中无法使用

  1. $ cat .git/refs/tags/at
  2. 5d8c31834c081f6cb27d8f9f524172721e9907a8
  3. $ git cat-file -t 5d8c
  4. tag
  5. $ git cat-file -p 5d8c
  6. object d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
  7. type commit
  8. tag at
  9. tagger Changming Xu Thu Feb 10 11:16:26 2011 +0800

  10. as
  11. -----BEGIN PGP SIGNATURE-----
  12. Version: GnuPG v1.4.10 (GNU/Linux)

  13. iJwEAAECAAYFAk1TWI4ACgkQiULbxSgZjCBG4wQAjofZs67dMHwylMtpktV7YeXp
  14. COihfZfQ18DqlzGZ575m9h+DiaJ1XjvZxu3NyHqywi1fxFRa9aJ88+m4At2BR4jf
  15. UfWfFXX1mOkdVD+Q3f0hrn4q6v0bcw7jQkF8KyiMBsVOoJ1Y9l2utKbMYX194ACt
  16. PzMwFYmEBO+lK1RpRo4=
  17. =sxWQ
  18. -----END PGP SIGNATURE-----


  • 复制仓库
复制仓库可以直接

  1. $cp -a git-turtorial new-git-turtorial
也可以使用其它任何类似的方式

之后需要使用如下命令更新索引文件,因为索引文件还包含一些stat信息,而不仅仅是文件内容,有时候这些
信息用于快速检查某个文件是否更改

  1. $ git update-index --refresh
如果hello的内容和索引文件不一致,将被提示
  1. $ git update-index --refresh
  2. hello: needs update
有时我们希望丢弃所有的未提交的改动(由于刚刚cp):

$git read-tree --reset HEAD
$git update-index --refresh
read-tree用commit tree的内容填充索引文件
以上两个命令等价于 git reset


有些raw仓库根本就不包含索引文件(不用于本地开发)

可以用read-tree来生成索引文件

一旦有了索引文件,对于raw仓库,需要生成工作目录的内容

  1. $git checkout-index -u -a
-a: all files
-u: up-to-date

如果当前目录有某个文件和索引不一致:
  1. $ git checkout-index -u -a
  2. $ >hello
  3. $ git checkout-index -u -a
  4. git-checkout-index: hello already exists

  1. $ git checkout-index -u -a -f
  2. $ cat hello
  3. Hello World
  4. It's a new day for git
-f: force

  • 分支
建立新分支
  1. $ git branch newb
  2. $ git branch
  3. * master
  4. newb
*表示当前工作分支

切换分支
  1. $ git checkout newb
  2. Switched to branch "newb"
  3. $ git branch
  4. master
  5. * newb
git branch 显示所有分支,分支存在于.git/refs/heads/branchname中,里面是个commit object

也可以用checkout -b newb 直接建立并切换到新分支

  • 分支合并
分支用于新的编辑工作,如果合适可以合并到主分支,通常叫master分支
  1. $ echo "Work, work, work" >>hello
  2. $ cat hello
  3. Hello World
  4. It's a new day for git
  5. Work, work, work
  6. $ git commit -m "Some work" -i hello
  7. Created commit db6bd80: Some work
  8. 1 files changed, 1 insertions(+), 0 deletions(-)
-i hello 参数相当于 update-index hello

接下来可以在主分支上工作(比如仓库的维护者)

  1. $ git checkout master
  2. Switched to branch "master"
  3. $ echo "Play, play, play" >>hello
  4. $ echo "Lots of fun" >>example
  5. $ git commit -m "Some fun." -i hello example
  6. Created commit a066fc0: Some fun.
  7. 2 files changed, 2 insertions(+), 0 deletions(-)
gitk 命令可以以图形方式显示, --all表示所有分支

  1. $ gitk --all





接下来可以合并newb中的内容到master
  1. $ git merge -m "Merge work in newb" newb
  2. Auto-merged hello
  3. CONFLICT (content): Merge conflict in hello
  4. Automatic merge failed; fix conflicts and then commit the result.
由于有冲突,需要手工解决

  1. $ git merge -m "Merge work in newb" newb
  2. You are in the middle of a conflicted merge.

  1. $ cat hello
  2. Hello World
  3. It's a new day for git
  4. <<<<<<< HEAD:hello
  5. Play, play, play
  6. =======
  7. Work, work, work
  8. >>>>>>> newb:hello
把hello中的冲突解决掉
  1. Hello World
  2. It's a new day for git
  3. Play, play, play
  4. Work, work, work

  1. $ git commit -i hello
  2. Created commit fae3da1: Merge work in newb
也可以用 show-branch命令查看非图形格式的分支
  1. $ git show-branch --topo-order --more=1 master newb
  2. * [master] Merge work in newb
  3. ! [newb] Some work
  4. --
  5. - [master] Merge work in newb
  6. *+ [newb] Some work
  7. * [master^] Some fun.

接下来,在newb分支工作的人可以进行 “upstream changes"
  1. $ git merge -m "Merge upstream changes." master
  2. Updating db6bd80..fae3da1
  3. Fast forward (no commit created; -m option ignored)
  4. example | 1 +
  5. hello | 1 +
  6. 2 files changed, 2 insertions(+), 0 deletions(-)
  • 合并外部分支
通常别人新建了一个分支,做了些工作,仓库的维护者进行merge
merge相当于先把待merge的内容fetch到本地,打一个临时的标签
然后merge到本地
  1. $ git fetch
具体的仓库格式取决于传输协议
包含如下等协议


  1. rsync://remote.machine/path/to/repo.git/

    /path/to/repo.git/

  2. git://remote.machine/path/to/repo.git/



git pull 等价于 fetch + merge

  • 合并的内幕
为了展示合并的细节,首先回退到合并之前的commit

  1. $ git checkout newb
  2. $ git reset --hard master^2
  3. $ git checkout master
  4. $ git reset --hard master^
  5. $ git show-branch
    * [master] Some fun.
    ! [newb] Some work.
    --
    * [master] Some fun.
    + [newb] Some work.
    *+ [master^] Initial commit
合并两个分支使用的是3路合并算法
首先获取分支的共同祖先

  1. $ mb=$(git merge-base HEAD newb)
  2. $ echo $mb
  3. d6ce3b99c5e317fcfba2d0128e5726799fd2fee8

  4. $ git log
    commit a066fc0968110a3dcb8ea84ab250bd75e0127056
    Author: Changming Xu
    Date:   Thu Feb 10 14:51:38 2011 +0800

        Some fun.

    commit d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
    Author: Changming Xu
    Date:   Thu Feb 10 09:32:37 2011 +0800

        add It's a new day for git to hello

    commit aba02d6cb08a6c671e89d03ccfd46c893ba3279d
    Author: Changming Xu
    Date:   Wed Feb 9 22:49:17 2011 +0800

        Initial commit

read-tree把这三个不同的文件都提取到索引文件中

  1. $ git read-tree -m -u $mb HEAD newb
  2. $ git ls-files --stage
    100644 7f8b141b65fdcee47321e399a2598a235a032422 0    example
    100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1    hello
    100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2    hello
    100644 cc44c73eb783565da5831b4d820c962954019b69 3    hello

接下来进行merge
  1. git merge-index git-merge-one-file hello
冲突之后手工解决

  • 清理垃圾对象
有时候我们经常对一个文件修改然后git add
这样会产生很多object, 而只有最后的object才会使用,可以用如下命令删除不需要的object
  1. $git prune


阅读(2549) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~