請選擇 進入手機版 | 繼續訪問電腦版

[科技資訊] Git 各指令的本質,真是通俗易懂啊

[複製鏈接]

269

主題

269

帖子

969

積分

超級版主

Rank: 8Rank: 8

積分
969
qrcode
分享到:
發表於 2021-5-16 14:28:09 | 顯示全部樓層 |閱讀模式
<

前言
作為當前世界上最強大的代碼管理工具Git相信大家都很熟悉,但據我所知有很大一批人停留在clone、commit、pull、push...的階段,是不是對rebase心裡沒底只敢用merge?

碰見版本回退就抓瞎?別問我怎麼知道的,問就是:“我曾經就是這樣啊~~”。

針對這些問題,今天我就將這幾年對Git的認知和理解分享出來,盡可能的從本質去講解Git,幫助你一步一步去了解Git的底層原理,相信讀完本篇文章你便可以換種姿態,更加風騷得使用Git各種指令。

目錄
1. 基本概念

1.1 Git的優勢

1.2 文件狀態

1.3 commit 節點

1.4 HEAD

1.5 遠程倉庫

2. 分支

2.1 什麼是分支?
3. 命令詳解

3.1 提交相關

3.2 分支相關

3.3 合併相關

3.4 回退相關

3.5 遠程相關

1、基本概念
1.1 Git的優勢
Git是一個分佈式代碼管理工具,在討論分佈式之前避免不了提及一下什麼是中央式代碼管理倉庫

中央式:所有的代碼保存在中央服務器,所以提交必須依賴網絡,並且每次提交都會帶入到中央倉庫,如果是協同開發可能頻繁觸發代碼合併,進而增加提交的成本和代價。最典型的就是svn

分佈式:可以在本地提交,不需要依賴網絡,並且會將每次提交自動備份到本地。每個開發者都可以把遠程倉庫clone一份到本地,並會把提交歷史一併拿過來。代表就是Git

那Git相比於svn有什麼優勢呢?

打個比方:"巴拉巴拉寫了一大堆代碼,突然發現寫的有問題,我想回到一個小時之前",對於這種情況Git的優勢就很明顯了,因為commit的成本比較小並且本地會保存所有的提交記錄,隨時隨刻可以進行回退。

在這並不是說svn的不能完成這種操作,只是Git的回退會顯得更加的優雅。 Git相比於中央式工具還有很多優點,就不一一列舉了,感興趣的可自行了解。

1.2 文件狀態
在Git中文件大概分為三種狀態:已修改(modified)、已暫存(staged)、已提交(committed)

修改:Git可以感知到工作目錄中哪些文件被修改了,然後把修改的文件加入到modified區域

暫存:通過add命令將工作目錄中修改的文件提交到暫存區,等候被commit

提交:將暫存區文件commit至Git目錄中永久保存

1.3 commit節點
為了便於表述,本篇文章我會通過節點代稱commit提交

在Git中每次提交都會生成一個節點,而每個節點都會有一個哈希值作為唯一標示,多次提交會形成一個線性節點鏈(不考慮merge的情況),如圖1-1

節點上方是通過 SHA1計算的哈希值

C2節點包含C1提交內容,同樣C3節點包含C1、C2提交內容

1.4 HEAD
HEAD是Git中非常重要的一個概念,你可以稱它為指針或者引用,它可以指向任意一個節點,並且指向的節點始終為當前工作目錄,換句話說就是當前工作目錄(也就是你所看到的代碼)就是HEAD指向的節點。

還以圖1-1舉例,如果HEAD指向C2那工作目錄對應的就是C2節點。具體如何移動HEAD指向後面會講到,此處不要糾結。

同時HEAD也可以指向一個分支,間接指向分支所指向的節點。

1.5 遠程倉庫

雖然Git會把代碼以及歷史保存在本地,但最終還是要提交到服務器上的遠程倉庫。通過clone命令可以把遠程倉庫的代碼下載到本地,同時也會將提交歷史、分支、HEAD等狀態一併同步到本地,但這些狀態並不會實時更新,需要手動從遠程倉庫去拉取,至於何時拉、怎麼拉後面章節會講到。

通過遠程倉庫為中介,你可以和你的同事進行協同開發,開發完新功能後可以申請提交至遠程倉庫,同時也可以從遠程倉庫拉取你同事的代碼。

注意點

因為你和你的同事都會以遠程倉庫的代碼為基準,所以要時刻保證遠程倉庫的代碼質量,切記不要將未經檢驗測試的代碼提交至遠程倉庫

2、分支
2.1 什麼是分支?
分支也是Git中相當重要的一個概念,當一個分支指向一個節點時,當前節點的內容即是該分支的內容,它的概念和HEAD非常接近同樣也可以視為指針或引用,不同的是分支可以存在多個,而HEAD只有一個。通常會根據功能或版本建立不同的分支。

那分支有什麼用呢?

舉個例子:你們的App 經歷了千辛萬苦終於發布了v1.0版本,由於需求緊急v1.0上線之後便馬不停蹄的開始v1.1,正當你開發的興起時,QA同學說用戶反饋了一些bug,需要修復然後重新發版,修復v1.0肯定要基於v1.0的代碼,可是你已經開發了一部分v1.1了,此時怎麼搞?

面對上面的問題通過引入分支概念便可優雅的解決,如圖2-1

先看左邊示意圖,假設C2節點既是v1.0版本代碼,上線後在C2的基礎上新建一個分支ft-1.0

再看右邊示意圖,在v1.0上線後可在master分支開發v1.1內容,收到QA同學反饋後提交v1.1代碼生成節點C3,隨後切換到ft-1.0分支做bug修復,修復完成後提交代碼生成節點C4,然後再切換到master分支並合併ft-1.0分支,到此我們就解決了上面提出的問題

除此之外利用分支還可以做很多事情,比如現在有一個需求不確定要不要上線,但是得先做,此時可以單獨創建一個分支開發該功能,等到啥時候需要上線直接合併到主分支即可。分支適用的場景很多就不一一列舉了。

注意點

當在某個節點創建一個分支後,並不會把該節點對應的代碼複製一份出來,只是將新分支指向該節點,因此可以很大程度減少空間上的開銷。一定要記著不管是HEAD還是分支它們都只是引用而已,量級非常輕

3、命令詳解
3.1 提交相關
前面我們提到過,想要對代碼進行提交必須得先加入到暫存區,Git中是通過命令 add 實現

添加某個文件到暫存區:

git add 文件路徑

添加所有文件到暫存區:

git add .


同時Git也提供了撤銷工作區和暫存區命令

撤銷工作區改動:

git checkout -- 文件名

清空暫存區:

git reset HEAD 文件名

提交:

將改動文件加入到暫存區後就可以進行提交了,提交後會生成一個新的提交節點,具體命令如下:

git commit -m "該節點的描述信息"

3.2 分支相關
創建分支
創建一個分支後該分支會與HEAD指向同一節點,說通俗點就是HEAD指向哪創建的新分支就指向哪,命令如下:

git branch 分支名

切換分支
當切換分支後,默認情況下HEAD會指向當前分支,即HEAD間接指向當前分支指向的節點

git checkout 分支名

同時也可以創建一個分支後立即切換,命令如下:

git checkout -b 分支名

刪除分支
為了保證倉庫分支的簡潔,當某個分支完成了它的使命後應該被刪除。比如前面所說的單獨開一個分支完成某個功能,當這個功能被合併到主分支後應該將這個分支及時刪除。

刪除命令如下:

git branch -d 分支名

3.3 合併相關
關於合併的命令是最難掌握同時也是最重要的。我們常用的合併命令大概有三個merge、rebase、cherry-pick

merge
merge是最常用的合併命令,它可以將某個分支或者某個節點的代碼合併至當前分支。具體命令如下:

git merge 分支名/節點哈希值

如果需要合併的分支完全領先於當前分支,如圖3-1所示

由於分支ft-1完全領先分支ft-2即ft-1完全包含ft-2,所以ft-2執行了“git merge ft-1”後會觸發fast forward(快速合併),此時兩個分支指向同一節點,這是最理想的狀態。

但是實際開發中我們往往碰到是是下面這種情況:如圖3-2(左)

這種情況就不能直接合了,當ft-2執行了“git merge ft-1”後Git會將節點C3、C4合併隨後生成一個新節點C5,最後將ft-2指向C5 如圖3-2 (右)

注意點:

如果C3、C4同時修改了同一個文件中的同一句代碼,這個時候合併會出錯,因為Git不知道該以哪個節點為標準,所以這個時候需要我們自己手動合併代碼

rebase
rebase也是一種合併指令,命令行如下:

git rebase 分支名/節點哈希值
與merge不同的是rebase合併看起來不會產生新的節點(實際上是會產生的,只是做了一次復制),而是將需要合併的節點直接累加 如圖3-3

當左邊示意圖的ft-1.0執行了git rebase master後會將C4節點複製一份到C3後面,也就是C4',C4與C4'相對應,但是哈希值卻不一樣。

rebase相比於merge提交歷史更加線性、乾淨,使並行的開發流程看起來像串行,更符合我們的直覺。既然rebase這麼好用是不是可以拋棄merge了?其實也不是了,下面我羅列一些merge和rebase的優缺點:

merge優缺點:

優點:每個節點都是嚴格按照時間排列。當合併發生衝突時,只需要解決兩個分支所指向的節點的衝突即可

缺點:合併兩個分支時大概率會生成新的節點並分叉,久而久之提交歷史會變成一團亂麻

rebase優缺點:

優點:會使提交歷史看起來更加線性、乾淨

缺點:雖然提交看起來像是線性的,但並不是真正的按時間排序,比如圖3-3中,不管C4早於或者晚於C3提交它最終都會放在C3後面。並且當合併發生衝突時,理論上來講有幾個節點rebase到目標分支就可能處理幾次沖突

對於網絡上一些只用rebase的觀點,作者表示不太認同,如果不同分支的合併使用rebase可能需要重複解決衝突,這樣就得不償失了。但如果是本地推到遠程並對應的是同一條分支可以優先考慮rebase。所以我的觀點是 根據不同場景合理搭配使用merge和rebase,如果覺得都行那優先使用rebase

cherry-pick
cherry-pick的合併不同於merge和rebase,它可以選擇某幾個節點進行合併,如圖3-4
命令行:

git cherry-pick 節點哈希值

假設當前分支是master,執行了git cherry-pick C3(哈希值),C4(哈希值)命令後會直接將C3、C4節點抓過來放在後面,對應C3'和C4'

3.4 回退相關
分離HEAD
在默認情況下HEAD是指向分支的,但也可以將HEAD從分支上取下來直接指向某個節點,此過程就是分離HEAD,具體命令如下:

git checkout 節點哈希值//也可以直接脫離分支指向當前節點git checkout --detach

由於哈希值是一串很長很長的亂碼,在實際操作中使用哈希值分離HEAD很麻煩,所以Git也提供了HEAD基於某一特殊位置(分支/HEAD)直接指向前一個或前N個節點的命令,也即相對引用,如下:

//HEAD分離並指向前一個節點git checkout 分支名/HEAD^

//HEAD分離並指向前N個節點git checkout 分支名~N

將HEAD分離出來指向節點有什麼用呢?舉個例子:如果開發過程發現之前的提交有問題,此時可以將HEAD指向對應的節點,修改完畢後再提交,此時你肯定不希望再生成一個新的節點,而你只需在提交時加上--amend即可,具體命令如下:

git commit --amend

回退
回退場景在平時開發中還是比較常見的,比如你巴拉巴拉寫了一大堆代碼然後提交,後面發現寫的有問題,於是你想將代碼回到前一個提交,這種場景可以通過reset解決,具體命令如下:

//回退N個提交git reset HEAD~N

reset和相對引用很像,區別是reset會使分支和HEAD一併回退。

3.5 遠程相關
當我們接觸一個新項目時,第一件事情肯定是要把它的代碼拿下來,在Git中可以通過clone從遠程倉庫複製一份代碼到本地,具體命令如下:

git clone 倉庫地址

前面的章節我也有提到過,clone不僅僅是複制代碼,它還會把遠程倉庫的引用(分支/HEAD)一併取下保存在本地,如圖3-5所示:

其中origin/master和origin/ft-1為遠程倉庫的分支,而遠程的這些引用狀態是不會實時更新到本地的,比如遠程倉庫origin/master分支增加了一次提交,此時本地是感知不到的,所以本地的origin/master分支依舊指向C4節點。我們可以通過fetch命令來手動更新遠程倉庫狀態

小提示:

並不是存在服務器上的才能稱作是遠程倉庫,你也可以clone本地倉庫作為遠程,當然實際開發中我們不可能把本地倉庫當作公有倉庫,說這個只是單純的幫助你更清晰的理解分佈式

fetch
說的通俗一點,fetch命令就是一次下載操作,它會將遠程新增加的節點以及引用(分支/HEAD)的狀態下載到本地,具體命令如下:

git fetch 遠程倉庫地址/分支名

pull
pull命令可以從遠程倉庫的某個引用拉取代碼,具體命令如下:

git pull 遠程分支名

其實pull的本質就是fetch+merge,首先更新遠程倉庫所有狀態到本地,隨後再進行合併。合併完成後本地分支會指向最新節點

另外pull命令也可以通過rebase進行合併,具體命令如下:

git pull --rebase 遠程分支名

push
push命令可以將本地提交推送至遠程,具體命令如下:

git push 遠程分支名

如果直接push可能會失敗,因為可能存在衝突,所以在push之前往往會先pull一下,如果存在衝突本地解決。 push成功後本地的遠程分支引用會更新,與本地分支指向同一節點。

綜上所述
不管是HEAD還是分支,它們都只是引用而已,引用+節點是 Git 構成分佈式的關鍵

merge相比於rebase有更明確的時間歷史,而rebase會使提交更加線性應當優先使用

通過移動HEAD可以查看每個提交對應的代碼

clone或fetch都會將遠程倉庫的所有提交、引用保存在本地一份

pull的本質其實就是fetch+merge,也可以加入--rebase通過rebase方式合併

回復

使用道具 舉報

使用高級回帖 (可批量傳圖、插入視頻等)快速回復

您需要登錄後才可以回帖 登錄 | 立即註冊

本版積分規則   Ctrl + Enter 快速發佈  

發帖時請遵守我國法律,網站會將有關你發帖內容、時間以及發帖IP地址等記錄保留,只要接到合法請求,即會將信息提供給有關政府機構。
快速回復 返回頂部 返回列表