深刻Git

前言java

這是介紹git的第二篇博客,第一篇博客 http://zhangfengzhe.blog.51cto.com/8855103/1720049 初步介紹了git,下面咱們來對git進行些深刻的介紹。git


COMMIT對象
bash

在git中有好多種對象,COMMIT就是其中一種。HISTORY完整的叫法,應該是COMMIT HISTORY。ide


咱們先來看一個圖:
oop


wKioL1Zs0kjDJeUqAAAVwNDtUhk456.png


HEAD如同遊標似的,指向最新提交的COMMIT對象spa


那麼這個COMMIT對象裏面到底包含了什麼東西呢?3d


COMMIT對象包含了下面一些重要信息:版本控制

  • TREE      目錄樹【咱們全部的文件都必須在一個目錄裏面】指針

  • PARENT    指向前面一個COMMIT對象


若是咱們想取得前一個COMMIT對象,那該怎麼作呢?


當前COMMIT對象的前一個COMMIT  用HEAD~   OR    HEAD~1表示

HEAD~~  等價於  HEAD~2


[root@localhost hadoop]# git log
commit 54cf628bf72462bc37804fcc5df3850eacf9cf7e
Author: zhangfengzhe <zhangfengzhe1990@163.com>
Date:   Sat Dec 5 18:12:13 2015 -0800
    bug fix
commit b714e9882e287957061ce6a06bca26092a0eea48
Author: zhangfengzhe <zhangfengzhe1990@163.com>
Date:   Sat Dec 5 18:05:56 2015 -0800
    commit

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 <zhangfengzhe1990@163.com> 1449367933 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449367933 -0800
bug fix
[root@localhost hadoop]# git cat-file -p HEAD~
tree bffc7fb973539b5560cafbea421b65c7de5630ce
parent 9ea1deaa2bafdf6061ff1dcffdf8ede25b3c0e73
author zhangfengzhe <zhangfengzhe1990@163.com> 1449367556 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449367556 -0800
commit
[root@localhost hadoop]# git cat-file -p HEAD~~
tree ac2e6ed4de163a6fd92f88bd1516728fc1f7b9a9
parent b1a45f0a85ff0e720f240c978a31f7afc2a812b5
author zhangfengzhe <zhangfengzhe1990@163.com> 1449366049 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449366049 -0800
commit
[root@localhost hadoop]# git cat-file -p HEAD~2
tree ac2e6ed4de163a6fd92f88bd1516728fc1f7b9a9
parent b1a45f0a85ff0e720f240c978a31f7afc2a812b5
author zhangfengzhe <zhangfengzhe1990@163.com> 1449366049 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449366049 -0800
commit
[root@localhost hadoop]#

注意:

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碼。


那麼完整的示意圖以下:


wKiom1Zs1Uaik9eWAABT9XDtfEE431.png



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 <zhangfengzhe1990@163.com> 1449367933 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449367933 -0800
bug fix
[root@localhost .git]# git cat-file -p master
tree 1aabc54505caf74bbe716649a974484cdc954438
parent b714e9882e287957061ce6a06bca26092a0eea48
author zhangfengzhe <zhangfengzhe1990@163.com> 1449367933 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449367933 -0800
bug fix
[root@localhost .git]#

經過上面的,咱們是否能夠得出HEAD eq master ?


實際上在GIT裏面,master就是一個分支,即branch,也就是一個文件,裏面存放着HASH!


在GIT中,HEAD是能夠發生指向變化的,稍後會介紹。

[root@localhost .git]# git cat-file -p HEAD~
tree bffc7fb973539b5560cafbea421b65c7de5630ce
parent 9ea1deaa2bafdf6061ff1dcffdf8ede25b3c0e73
author zhangfengzhe <zhangfengzhe1990@163.com> 1449367556 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449367556 -0800
commit
[root@localhost .git]# git cat-file -p master~
tree bffc7fb973539b5560cafbea421b65c7de5630ce
parent 9ea1deaa2bafdf6061ff1dcffdf8ede25b3c0e73
author zhangfengzhe <zhangfengzhe1990@163.com> 1449367556 -0800
committer zhangfengzhe <zhangfengzhe1990@163.com> 1449367556 -0800
commit
[root@localhost .git]# 
[root@localhost .git]# git rev-parse HEAD
54cf628bf72462bc37804fcc5df3850eacf9cf7e
[root@localhost .git]# git rev-parse master
54cf628bf72462bc37804fcc5df3850eacf9cf7e
[root@localhost .git]#


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對象!  以下圖所示:


wKiom1Zs2ZKhNBBSAAApPW7e2NQ429.png


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]#

畫個圖,來講明:


wKiom1Zs25mxB-qoAAA3ncoRUW0025.png


若是咱們把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,那麼怎麼合併呢?


wKioL1Zs4_LjQUoaAAAudLkMSao378.png


此時此刻,就不能夠將master指向commit-g那麼簡單了。


那麼實際上,git會對於master以及mybranch1分支的共有部分commit-d、commit-e、commit-g進行比較處理,生成一個新的commit對象完成merge操做。但這對於咱們都是透明的,咱們其實無需關心,仍是直接使用git merge便可!

相關文章
相關標籤/搜索