- $mkdir git-tutorial
- $cd git-tutorial
- $git init
当前目录会生成.git目录保存仓库的信息,包括HEAD文件,objects目录,refs等目录
refs/heads目录包含各个分支的当前位置
refs/tags包含轻量级标签,就是个别名,比如refs/tags/v2.6.36,其内容是个object
object用160位的SHA1 hash值表示,根据它能得到内容
HEAD的内容指名当前工作分支
- $echo "Hello World" > hello
-
$echo "Silly example" > example
这样生成两个文件,但是git并没有感知到它们
这需要通过两个步骤告诉git
- 更新索引文件,即INDEX文件,它描述当前工作目录的内容
- 提交该索引文件,得到一个新的object,该object代表了当前工作目录的内容
第一步比较简单
- $git update-index --add hello example
--add标志表明后面的文件可以是新添加的,否则git拒绝更新索引文件(避免误添加垃圾文件)
这样索引文件就包含了hello和example这两个文件的信息,具体的:
- $ ls .git/objects/??/*
-
.git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238
-
.git/objects/f2/4c74a2e500f5ee1332c86b94199f52b1d1d962
git把两个文件的内容添加到了.git/objects目录
可以使用cat-file命令查看object的内容
- $git cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
-
blob
每个对象可以用hash值的前几个字符来表示
- $git cat-file -t 557d
- blob
-t 用于显示对象类型
blob表示该对象代表一个普通文件,其内容用如下命令显示
- $git cat-file blob 557d
-
Hello World
557db03de997c86a4a028e1ebd3a1ceb225be238代表一个对象,它是hello文件的一个快照,对应内容为
"Hello World"
如果更改hello的内容,那么再次update-index的时候会再次生成一个object
- $echo "It's a new day for git" >>hello
再给hello添加"It's a new day for git" 之前,索引文件包含两个object,分别对应于hello和example的当前内容
添加一行到hello之后,我们可以对比修改的内容
- $git diff-files
-
:100644 100644 557db03de997c86a4a028e1ebd3a1ceb225be238 0000000000000000000000000000000000000000 M hello
该命令比较索引和当前工作目录的不同
它显示hello文件被修改了,即和索引中的hello快照不一致
可以用如下命令显示具体修改
- $git diff-files -p
-
diff --git a/hello b/hello
-
index 557db03..263414f 100644
-
--- a/hello
-
+++ b/hello
-
@@ -1 +1,2 @@
-
Hello World
-
+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
- $ git write-tree
-
8988da15d077d4829fc51d8544c097def6644dbb
- $ git cat-file -t 8988
-
tree
- $ git cat-file -p 8988
-
100644 blob f24c74a2e500f5ee1332c86b94199f52b1d1d962 example
-
100644 blob 557db03de997c86a4a028e1ebd3a1ceb225be238 hello
接下来提交该tree对象
git会让输入日志, 因为该命令一般不由用户使用,所以它从标准输入获取输入,而不是弹出一个编辑器让编辑内容
输入 "Initial commlit"
Ctrl-d
git会输出如下内容(具体内容无须关注)
- aba02d6cb08a6c671e89d03ccfd46c893ba3279d
它代表一个commit对象
可以用cat-file显示其内容
- $ git cat-file -p aba02d6cb08a6c671e89d03ccfd46c893ba3279d
-
tree 8988da15d077d4829fc51d8544c097def6644dbb
-
author Changming Xu 1297260297 +0800
-
committer Changming Xu 1297260297 +0800
-
-
Initial commit
(邮件和名字信息是之前用git config 命令设置的,和这里内容不太相关)
commit-tree 命令也可以用-p参数指定父commit
接下来,我们希望紧接着该commit继续工作,我们希望当前分支的头为
aba02d6cb08a6c671e89d03ccfd46c893ba3279d
如下命令可以完成该工作
- $git update-ref HEAD aba02d6cb08a6c671e89d03ccfd46c893ba3279d
意义很明显,它把aba02d6cb08a6c671e89d03ccfd46c893ba3279d写入refs/heads/master(HEAD的内容)文件中
diff-file 比较索引文件和工作目录, diff-index 比较commit tree和工作目录(或者索引文件)
- $ git diff-index -p HEAD --cached
$ git diff-index -p HEAD -
diff --git a/hello b/hello
-
index 557db03..263414f 100644
-
--- a/hello
-
+++ b/hello
-
@@ -1 +1,2 @@
-
Hello World
-
+It's a new day for git
--cached指示比较索引文件
git diff-index -p HEAD 等价于 git diff HEAD
现在我们把添加过“It's a new day for git“的hello文件更新到索引文件中
这次不需要--add标志,因为hello已经在索引文件中了
再次比较修改
- $ git diff-index -p HEAD
-
diff --git a/hello b/hello
-
index 557db03..263414f 100644
-
--- a/hello
-
+++ b/hello
-
@@ -1 +1,2 @@
-
Hello World
-
+It's a new day for git
-
$ git diff-index -p HEAD --cached
-
diff --git a/hello b/hello
-
index 557db03..263414f 100644
-
--- a/hello
-
+++ b/hello
-
@@ -1 +1,2 @@
-
Hello World
-
+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"
- Created commit d6ce3b9: add It's a new day for git to hello
1 files changed, 1 insertions(+), 0 deletions(-)
如下命令可以查看最后依次改动了什么内容
- $ git diff-tree -p HEAD
-
d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
-
diff --git a/hello b/hello
-
index 557db03..263414f 100644
-
--- a/hello
-
+++ b/hello
-
@@ -1 +1,2 @@
-
Hello World
-
+It's a new day for git
diff-tree比较两棵树,如果只有一个参数,则比较它和它的父commit树
以下是几个diff*命令的区别
- diff-tree
-
+----+
-
| |
-
| |
-
V V
- +-----------+
-
| Object DB |
-
| Backing |
-
| Store |
-
+-----------+
-
^ ^
-
| |
-
| | diff-index --cached
-
| |
- diff-index | V
-
| +-----------+
-
| | Index |
-
| | "cache" |
-
| +-----------+
-
| ^
-
| |
-
| | diff-files
-
| |
-
V V
-
+-----------+
-
| Working |
-
| Directory |
-
+-----------+
--pretty 还显示作者和日期等信息
- $ git diff-tree --pretty -p HEAD
-
commit d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
-
Author: Changming Xu
-
Date: Thu Feb 10 09:32:37 2011 +0800
-
-
add It's a new day for git to hello
-
-
diff --git a/hello b/hello
-
index 557db03..263414f 100644
-
--- a/hello
-
+++ b/hello
-
@@ -1 +1,2 @@
-
Hello World
-
+It's a new day for git
如下命令列出所有从HEAD可达的版本
- $ git rev-list HEAD
-
d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
-
aba02d6cb08a6c671e89d03ccfd46c893ba3279d
查看改动的文件
- l$ git whatchanged
-
commit d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
-
Author: Changming Xu
-
Date: Thu Feb 10 09:32:37 2011 +0800
-
-
add It's a new day for git to hello
-
-
:100644 100644 557db03... 263414f... M hello
-
-
commit aba02d6cb08a6c671e89d03ccfd46c893ba3279d
-
Author: Changming Xu
-
Date: Wed Feb 9 22:49:17 2011 +0800
-
-
Initial commit
-
-
:000000 100644 0000000... f24c74a... A example
-
:000000 100644 0000000... 557db03... A hello
查看某个文件的修改情况
- $ git whatchanged example
-
commit aba02d6cb08a6c671e89d03ccfd46c893ba3279d
-
Author: Changming Xu
-
Date: Wed Feb 9 22:49:17 2011 +0800
-
-
Initial commit
-
-
:000000 100644 0000000... f24c74a... A example
- $ git whatchanged hello
-
commit d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
-
Author: Changming Xu
-
Date: Thu Feb 10 09:32:37 2011 +0800
-
-
add It's a new day for git to hello
-
-
:100644 100644 557db03... 263414f... M hello
-
-
commit aba02d6cb08a6c671e89d03ccfd46c893ba3279d
-
Author: Changming Xu
-
Date: Wed Feb 9 22:49:17 2011 +0800
-
-
Initial commit
-
-
:000000 100644 0000000... 557db03... A hello
打轻量级标签
- $ git tag lt
-
$ cat .git/refs/tags/lt
-
d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
-
$ git cat-file -t d6ce
-
commit
可以看出,它仅仅是把HEAD的内容写入.git/refs/tags/lt
带签名的标签:
- $ git tag -s at
-
您需要输入密码,才能解开这个用户的私钥:“Changming Xu ”
-
1024 位的 RSA 密钥,钥匙号 28198C20,建立于 2010-08-14
-
-
gpg: gpg-agent 在此次会话中无法使用
- $ cat .git/refs/tags/at
-
5d8c31834c081f6cb27d8f9f524172721e9907a8
- $ git cat-file -t 5d8c
-
tag
-
$ git cat-file -p 5d8c
-
object d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
-
type commit
-
tag at
-
tagger Changming Xu Thu Feb 10 11:16:26 2011 +0800
-
-
as
-
-----BEGIN PGP SIGNATURE-----
-
Version: GnuPG v1.4.10 (GNU/Linux)
-
-
iJwEAAECAAYFAk1TWI4ACgkQiULbxSgZjCBG4wQAjofZs67dMHwylMtpktV7YeXp
-
COihfZfQ18DqlzGZ575m9h+DiaJ1XjvZxu3NyHqywi1fxFRa9aJ88+m4At2BR4jf
-
UfWfFXX1mOkdVD+Q3f0hrn4q6v0bcw7jQkF8KyiMBsVOoJ1Y9l2utKbMYX194ACt
-
PzMwFYmEBO+lK1RpRo4=
-
=sxWQ
-
-----END PGP SIGNATURE-----
-
复制仓库可以直接
- $cp -a git-turtorial new-git-turtorial
也可以使用其它任何类似的方式
之后需要使用如下命令更新索引文件,因为索引文件还包含一些stat信息,而不仅仅是文件内容,有时候这些
信息用于快速检查某个文件是否更改
- $ git update-index --refresh
如果hello的内容和索引文件不一致,将被提示
- $ git update-index --refresh
-
hello: needs update
有时我们希望丢弃所有的未提交的改动(由于刚刚cp):
$git read-tree --reset HEAD
$git update-index --refresh
read-tree用commit tree的内容填充索引文件
以上两个命令等价于 git reset
有些raw仓库根本就不包含索引文件(不用于本地开发)
可以用read-tree来生成索引文件
一旦有了索引文件,对于raw仓库,需要生成工作目录的内容
- $git checkout-index -u -a
-a: all files
-u: up-to-date
如果当前目录有某个文件和索引不一致:
- $ git checkout-index -u -a
-
$ >hello
-
$ git checkout-index -u -a
-
git-checkout-index: hello already exists
- $ git checkout-index -u -a -f
-
$ cat hello
-
Hello World
-
It's a new day for git
-f: force
建立新分支
- $ git branch newb
-
$ git branch
-
* master
-
newb
*表示当前工作分支
切换分支
- $ git checkout newb
-
Switched to branch "newb"
-
$ git branch
-
master
-
* newb
git branch 显示所有分支,分支存在于.git/refs/heads/branchname中,里面是个commit object
也可以用checkout -b newb 直接建立并切换到新分支
分支用于新的编辑工作,如果合适可以合并到主分支,通常叫master分支
- $ echo "Work, work, work" >>hello
-
$ cat hello
-
Hello World
-
It's a new day for git
-
Work, work, work
-
$ git commit -m "Some work" -i hello
-
Created commit db6bd80: Some work
-
1 files changed, 1 insertions(+), 0 deletions(-)
-i hello 参数相当于 update-index hello
接下来可以在主分支上工作(比如仓库的维护者)
- $ git checkout master
-
Switched to branch "master"
- $ echo "Play, play, play" >>hello
-
$ echo "Lots of fun" >>example
-
$ git commit -m "Some fun." -i hello example
-
Created commit a066fc0: Some fun.
-
2 files changed, 2 insertions(+), 0 deletions(-)
gitk 命令可以以图形方式显示, --all表示所有分支
接下来可以合并newb中的内容到master
- $ git merge -m "Merge work in newb" newb
-
Auto-merged hello
-
CONFLICT (content): Merge conflict in hello
-
Automatic merge failed; fix conflicts and then commit the result.
由于有冲突,需要手工解决
- $ git merge -m "Merge work in newb" newb
-
You are in the middle of a conflicted merge.
- $ cat hello
-
Hello World
-
It's a new day for git
-
<<<<<<< HEAD:hello
-
Play, play, play
-
=======
-
Work, work, work
-
>>>>>>> newb:hello
把hello中的冲突解决掉
- Hello World
-
It's a new day for git
-
Play, play, play
-
Work, work, work
- $ git commit -i hello
-
Created commit fae3da1: Merge work in newb
也可以用 show-branch命令查看非图形格式的分支
- $ git show-branch --topo-order --more=1 master newb
-
* [master] Merge work in newb
-
! [newb] Some work
-
--
-
- [master] Merge work in newb
-
*+ [newb] Some work
-
* [master^] Some fun.
接下来,在newb分支工作的人可以进行 “upstream changes"
- $ git merge -m "Merge upstream changes." master
-
Updating db6bd80..fae3da1
-
Fast forward (no commit created; -m option ignored)
-
example | 1 +
-
hello | 1 +
-
2 files changed, 2 insertions(+), 0 deletions(-)
通常别人新建了一个分支,做了些工作,仓库的维护者进行merge
merge相当于先把待merge的内容fetch到本地,打一个临时的标签
然后merge到本地
具体的仓库格式取决于传输协议
包含如下等协议
- rsync://remote.machine/path/to/repo.git/
/path/to/repo.git/
git://remote.machine/path/to/repo.git/
git pull 等价于 fetch + merge
为了展示合并的细节,首先回退到合并之前的commit
- $ git checkout newb
-
$ git reset --hard master^2
-
$ git checkout master
-
$ git reset --hard master^
- $ git show-branch
* [master] Some fun.
! [newb] Some work.
--
* [master] Some fun.
+ [newb] Some work.
*+ [master^] Initial commit
合并两个分支使用的是3路合并算法
首先获取分支的共同祖先
- $ mb=$(git merge-base HEAD newb)
- $ echo $mb
-
d6ce3b99c5e317fcfba2d0128e5726799fd2fee8
$ 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把这三个不同的文件都提取到索引文件中
- $ git read-tree -m -u $mb HEAD newb
- $ git ls-files --stage
100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example
100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello
100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
接下来进行merge
- git merge-index git-merge-one-file hello
冲突之后手工解决
有时候我们经常对一个文件修改然后git add
这样会产生很多object, 而只有最后的object才会使用,可以用如下命令删除不需要的object
阅读(2539) | 评论(0) | 转发(0) |