1. 程式人生 > >git checkout 命令詳解

git checkout 命令詳解

在日常的git操作中,git checkout——檢出,是我們的常用命令。最為常用的兩種情形是建立分支和切換分支。

(一)基礎——千里之行,始於切糕(checkout)

先熟悉下常用操作,建立分支和切換分支,也可以稱為檢出分支。

首先我們新建一個倉庫gitTest,然後新建檔案a,為什麼要用a命名呢,這裡是故意為之,後面為大家揭曉分支。呵呵。或許下面的介紹會有些枯燥乏味,因為您已經對這些命令爛熟於胸,而且運用得相當熟練,那麼您可以直接跳過這一步。

在master分支上,做一次提交c1,然後現在新建一個分支a,並切換到a分支。

這個操作主要會用到兩個命令:

建立新分支:git branch branchName

切換到新分支:git checkout branchName

然後 ,上面兩個命令也可以合成為一個命令:

git checkout -b branchName

(二)真相——HEAD是checkout的靈魂

其實,我們在切換分支,和新建分支的時候,有沒有想過,這些操作操作背後的工作原理是怎樣的呢?最大的功臣就是.git目錄下的HEAD引用,她宛如一個芭蕾舞者,從一個分支飄逸的跳到另一個分支,雖無聲無息,卻精準無比。

在我們身處master分支的時候,您一定很好奇,當前的HEAD的內容是什麼?不妨來看看吧。

我們看到c1的提交hash值和HEAD對應分支master的當前hash值是一樣的。也就是說,HEAD指向的是當前分支名master,而master又對應了當前的最新的一次提交ID.

好,那麼我們再做一次提交,看看master對應的hash值有無變化。

從上圖,我們可以不難看出,HEAD對應的ref沒有變化,還是master,但是master對應的commit ID卻變成了c2對應的commit ID,即更新為最後一次提交的ID咯。

現在,提交一次的原理,我們已然瞭解,那麼切換分支的時候呢??

現在我們身處master分支,然後我們切換到a分支,看看會發生什麼樣的情況吧。

從上圖分析,在master分支上的時候,HEAD指向的是master,對應的是c2的commit ID。而切換到a分支的時候,HEAD也相應的指向了a,同時a對應的是a分支上的最新commit ID。因此,我們可以得出結論,在切換分支的時候,HEAD也會相應的指向對應的分支引用。

但是,使用checkout命令的時候,並不是每次都會改變HEAD指標的指向哦。在什麼情況下HEAD一直堅定不移的擁護者他的女神呢?可謂衣帶漸寬終不悔,長使英雄淚滿襟啊!讓我們接著往下看。

(三)進階——HEAD懂不懂,看你怎麼用

checkout命令用法如下:

1. git checkout [-q] [<commit>] [--] <paths> ...

2. git checkout [<branch>]

3. git checkout [-m] [ [-b | -- orphan ] <new_branch>]  [start_point] 

用法2比用法1的區別在於,用法1包含了路徑。為了避免路徑和引用(或提交ID)同名而發生衝突,可以在<paths>前用兩個連續的連字元作為分隔。用法1的<commit>是可選項,如果省略,則相當於從暫存區進行檢出。

來看個例子:

情景1,省略掉<commit>

現在我們處於master分支下,然後我們修改了檔案a,輸入“c3”文字到a中,這時候,暫存區中的內容是沒有"c3"的,通過git diff可以比較。現在我們從當前分支暫存區中檢出檔案a。那麼我們可以直接使用git checkout a。

  這時候,提示檢出失敗,git以為我們想檢出倉庫a。還記得為什麼在第一步中,我們曾新建的檔案a嗎?這裡終於派上用場了,由於倉庫中還存在分支a,同時當前分支中又存在檔案a,於是git傻傻分不清楚了。這時候怎麼辦?有兩個辦法,第一,我們在命名分支的時候要注意語義性,分支名要具有一定的意義,不能使用簡單的a,b,c來命名,這樣很容易導致分支名和檔名重複;第二,參照用法1,使用兩個連字元來分隔。在目前的情形中,我們使用第二種方法吧。

這時候,發現工作區的內容被暫存區的內容覆蓋,"c3"文字也沒有了,當然HEAD指標也沒有什麼變化,一切又恢復了平靜。

再看一個例子:

情景2,不省略<commit>

在不省略<commit>的時候,<commit>既可以是某一個具體的commit hash值,也可以是某個分支名稱,tag名稱。不論分支也好,tag也好,它們本質上對應的都是一個commit hash值。

在檢出a分支下的a檔案的時候,最好把兩個連字元加上,不然git也會無法區分。整個過程中,HEAD頭指標沒有發生改動。

總結:第1種用法(包含<paths>的用法)不會改變HEAD頭指標,主要使用於指定版本的檔案覆蓋工作區中對應的檔案。如果省略<commit>,則會用暫存區的檔案覆蓋工作區中的檔案,否則用指定提交中的檔案覆蓋暫存區和工作區中的對應檔案。

接下來,我們看看用法2,在第一部分中,我們知道切換分支,會改變HEAD的指向,那麼如果我們是檢出某個commit會怎樣呢?同檢出分支一樣,會用該commit下的內容覆蓋當前分支工作區和暫存區的內容,請看例子。

目前我們處於master分支上,且已經有了兩次提交,分別是c1和c2,然後我們修改a,給a檔案新增內容"c3",並add到暫存區,隨即使用checkout到c1的commit 上。注意,剛開始checkout的時候,git不會允許你直接切換,因為你修改了暫存區的內容, 它會提醒你提交後再切換,這時候,你可以使用-f 強行切換。再檢視狀態的時候,git提示我們已經不在任何分支上,HEAD指標也是指向具體的c1的commit值,進入了“分離頭指標”狀態。這個狀態下,要回到master上面,只需要git checkout master即可,也可以在這個狀態上新建分支。

如果,checkou後面不跟任何引數,則就是對工作區進行檢查,請看例子。

我們身處master分支上,並且沒有任何改動,這時候git checkout沒有任何輸出。然後,我們給a檔案新增內容“c3”,然後再git checkout一下,git就會提示a檔案有修改,是不是很簡單?

總結:對於第2種用法,不是檢出某個具體檔案的的時候,即不指定<paths>的時候,單純的檢出某個commit或分支,是會改變HEAD頭指標的。而且只有當HEAD切換到某個分支的時候才可以對提交進行跟蹤,否則就會進入“分離頭指標”的狀態。如果省略用法2後面的<branch>,則預設對工作區進行狀態檢查。

(四)熟悉的checkout,陌生的用法,媽媽再也不用擔心我的checkout啦!

1. git branch <branch> <start point>

以某個commit建立新分支。 在通常情況下,我們都會在當前分支的基礎上,建立新分支。比如git branch new_branch

也許你不知道,我們還可以基於當前分支的某一次commit來建立分支。請看!

從上圖可見,我們想基於master分支的c1 提交ID建立新分支new_branch,建立成功後,切換到new_branch,檢視log,只有c1,耶~~成功啦! 當然,也可以使用git checkout -b <new_branch> <start point>這個常用的命令。 2.  git checkout --datch <branch> 切換到分支的遊離狀態,預設以該分支下的最後一次提交ID,請看下面的例子。 當前分支為a,然後使用git checkout --detach master,那麼HEAD就會切換到master的最後一次commit值的狀態下!

3. git checkout -B <branch>

這個命令,可以強制建立新的分支,為什麼加-B呢?如果當前倉庫中,已經存在一個跟你新建分支同名的分支,那麼使用普通的git checkout -b <branch>這個命令,是會報錯的,且同名分支無法建立。如果使用-B引數,那麼就可以強制建立新的分支,並會覆蓋掉原來的分支。請看具體操作。

當前分支為master,且倉庫中已經存在分支a,我們先用git checkout -b a來建立a分支,必然會失敗的,並提示我們倉庫中已經有了一個a分支咯,彷彿在說“hi,哥們,你已經有了一個老婆了,一夫一妻制你的不懂?你以為這裡是印度啊?”。隨後,我們使用git checkout -B a,耶~~,it works!

4. git checkout --orphan <branch>

是的,假如你的某個分支上,積累了無數次的提交,你也懶得去打理,打印出的log也讓你無力吐槽,那麼這個命令將是你的神器,它會基於當前所在分支新建一個赤裸裸的分支,沒有任何的提交歷史,但是當前分支的內容一一俱全。新建的分支,嚴格意義上說,還不是一個分支,因為HEAD指向的引用中沒有commit值,只有在進行一次提交後,它才算得上真正的分支。還等什麼呢?趕緊試試!

好了,現在我們終於找到組織了!

5. git checkout --merge <branch>

這個命令適用於在切換分支的時候,將當前分支修改的內容一起打包帶走,同步到切換的分支下。

有兩個需要注意的問題。

第一,如果當前分支和切換分支間的內容不同的話,容易造成衝突。

第二,切換到新分支後,當前分支修改過的內容就丟失了。

所以這個命令,慎用!

6. git checkout -p <branch>

這個命令可以用來打補丁。這個命令主要用來比較兩個分支間的差異內容,並提供互動式的介面來選擇進一步的操作。這個命令不僅可以比較兩個分支間的差異,還可以比較單個檔案的差異哦!

結束語 :至此,關於git checkout命令暫告一段落,對於checkout命令,你也有所熟悉了吧。當然,git checkout還有一些其它用法,本文並沒有講到,你可以在git bash或終端中使用git checkout --help去進一步瞭解!