在Git中提交时,会保存一个提交(commit)对象,它包含一个指向暂存内容快照的指针,作者和相关附属信息,以及一定数量(也可能没有)指向
提交对象直接祖先的指针:第一次提交是没有直接祖先的,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。现在假设
工作目录下有3个文件,准备将他们暂存后提交。暂存操作会对每一个文件计算校验和(即SHA-1哈希字符串),然后把当前版本控制的文件快照
保存到Git仓库中,并将校验和加入暂存区域。当使用git commit新建一个提交对象前,Git会先计算每一个子目录(本例中就是就是项目根目录)
的校验和,然后在Git仓库中将这些目录保存为树(tree)对象。之后Git创建的提交对象(commit),除了包含相关提交信息以外,还包含着指向这
个树对象(项目根目录)的指针,如此他就可以在将来需要的时候,重现此次快照的内容。
这个是提交一次后仓库里的数据
多次提交后Git仓库数据
Git中的分支,其实本质就是个指向commit对象的可变指针。Git会使用master作为分支的默认名字。在若干次提交后,你其实已经有了一个
指向最后一次提交对象的master分支,它在每次提交的时候都会自动向前移动。
一:git branch
经过多次提交仓库中的情况如上,master指向最新的commit,那么怎样创建一个新的分支呢,可以使用git branch+分知名,这里用
git branch testing,就创建了一个基于master的分支。
$git branch testing
HEAD是一个引用,指向正在使用的分支。
进行查看:
下面切换到新的分支:
$git checkout testing
使用git branch可以查看当前都有哪些branch,前面有*的表示,当前所在的branch。
可以看到,此时master与testing都指向同一个commit,这里的9831*******,一共是20个字节,40个十六进制的字符,这个就是将文
件放到暂存区时产生的哈希字符串,它作为文件的名字,这里使用git cat-file -t 查看object的类型,这里至少用前四个字符。
现在 切换到testing分支,修改工作目录下的main.c文件,然后提交,再次看HEAD的指向,发现testing中的HEAD已经指向新的
commit了。
现在对master分支的main.c文件进行修改:
可以看到此时master指向的commit已经变化,形成上图的分支。
git branch -a 列出所有分支,包括remote和local branches
git branch -r 列出remote branches
git branch -d new-branch 删除new-branch
git branch -D new-branch 强制删除new-branch
二:git merge
从上边几张图可以看出先是基于master创建了分支iss53,此时它们指向同一个commit,之后iss32分支进行了commit,然后基
于master创建了新的分支hotfix,并进行了commit。现在要把hotfix分支合并到master分支中。
git merge + 要merge的branch,这样就可以把branch merge到当前branch上了。先git checkout master,然后git merge hotfix,
这样hotfix branch就可以merge到master分支上了,然后git branch -d hotfix对hotfix分支进行删除。这里的merge其实是比较简
单的,由于master分支指向的commit是hotfix分支指向的commit的parent,所以直接移动master指针到hotfix指向的commit就可以了。
git branch --merge查看哪些分支已被并入当前分支
git branch --no-merge查看哪些分支没有被并入当前分支
下面这个是three-way merge
这里要merge c4和c5,此时Git会用两个分支的末端(C4和C5)和他们的共同祖先(C2)进行一次简单的三方合并计算。Git可以
自己裁决哪个公共祖先才是最佳合并基础。
三:git checkout
git checkout branch-name 切换到branch-name
git checkout master 切换到master
git checkout -b new-branch master 从master建立新的new-branch,并同时切换过去new-branch
git checkout -b newbranch 由现在的分支为基础,建立新的branch
git checkout -b newbranch origin 由origin的基础,建立新的branch
git checkout filename 还原档案到Repository状态
git checkout HEAD 将所有档案都checkout出来(最后一次commit的版本),注意,若有修改的档案都会被还原到上一版
git checkout xxxx 将所有档案都checkout出来(xxxx commit的版本,xxxx是commit的编号前四位),注意,若有修改的档案
都会被还原到上一版
四:git show
可以使用git show加上commit名称来显示更详细的commit信息:
也可以使用git show加分支名称,也可显示分支信息:
使用HEAD字段可以代表当前分支的头(也就是最近一次commit):
$git show HEAD
每一次commit都会有“parent commit”,可以使用^表示parent:
$git show HEAD^ 查看HEAD的父母的信息
$git show HEAD^^ 查看HEAD的父母的父母的信息
$git show HEAD~5 查看HEAD上溯5代的信息
有的时候git merge会产生双父母,比如three-way merge的时候,这种情况这样处理:
$git show HEAD^1 查看HEAD的第一个父母
$git show HEAD^2 查看HEAD的第二个父母
五:git archive
可以把当前版本(HEAD所处的位置)给export出来
使用git describe可以查看当前的version
$mkdir ../linux-2.6.11
$git archive -v v2.6.11 | (cd ../linux-2.6.11/ && tar xf -)
$head -4 ../linux-2.6.11/Makefile
从本地git仓库中提取某个版本的kernel:
$git archive -v v2.6.11 | (cd ../linux-2.6.11/ && tar xf -)
-v表示--verbose,注意'v2.6.11'可以是git tag -l列出的tags中的一个,也可以是其他Rev ID例如HEAD等
这里的“&&”类似“;”不过是有区别的,如果每个命令被一个分号“;”所分隔,那么命令会连续的执行下去。如果每个命令
被“&&”号分隔,那么这些命令会一直执行下去,如果中间有错误的命令存在,则不再执行后面的命令,没错则执行到完为止。
这里的“-”作用是把前边的输出作为这里的输入
导出最新的kernel:
$git archive -v HEAD | (cd ../linux-HEAD/ && tar xf -)
或者打成tar包:
$git archive -v --format=tar v3.0 | bzip2 >../linux-3.0.tar.bz2