【摘】Git-從零單排 03期

前言

書接上回,02期裏大概的介紹了 Git  部分操做的底層原理。這一期呢,筆者想談談 指針 。在 Git 裏, 指針  這個概念過重要了。若是理解不到位,你可能根本不敢碰 reset 、 rebase 等指令,就算用了,也是隻知其一;不知其二。相反,若是你對 指針 理解深入,遇到任何狀況,你都會在第一時間想到多種解決辦法,你將再也不被侷限在 add、commit、pull、push這四個指令裏。 git

指針

咱們先不聊指針,咱們先來看看 Git 裏常見的 5 個事務,它們究竟是什麼?
首先是 tag ,項目功能上線,咱們須要打 tag 。具體的命令以下:vim

git tag v1.0.0 # 針對當前的commit,打了一個名爲v1.0.0的輕量標籤
git tag -a v1.0.0 -m 'tag說明,項目1.0.0,功能有xxxx' # 這是打一個詳細的附註標籤
git tag -a v1.0.0 75f75b1 -m 'tag說明,項目1.0.0,功能有xxxx' # 在 75f75b1 提交對象上打了一個附註標籤
git tag # 查看整個倉庫全部 tag
git tag -d v1.0.0 # 刪除 tag v1.0.0
git tag -l 'v3.*' # 只查看v3.開頭的tag
git push origin --tags # 把標籤推送到遠端共享
git push origin :refs/tags/v1.0.0 # 刪除遠端標籤
複製代碼

咱們實際操做一下,咱們先進入到項目位置,這裏就繼續使用以前文章裏操做過的項目目錄。bash

vim hello-git.txt # 輸入字符 tag 1
git commit -am 'chore: tag 1' # 生成一個 commit
[master 1fce6d3] chore: tag 1
 1 file changed, 1 insertion(+)
git tag tag-1 # 在 1fce6d3 上打一個輕量標籤,標籤名爲 tag-1
git cat-file -t tag-1 # 查看 tag-1 的類型
commit # 顯示爲類型 commit ,這裏咱們先記一下,tag的類型是 commit
git cat-file -p tag-1 # 查看 tag-1 的具體內容
tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6be
parent 75f75b1a233547abb2672049ca330881d5055bda
author kael <kael_yp@foxmail.com> 1591448989 +0800
committer kael <kael_yp@foxmail.com> 1591448989 +0800

chore: tag 1
複製代碼

看到這裏,有沒有發現一個現象, tag 的行爲跟咱們之前查看 commit 是否是超級像?咱們暫且,按下不表。
咱們接着看一下 branch 。平時,開發中,咱們接到一個新的需求,通常都會從新開一個新的分支,用於開發,等開發完成,再合併到主線分支。 branch 命令以下:post

git branch branchName # 建立分支
git branch # 查看分支 能夠帶 -a -vv 等參數。
git branch -d # 刪除分支,-D 強制刪除。
git branch -u # 設置上游分支
git branch -m # 修改分支名稱
複製代碼

來,實際操做一波。ui

git branch -a # 查看咱們項目的分支
* master # 只有一個master分支,由於咱們還沒建立過度支,目前就只會有一個默認的master分支。這裏有一個 * ,表明咱們當前處於master
git branch test # 建立一個test
git branch -m test newTest # 修改 test 分支爲 newTest
git branch -a # 查看分支狀況
* master 
  newTest # 和預料同樣,如今有一個 newTest 分支
git cat-file -t newTest 
commit # 也是 commit
git cat-file -p newTest # 查看 newTest 這個分支的內容,跟 tag-1 徹底同樣。看官,請記一下
tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6be
parent 75f75b1a233547abb2672049ca330881d5055bda
author kael <kael_yp@foxmail.com> 1591448989 +0800
committer kael <kael_yp@foxmail.com> 1591448989 +0800

chore: tag 1
git branch -d newTest # 刪除 newTest 分支
Deleted branch newTest (was 1fce6d3). # 成功刪除newTest分支,這裏 1fce6d3 又出現了,跟上面的tag-1的同樣
複製代碼

這裏咱們就大概演示一下branch的查看、建立、刪除、改名操做。而且,咱們也發現,newTest 分支跟 tag-1 徹底同樣,也在後續統一說明。切換分支後面講。
接下來,咱們再看看 HEAD ,這個咱們通常稱它爲頭指針。spa

git cat-file -t HEAD # 看看頭指針是什麼類型
commit # 也是 commit
git cat-file -p HEAD # 看看頭指針的內容,跟以前查看的也是同樣
tree 7ff2b6fd216355c918546fb3ce23e07dc9bbc6be
parent 75f75b1a233547abb2672049ca330881d5055bda
author kael <kael_yp@foxmail.com> 1591448989 +0800
committer kael <kael_yp@foxmail.com> 1591448989 +0800

chore: tag 1
複製代碼

HEAD 這個是一個特殊的指針,筆者對它的理解就是像一個指揮官,控制着你的大部分對歷史記錄的操做。3d

git log --oneline # 查看一下歷史記錄
1fce6d3 (HEAD -> master, tag: tag-1, newTest) chore: tag 1 # 最重要的在這裏
75f75b1 create lib/git.txt
9eb3d5f create hello-git.txt


複製代碼

好了,到這裏,咱們來分析一下。當咱們輸入查看歷史記錄的時候,你會看到幾個熟悉的字眼, 1fce6d3 (HEAD -> master, tag: tag-1, newTest) chore: tag 1  1fce6d3 是一個提交對象的 hashId  , HEAD -> master 的意思是頭指針當前在 master 分支上,這也就是當前分支,查看分支時,分支前面的那個 * 號就是代碼頭指針在那,同時 master 分支指向 1fce6d3 這個提交對象。 tag: tag-1 的意思是,這個提交對象上有一個標籤,名稱是 tag-1 。 newTest 的意思是 newTest 分支也指向 1fce6d3 這個提交對象。 chore: tag 1 是 1fce6d3 這個提交對象的提交說明。

上面描述的是現象,筆者如今來針對這個現象,表達本身的理解。

首先是 HEAD ,這是一個無論你怎麼操做,都指向當前提交對象的指針,你們都叫它 頭指針 。這個指針的能帶着 branch 一塊兒移動,當你進行commit提交時。

而後是 branch ,你們都叫它分支,可是,我本身的理解裏,我更願意叫它 遊標 ,由於 分支 這個詞在記憶理解的時候,會讓我聯想到 樹木的樹枝,河流的支流,而後會把分支想成一個 有頭有尾的連續的 線條狀的事務。其實,這是大錯特錯的,若是你把分支理解成線條狀的事務,你就會沒法理解切分支的原理,(我之前就是把切分支理解成,把一根根線條狀事務移來移去,把刪除分支理解成把某一整條歷史記錄都刪除)對於分支操做出現問題時,你可能會沒法理解。其實,分支就是一個指向提交對象的指針,刪除分支就只是把一個指針刪除而已,刪除分支並不會影響到任何一個提交對象,也不影響到任何一個歷史記錄(其它操做分支的行爲也都是同樣)。對應 HEAD 來理解,頭指針是一個永遠指向當前提交對象的指針,而 branch 是一個指向當前‘業務’所在的提交對象的指針。
解釋一下這裏說的‘業務’。好比咱們開發一個項目,同事A開發訂單模塊,她就切一個名爲 order 的 分支 ,那麼通常正常狀況下,這個order分支,必定是指向訂單模塊當下所在的那個提交對象的。而後同事B呢,她開發門店模塊,她就切一個名爲 shop 的 分支 ,同理,這個shop的分支,必定是指向門店模塊當下所在的提交對象。
咱們使用 git log order 來看訂單分支的歷史記錄,而後出來的就是 order 所指向的 提交對象的 parent對象造成的鏈表,這是一個以當前提交對象爲‘起始結點’,第一個提交對象爲‘終端結點’的鏈表。再次強調,這個鏈表不是分支,分支只是一個指向這個鏈表‘起始結點’的指針,因此你刪除分支,用reset移動分支時,並不會影響這個已經造成的鏈表記錄。
因此呢, 分支 就是一個提交記錄的別名,由於 order 這種跟業務相關,語義化強的字符,確定比40位的無規律的 hashId好記,對應着 ip 與 域名記憶哈(你就說你記得 www.baidu.com, 仍是記得 180.101.49.11)。還有要強調的一點, 分支 指向的提交對象是能夠變化的。因此我更願意叫它 遊標。

接下來,就該是 tag 了,能夠叫它 標記。功能上來講,它就是一個指向特定提交對象的指針,或者說,它也是一個特定提交對象的別名。爲何有了分支的別名,還要再搞一個 tag 的別名呢?這是由於,分支是能夠移動的,今天分支指向的是 A 提交對象,明天可能就指向 B 了。tag 呢,是一個永遠都不能移動的別名,無論何時使用它,它指向的提交對象絕對是同一個。因此,在項目上線了新功能時,會打個 tag 。若是後面須要讓這個新功能下架,咱們只要操做這個 tag 就行。tag 相對於分支來講,它更特殊一點,若是 tag 與 分支重名(假設是 abc)了,除了在 git branch 命令後,其它地方,使用 abc,拿到的必定是 tag 指針。因此遇到 tag 和 分支重名,必定要記得用 git branch -m abc abcBranch 命令把分支的名稱修改。

就如前面讓看官記下的,tag-1 、 newTest 、HEAD的內容都是同樣的,爲何呢?由於它們在那個時候都指向同一個提交對象 1fce6d3 。在 Git 裏,指針還有不少,好比,stash@{0}, origin/master, HEAD^ , HEAD~1 等。好了,咱們如今都理解了指針,那看看咱們在工做中,能夠怎麼操做吧
指針

示例

新建分支。操做以下:code

git branch test 
# 這是新建一個叫test的分支,這是怎麼新建的呢?其實它原來是這樣的。
git branch test HEAD 
# 就是在頭指針指向的提交對象上創建一個test別名,強調:在 Git 裏大部分放 HEAD 的地方,均可以把 HEAD 省略。
# 好比 
git show # 對應就是 git show HEAD
git log # 對應就是 git log HEAD
# 繼續哈,既然 HEAD 是指針,那這個放 HEAD 的地方就能夠放其它任何指針,就算放 hashId 也是能夠的。
git branch dev 1fce6d3 # 這就是在 1fce6d3 這個提交對象上創建一個 dev 分支。
複製代碼

新建 tag。操做以下:對象

git tag v1.0.0
# 這裏新建一個輕量標籤 v1.0.0,同理可得,它原來是這樣的。
git tag v1.0.0 HEAD
# 在頭指針的位置打一個標籤。如今你理解指針以後,你就能夠給任何一個你打標籤的提交對象打tag,只要你能找到它。
# 好比,我如今根本不知道其它同事在寫的order分支到哪一步了,你也能夠
git tag testTag order # 本系列01期說了,這些操做都是本地,因此你的本地要有拉取到同事的 order 分支。
# 這樣就給 order 分支所指向的提交對象打了一個名叫 testTag 的標籤。而後,你就會被同事打了!
複製代碼

查看之前版本代碼。操做以下:

git reset --hard v1.0.0 
# 假如,咱們已經提交了不少次了,項目已經到了v3.0.0了。而後,領導說,我很懷念咱們加班加點作的1.0版本啊
# 那你就能夠用上面的命令,讓代碼回到1.0的樣子。老闆看完了,你再使用
git reset --hard feat 
# 假設你以前是在feat分支開發。
複製代碼

後語

最後,再來講說, git checkout 這個命令叫簽出,它的做用是移動 HEAD 頭指針。好比,你使用 git checkout branchName 那就是把頭指針掛在 branchName 這個分支上,下一次提交時,頭指針往前移,同時會帶着 branchName 分支一塊兒移動。若是你使用 git reset 命令移動頭指針,頭指針也會帶着 branchName 一塊兒走。你就想象分支身上是有一個洞的,頭指針有一個鉤。頭指針鉤住了分支,就會帶着一塊兒走。 git checkout 呢,就是把頭指針的鉤從分支身上取下,並移動另外一個地方,新地方有洞就鉤住,沒洞的話,Git 會提示你,如今頭指針是分離狀態。 git checkout -b name master 這個命令是,先從 master 分支所指向的提交對象上,新建一個 name 分支,而後把頭指針從當下的位置拿下來,掛到 name 這個分支上,也就新建加切換分支。基於以上觀點,筆者認爲 tag 呢,它是被焊死在提交對象上的,永遠在那。
還有一個,那就是,在操做 Git 的時候,不要擔憂,無論你怎麼玩,只要你不搞 .git 裏的東西,你就不能夠把項目搞壞,你想要的東西,經過 git reflog 命令就能所有找到。因此,若是你理解了指針,那就大膽的操做吧,玩不壞的啦!

世界上任何值得去的地方,都沒有捷徑!

相關文章
相關標籤/搜索