天天看點

detached HEAD意義詳解

        概述:在使用git的過程中,我們常常會對其中的HEAD感到疑惑:HEAD是什麼意思?表示目前分支嗎?什麼是處于遊離态的commit點?等等,這些都和一個叫做detached head的狀态有關,我們來看看它的真面目。

        通常情況下,HEAD指向一個分支;同時,每一個分支對應一個特定的commit(确切的說,一個分支上可以有多個commit,但是隻有一個頂層commit,而且commit之間是簡單的線性關系)。我們來看下面這個包含三個commit的例子,其中目前在master分支上。

                      HEAD (refers to branch 'master')

                        |

                       v

           a---b---c  branch 'master' (refers to commit 'c')

                 ^

                  |

             tag 'v2.0' (refers to commit 'b')

        在這樣的狀況下,如果進行一次送出,目前分支将指向新的送出點。具體來說,git commit建立了一個新的commit id,它的父節點是的commit的id是C。這個時候,HEAD仍然指向master分支,而且指向commit id是d的送出點。

    $ edit; git add; git commit

HEAD (refers to branch 'master')

                             |

                             v

           a---b---c---d  branch 'master' (refers to commit 'd')

                ^

                |

             tag 'v2.0' (refers to commit 'b')

       有時,如果能夠檢出到一個不在分支頂端的commit點是很有用的(通常情況下,我們直接運作指令git checkout mater,這樣checkout到master的最新commit上);同樣,如果能夠建立一個不屬于任何分支的送出點也是一件很有用的事情。運作接下來的兩條指令,看看會發生什麼。

    $ git checkout v2.0  # or

    $ git checkout master^^

HEAD (refers to commit 'b')

               |

               v

           a---b---c---d  branch 'master' (refers to commit 'd')

               ^

               |

             tag 'v2.0' (refers to commit 'b')       

    注意,現在head已經指向commit b,這就是所謂的dedatched head狀态。從這裡我們也可以看出,head是目前index的狀态,而不是目前分支(的最近commit節點)。這僅僅意味着head指向某個特定的commit點,而不是指向每一個特定的分支(的頂端節點)。如果我們此時送出一個commit,看看将要發生什麼:

        $ edit; git add; git commit

HEAD (refers to commit 'e')

                 |

                 v

                 e

                /

           a---b---c---d  branch 'master' (refers to commit 'd')

               ^

               |

             tag 'v2.0' (refers to commit 'b')

    注意,此時産生了一個新的送出點,但是它隻能被head索引到,不屬于任何一個分支。當然,我們還可以給在這個“無名分支”的基礎上繼續送出。

 $ edit; git add; git commit

                    HEAD (refers to commit 'f')

                      |

                      v

                 e---f

                /

           a---b---c---d  branch 'master' (refers to commit 'd')

               ^

               |

             tag 'v2.0' (refers to commit 'b')

     實際上,我們可以進行任何git的正常操作。但是,讓我們開看看如果我們運作git checkout master将會發生什麼:

 $ git checkout master

 HEAD (refers to branch 'master')

                 e---f     |

                /           v

           a---b---c---d  branch 'master' (refers to commit 'd')

               ^

               |

             tag 'v2.0' (refers to commit 'b')

 此時,我們一定要注意,e f已經處于無法被索引到的狀态。最終e和f将被git的預設回收機制所回收,除非我們在它們被回收之前建立一個指向他們的索引。如果我們沒有從commit f離開的話,可以用接下來的指令建立一個指向f的索引。        

$ git checkout -b foo   (1)

$ git branch foo        (2)

$ git tag foo           (3)

1.建立來一個foo分支,指向f,接着更新head指向分支foo,此時,我們不再處在detached head的狀态

2.同樣建立了一個foo分支,但是head仍然指向master分支,仍然處在detached head的狀态。

3.建立了一個新标簽foo,仍處于detached的狀态。

        如果我們從f處離開,我們必須首先恢複他的主體名稱,接着我們才可以建立指向它的索引。例如,為了看看最近的兩個由head指向的commit點,我們可以使用如下的指令:   

$ git reflog -2 HEAD # or

$ git log -g -2 HEAD

參考文獻:man  git-checkout

繼續閱讀