Git原理與高級使用(2)

上一篇中咱們介紹了git的基礎概念,這篇咱們就來講說分支linux

分支

git中的分支其實只是一個指針指向一個commit對象,而不是像傳統的版本控制系統同樣把整個當前版本複製一份出來。它背後其實就是一個文件,咱們能夠去.git/refs/heads文件夾下面查看,裏面的每一個文件其實就是一個分支,而內容其實就是一串SHA1值,而這個SHA1值又是什麼呢,其實就是一個commit id,如下圖爲例,其實此時master分支文件存放的就是master分支上的最後一次commit id,而分支背後表明的就是一條commit對象鏈。git

而HEAD又是什麼呢,其實它也是一個指針,指向着是當前分支,而不是commit對象。在.git目錄下有一個HEAD文件,裏面就是記錄着HEAD指向的分支。 app

分支合併

接下來就用一個例子來介紹一下git分支合併的過程,首先假設咱們處於master分支上 post

接着咱們從當前master上建立一個新分支dev,而且換到當前分支上 git checkout -b dev
下一步咱們在dev分支上修改並提交一個新的commit
接着咱們回到master分支並執行 git merge dev合併分支
這裏要注意的是merge默認採用的是fast-forward快進模式,它表明着就是直接將master指針指向最新的commit id,中間master分支不會作任何的修改,而沒有建立任何新的commit,可是實際上不是任何狀況下咱們均可以直接這樣快進的,合併時修改的不是同一個文件的同一個內容是能夠直接fast-forward合併,可是由於兩個分支都有過commit,因此合併的時候就必定會產生一個新的commit對象,同時當咱們修改的是同一個文件同一個內容時,合併是會產生衝突的,這是後咱們就必需要手動修復衝突,解決後經過git add標示衝突已解決並用git commit提交,也同樣不可避免會產生一個新的commit。在合併以後,反過來若是被merge的分支想要去merge已經merge完的分支,就直接fast-forward就能夠了,覺得兩條分支已經有了一個交匯點,因此直接快進至交匯點便可,因此這裏實際上是能夠總結一條規律的,什麼狀況下能夠快進呢,就是噹噹前的commit對象鏈能夠從當前想要merge的分支走到要merge到的commit點,那就能夠直接快進了。
那說完快進模式後,天然也就有非快進模式,非快進模式模式在上面提到的狀況時即使沒有衝突,兩個分支也沒有同時進行修改也會產生一個新的commit,在咱們merge時咱們只要加上一個 --no-ff便可, git merge --no-ff dev,那非快進模式的好處是什麼呢,其實就是如同下圖所示,非快進模式能夠幫咱們保留分支的樣子,在樹狀結構中能夠完整的看出分支的狀況

Reset

git reset其實有3中模式,mixed, soft和hard,默認不寫的話會調用mixed模式,那麼3者的區別是什麼呢,mixed其實就是會將reset以後的commit與原commit之間的修改轉到工做區,而soft則是轉到暫存區,最後hard就是直接丟棄掉,因此能夠看出其實reset與它的字面意思有些不一樣的,咱們常常會誤會它是從新設定把以後的commit砍掉,其實它只是回到以前的狀態。3d

Stash

git stash的做用是什麼呢,假設當咱們在feature1上作事時,忽然須要緊急去feature2上作事,此時就須要將feature1上的事情暫時用git stash先存起來而後去feature2上作事,作完再回feature1上經過git stash pop將以前保存的修改取出。須要注意的是git stash會記住目前的commit id,因此若是同一個commit下stash兩次對於同一文件同一行內容的修改,恢復完第一次而且提交事後,在執行第二次恢復就會有衝突。版本控制

Tag

tag就是咱們以前提到的其實就是一個標籤對象,它也是指向某一個commit對象,它與分支的區別就在於當有commit發生時,分支會跟着一塊兒向前移,可是標籤一旦定下來就不會再移動了,因此它一般是用於咱們項目到了一個milestone,須要發佈一個新版本時使用。git標籤分爲兩種輕量級標籤(lightweight)和附註標籤(annotated),就如同字面上的意思,輕量級於附註標籤的區別就是一個有描述信息一個沒有。指針

Diff

git diff背後其實就是利用了linux自帶的diff模塊用來比較文件之間的差異,有興趣的能夠去了解一下日誌

指令

  1. 查看分支列表

    git branchcode

  2. 建立分支

    git branch 分支名cdn

  3. 切換分支

    git checkout 分支名

  4. 切換到上次處於的分支中

    git checkout -

  5. 刪除分支()

    git branch -d 分支名

    這裏須要注意不能刪除當前處於的分支,若是非master分支有改動但還未merge的話也不能夠,除非使用git branch -D 分支名

  6. 建立新分支並切換到新分支

    git checkout -b 分支名

  7. 顯示當前分支最近的一條提交消息

    git branch -v

  8. 將分支合併到當前分支

    git merge 分支名

  9. 禁用fast-forward,會多一個commit id

    git merge --no-ff 分支名

  10. 回退到上一次提交(基於當前的commit)

    git reset --hard HEAD^

  11. 回退到上上一次提交(都是基於當前的commit)

    git reset --hard HEAD^^

  12. 回退到當前分支上的前n次(從當前commit往前n次)的提交

    git reset --hard HEAD~n

  13. 回退到指定commit

    git reset --hard commit信息的前幾位

  14. 修改分支名

    git branch -m 原分支名 新分支名

  15. 將工做區的修改保存

    git stash

  16. 列出全部的保存

    git stash list

  17. 手動設置stash描述

    默認執行git stash返回的描述信息是

    其實就是包含了當前最新commit的消息,那麼咱們能夠經過如下命令來修改描述信息 git stash save 'hello basic'

  18. 恢復最近一次的保存,而且會把此次保存在列表中刪除

    git stash pop

  19. 恢復最近一次的保存,可是不會在列表中刪除

    git stash apply

  20. apply特定一個版,而且會把此次保存在列表中刪除

    git stash apply stash@{0}

  21. 手動刪除指定的一個保存版本

    git stash drop stash@{0}

  22. 建立輕量標籤

    git tag v1.0.1

  23. 建立附註標籤

    git tag -a v1.0.2 -m 'release 1.0.2'

  24. 查看標籤

    git tag能夠查看全部的標籤,同時也能夠經過git tag show 標籤名來看某個標籤

  25. 查找標籤

    git tag -l 'v1.0' 裏面可使用pattern,例如'v*', 表明v開頭的全部標籤

  26. 刪除標籤

    git tag -d 標籤名

  27. 列出每一行都是誰在什麼時間哪一個commit修改的

    git blame 文件名

  28. 比較算入暫存區修改的當前文件與工做區文件之間的區別

    git diff

  29. 比較當前最新commit與工做區的區別

    git diff HEAD

  30. 比較某個commit與工做區的區別

    git diff commit_id

  31. 比較最新提交與暫存區的區別

    git diff --cached

  32. 比較某個commit與暫存區的區別

    git diff --cached commit_id

  33. 一次把本地還未提交的標籤推送上去

    git push origin --tags

  34. 刪除遠程標籤

    能夠經過調用git push origin --delete tag 標籤名,或者使用git push origin :refs/tags/v6.0推送一個空標籤上去

  35. 裁剪掉當前遠程分支

    有時候可能用戶a執行了命令刪除了遠程的a1分支,那麼對於用戶b來講當他執行git remote show origin時,會發現遠程的a1分支是處於stale狀態,也就是說遠程已經沒有了,可是本地還在,這時候就能夠執行git remote prune origin,這樣就會把那個stale狀態下的遠程分支刪掉

場景

  1. 當咱們使用git reset --hard指令回退到以前的commit時,執行git log只能看到當前commit及其以前的commit,那若是我想回到以後的某個commit,我要怎麼獲取commit id呢?

    咱們能夠經過執行git reflog來查看操做日誌,看到咱們使用git的指令的歷史,這樣就能夠找回咱們以前的commit id了,這裏說下reflog背後其實就是記錄着HEAD指針的改變歷史,因此日常都會說不要手動去修改.git目錄下的文件,由於手工修改以後是不會記錄在reflog中的

  2. 我不當心把一個分支用git branch -D 分支名強制刪除掉了,還救得回來嗎?

    是救得回來的,這裏要先強調的是,分支其實只是一個指針,因此即使刪除掉了這個指針,咱們的commit對象還在,因此固然是能夠救回來的,只要咱們經過git reflog找到本來分支指向的commit-id,而後執行git branch 分支名 commit-id就能夠從新建立回咱們的分支了。這裏在延伸說一下,其實在git merge時背後其實也是在合併commit對象,因此咱們其實也能夠經過git merge commit-id來作合併的,分支只是給咱們一個相似貼紙同樣簡單標註了這串分支commit鏈作的是什麼。

Q&A

  1. git checkout commit-idgit reset --hard commit-id 有什麼區別呢?

    二者均可以回到對應的commit點,可是checkout與reset不一樣的是它會處於遊離狀態,任何的修改若是不作提交就會有警告不容許咱們跳去其餘的commit,同時修改完咱們能夠經過git branch 分支名 當前checkout的commit-id來建立一個新分支。

相關文章
相關標籤/搜索