前言
这是介绍git的第二篇博客,第一篇博客 http://zhangfengzhe.blog.51cto.com/8855103/1720049 初步介绍了git,下面我们来对git进行些深入的介绍。
COMMIT对象
在git中有好多种对象,COMMIT就是其中一种。HISTORY完整的叫法,应该是COMMIT HISTORY。 我们先来看一个图: HEAD如同游标似的,指向最新提交的COMMIT对象 那么这个COMMIT对象里面到底包含了什么东西呢? COMMIT对象包含了下面一些重要信息:
如果我们想取得前一个COMMIT对象,那该怎么做呢? 当前COMMIT对象的前一个COMMIT 用HEAD~ OR HEAD~1表示 HEAD~~ 等价于 HEAD~2 [root@localhost hadoop]# git log commit 54cf628bf72462bc37804fcc5df3850eacf9cf7e Author: zhangfengzhe log命令用于查看历史提交记录。 每一个COMMIT都有一个编号,如同那个字符串54cf628bf72462bc37804fcc5df3850eacf9cf7e,其实就是一个HASH码。 [root@localhost hadoop]# git cat-file -t HEAD commit [root@localhost hadoop]# git cat-file -p HEAD tree 1aabc54505caf74bbe716649a974484cdc954438 parent b714e9882e287957061ce6a06bca26092a0eea48 author zhangfengzhe 注意: git cat-file -p XXX eq git show XXX git cat-file -t XXX -p表示打印内容,-t表示取得类型,那么这个XXX,实际上是一个标示,可以是HEAD,HEAD~N,或者HASM码什么的,甚至可以是不完整的HASH都可以。 [root@localhost hadoop]# git cat-file -t ac2e tree [root@localhost hadoop]# git cat-file -p ac2e 100644 blob 5e6618e52979f6f581ce848dda15a0bfc24bac24HelloWorld.java 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391delete.me 100644 blob 4ca70693ed50750b52b2a5d1c289adc554a251aflove.txt 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391t1.txt [root@localhost hadoop]# 通过上面的打印 ,其实也清楚了,文件其实也是一个BLOB对象,目录其实也就是一个TREE对象,也看到了他们的HASH码。 那么完整的示意图如下: |
tree-ish表达式
我们先来看一下.git目录的结构: [root@localhost hadoop]# ll -A total 36 -rw-r--r-- 1 root root 26 Dec 5 18:12 delete.me drwxr-xr-x 8 root root 4096 Dec 5 18:12 .git -rw-r--r-- 1 root root 37 Dec 5 18:11 HelloWorld.java -rw-r--r-- 1 root root 31 Dec 5 06:42 love.txt -rw-r--r-- 1 root root 0 Dec 5 17:40 t1.txt [root@localhost hadoop]# [root@localhost .git]# ll total 96 drwxr-xr-x 2 root root 4096 Dec 4 19:41 branches -rw-r--r-- 1 root root 8 Dec 5 18:12 COMMIT_EDITMSG -rw-r--r-- 1 root root 92 Dec 4 19:41 config -rw-r--r-- 1 root root 73 Dec 4 19:41 description -rw-r--r-- 1 root root 23 Dec 4 19:41 HEAD drwxr-xr-x 2 root root 4096 Dec 4 19:41 hooks -rw-r--r-- 1 root root 361 Dec 5 18:12 index drwxr-xr-x 2 root root 4096 Dec 4 19:41 info drwxr-xr-x 3 root root 4096 Dec 5 03:47 logs drwxr-xr-x 50 root root 4096 Dec 5 18:12 objects -rw-r--r-- 1 root root 41 Dec 5 18:11 ORIG_HEAD drwxr-xr-x 4 root root 4096 Dec 5 18:12 refs [root@localhost .git]# cat HEAD ref: refs/heads/master [root@localhost .git]# cat refs/heads/master 54cf628bf72462bc37804fcc5df3850eacf9cf7e [root@localhost .git]# git cat-file -t 54cf6 commit [root@localhost .git]# 重点需要关注的就是HEAD,它链接到refs/heads/master,而master中存放的就是HASH码,其实就是一个COMMIT对象! 那么这个MASTER到底是什么东西呢? [root@localhost .git]# git cat-file -p HEAD tree 1aabc54505caf74bbe716649a974484cdc954438 parent b714e9882e287957061ce6a06bca26092a0eea48 author zhangfengzhe 通过上面的,我们是否可以得出HEAD eq master ? 实际上在GIT里面,master就是一个分支,即branch,也就是一个文件,里面存放着HASH! 在GIT中,HEAD是可以发生指向变化的,稍后会介绍。 [root@localhost .git]# git cat-file -p HEAD~ tree bffc7fb973539b5560cafbea421b65c7de5630ce parent 9ea1deaa2bafdf6061ff1dcffdf8ede25b3c0e73 author zhangfengzhe git rev-parse可以用于查看指向的HASH 那么问题来了,怎么定位到master~3的根目录呢? [root@localhost .git]# git cat-file -p master^{tree} 100644 blob d3aaea0fd831e7efc5751357d77afd3dde514b3fHelloWorld.java 100644 blob 01f9a2aac3e315c5caa00db4019f1d934171dba0delete.me 100644 blob 4ca70693ed50750b52b2a5d1c289adc554a251aflove.txt 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391t1.txt [root@localhost .git]# git cat-file -p master:love.txt i love you i hit you i eat you [root@localhost .git]# 其实,上面的例子,就是所谓的tree-ish表达式! tree-ish表达式,可以方便我们快速定位到任何一个对象。 |
创建/删除/合并分支
git与其他的版本控制系统,差别还是蛮大的,分支对于git而言,只是一个文件,里面存放HASH码而已。 [root@localhost hadoop]# git branch * master [root@localhost hadoop]# git branch 列出所有的branch 注意到master前面有一个*号,说明的是当前正在使用的branch [root@localhost hadoop]# git branch mybranch1 [root@localhost hadoop]# git branch * master mybranch1 [root@localhost hadoop]# 如果我们不想在master分支上,想切换到mybranch1上,该如何操作呢? [root@localhost hadoop]# git checkout mybranch1 Switched to branch 'mybranch1' [root@localhost hadoop]# git branch master * mybranch1 [root@localhost hadoop]# 如果我们想先创建一个分支,然后切换到这个分支的话,可以分步使用git branch + git checkout 来完成,当然也可以快捷完成:git checkout -b即可。 [root@localhost hadoop]# git checkout -b mybranch2 Switched to a new branch 'mybranch2' [root@localhost hadoop]# git branch master mybranch1 * mybranch2 [root@localhost hadoop]# 切换分支,是什么鬼? [root@localhost hadoop]# git branch master * mybranch1 [root@localhost hadoop]# cat .git/HEAD ref: refs/heads/mybranch1 [root@localhost hadoop]# git checkout master Mlove.txt Switched to branch 'master' [root@localhost hadoop]# cat .git/HEAD ref: refs/heads/master [root@localhost hadoop]# 切换分支,只是改变了HEAD文件指向而已! 其实这更加准确的说明了,HEAD指向的是当前的branch! 创建分支到底意味着什么? [root@localhost hadoop]# cd .git/refs/heads/ [root@localhost heads]# ll total 16 -rw-r--r-- 1 root root 41 Dec 12 00:10 master -rw-r--r-- 1 root root 41 Dec 12 00:24 mybranch1 [root@localhost heads]# cat * 51d60776dc4cbe9c07f65cee378874f232e198d8 51d60776dc4cbe9c07f65cee378874f232e198d8 [root@localhost heads]# 我们清楚的看到了,mybranch1和master一样,以文件的形式存在,放的是HASH CODE。 此时此刻,其实mybranch1 和 master 一样都指向当前的COMMIT对象! 如下图所示: HEAD的指向就是通过git checkout在不同的分支上来回切换。 下面,我们来做几个小例子: [root@localhost hadoop]# git branch master * mybranch1 [root@localhost hadoop]# vi love.txt [root@localhost hadoop]# cat love.txt i love you i hit you i eat you changed ? add [root@localhost hadoop]# git cat-file -p HEAD:love.txt i love you i hit you i eat you changed ? 分支改变了,但没有提交,看看对其他分支的影响: [root@localhost hadoop]# git checkout master Mlove.txt Switched to branch 'master' [root@localhost hadoop]# git cat-file -p HEAD:love.txt i love you i hit you i eat you changed ? 分支提交后,这个分支就会变化,但是不会影响其他分支的,其他分支看不到这种变化!因为HEAD指针指向的问题。 [root@localhost hadoop]# git checkout mybranch1 Mlove.txt Switched to branch 'mybranch1' [root@localhost hadoop]# git branch master * mybranch1 [root@localhost hadoop]# git add love.txt [root@localhost hadoop]# git commit -m 'test branch' love.txt [mybranch1 8cf1339] test branch 1 file changed, 1 insertion(+) [root@localhost hadoop]# git branch master * mybranch1 [root@localhost hadoop]# git cat-file -p HEAD:love.txt i love you i hit you i eat you changed ? add [root@localhost hadoop]# git checkout master Switched to branch 'master' [root@localhost hadoop]# git branch * master mybranch1 [root@localhost hadoop]# git cat-file -p HEAD:love.txt i love you i hit you i eat you changed ? [root@localhost hadoop]# 如果此时此刻,我们对mybranch1分支进行删除,会发生什么呢? [root@localhost hadoop]# git branch * master mybranch1 [root@localhost hadoop]# git branch -d mybranch1 error: The branch 'mybranch1' is not fully merged. If you are sure you want to delete it, run 'git branch -D mybranch1'. [root@localhost hadoop]# 画个图,来说明: 如果我们把mybranch1分支删除掉,那么commit-e对象将会找不到了,因为没有对象可以指向它,会成为“孤儿”,GIT不会允许出现这样的情况,此时我们要做的就是合并! [root@localhost hadoop]# git branch * master mybranch1 [root@localhost hadoop]# git merge mybranch1 Updating 7d1ea72..8cf1339 Fast-forward love.txt | 1 + 1 file changed, 1 insertion(+) [root@localhost hadoop]# git cat-file -p HEAD:love.txt i love you i hit you i eat you changed ? add [root@localhost hadoop]# 其实,合并后,只是更新了master的指向,master会指向commit-e而已。 注意git merge执行后的提示“Fast-forward”,这是个什么意思? 其实说的就是,这种合并是一个比较简单的合并方式,因为仅仅只是改变了master的指向就达到了合并的目的。还有一种较为复杂的情况3-WAY MERGE: 如果master分支有新的COMMIT,而mybranch1有2个新的COMMIT,那么怎么合并呢? 此时此刻,就不可以将master指向commit-g那么简单了。 那么实际上,git会对于master以及mybranch1分支的共有部分commit-d、commit-e、commit-g进行比较处理,生成一个新的commit对象完成merge操作。但这对于我们都是透明的,我们其实无需关心,还是直接使用git merge即可! |