分支其實就是一個指針,指向某個commit提交,每進行一次提交,指針都會移到最新提交的位置。相似於串珠子,每次提交就像是一個一個的珠子,經過分支串聯起來。以下圖所示segmentfault
執行git init
命令後,git會給項目自動建立一個空白的主分支,即master分支。
① 查看分支
能夠經過git branch
命令查看當前項目中存在哪些分支,可是該命令只能查看本地分支,沒法查看遠程分支,若是要查看遠程分支,那麼咱們須要帶上-a參數,即git branch -a
。this
> git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/branch-a remotes/origin/maste
帶星號的那個分支表示是當前活躍的分支,遠程分支以remotes/origin/開頭,以後的爲遠程版本庫的分支名。spa
② 建立分支
分支必需要至少一次提交,沒有提交的分支是不能經過git branch -a
命令查看到的。咱們能夠經過git branch <分支名>
命令來建立分支,建立的分支會以當前分支爲基礎,也就是說,新建立的分支會帶上當前分支的全部提交。指針
// 當前在master分支,執行命令建立branch-1 > git branch branch-1 // 建立的branch-1分支擁有和master分支同樣的提交信息
固然咱們能夠指定新建立分支的指針位置,即只要部分提交信息,能夠經過git branch <分支名> <commit-id>
,那麼建立的分支的提交信息,將僅僅包含當前分支的第一個提交到<commit-id>的這部分提交信息。code
> git branch branch-2 c12ac1
默認狀況下是以當前分支爲基礎建立新的分支,咱們也能夠指定以某條已經存在的分支爲基礎建立新分支,git branch <新分支名> <舊分支名>
,如:對象
// 建立的branch-3將以branch-2分支爲基礎 > git branch branch-3 branch-2
③ 切換分支git branch
命令建立好分支後,並不會自動從當前分支切換到新建立的分支上,若是咱們須要切換到其餘分支,那麼能夠經過git checkout <分支名>
命令切換到指定分支上,如:blog
// 切換到branch-a分支上 > git checkout branch-a
固然checkout還能夠作到建立並切換到新分支上,那就是加上-b參數,即git checkout -b <分支名>
,如:開發
// 建立並切換到branch-4分支上 > git checkout -b branch-4
checkout命令還能夠經過--orphan參數,建立一個孤兒分支,這是一個沒有任何提交的分支,因此其沒法被查看到,也叫空白分支,如:rem
// 建立一個沒有任何提交信息的branch-5分支 > git checkout --orphan branch-5 Switched to a new branch 'branch-5' > git branch branch-1 branch-2 branch-3 branch-4 master
能夠看到新建立的branch-5孤兒分支並無被查看到,只有進行提交以後,才能被查看到。
須要注意的是,咱們切換分支的時候,當前分支中工做區或暫存區中有新的修改未提交,那麼是沒法切換到其餘分支的,不過咱們能夠經過git checkout --force <分支>
的方式強制切換,可是這樣會致使以前的修改被丟棄。
④ 刪除分支
刪除分支,咱們能夠經過git branch -d <分支名>
命令,須要注意的是,刪除分支的時候是沒法本身刪除本身的,也就是說,沒法刪除當前活躍分支。當咱們建立好了一個新的分支後,若是尚未在這個分支上進行過任何提交,那麼咱們能夠直接刪除,由於新建立的分支上沒有提交過新的東西,版本庫沒有發生變化。可是若是咱們在新建立的分支上提交過新的修改,那麼新的分支上有了新的東西,那麼這個時候刪除分支就會提示,要刪除的分支上有尚未被merge的東西,而沒法刪除,固然能夠經過git branch -D <分支名>
的方式進行強制刪除。
// 當前在master分支上 // branch-1提交過新的修改 > git branch -d branch-1 error: The branch 'branch-1' is not fully merged. If you are sure you want to delete it, run 'git branch -D branch-1'. > git branch -D branch-1 Deleted branch branch-1 (was 091034a).
⑤ 恢復分支
因爲刪除分支的時候,只是刪除了指向相關提交的指針,可是該提交對象依然會留在版本庫中,因此咱們只要找到刪除分支時的散列值,那麼就能夠經過git branch <分支名> <刪除分支的散列值>
恢復刪除的分支,例如,上面刪除分支的時候的時候散列值爲091034a,如:
// 恢復branch-1分支 > git branch branch-1 091034a
固然咱們能夠同git reflog
找到刪除分支的時候的散列值
分支合併是很是常見的操做,好比,咱們在主線分支上開發新功能,同時在另外一個分支上進行bug的修復,咱們要將bug的修復合併到主線上;除了兩條分支上須要合併以外,同一條分支上也存在着合併的狀況,好比,有兩個開發者都在主線分支上進行開發,一個開發者A提交代碼後,另外一個開發者B須要把開發者A提交的代碼同步過來,也須要將A的提交進行合併。咱們能夠經過merge命令而且指定一個分支名,能夠將指定的分支合併到當前分支上。
// 切換到master分支上 > git checkout master // 將branch-1分支合併到master分支上 > git merge branch-1
git merge branch-1
等價於git merge branch-1 master
,merge後面的第一個參數是源分支,第二個參數爲目標分支。
從圖上能夠看到,branch-1由master分支的第二次提交處分叉而來,同時master分支上進行了第三次提交,而branch-1分支上則進行了第四和第五次提交,而後將branch-1上的提交合併到master分支上,在合併的過程當中,額外產生了一次合併提交,即第六次提交。經過git log
咱們能夠看到這六次提交都在,咱們能夠經過git log --graph
查看到分支的圖形化結構。
① 分支合併的過程當中發生的事
進行合併的時候,git可以自動進行合併。若是兩個修改的是同一個文件可是修改的地方不同,也就是說,修改的不是同一行,同一個文件在兩個不一樣的分支上顯示不一樣,好比a分支修改的是第一行,b分支修改的是第二行,對於a分支來講,該文件的第二行內容與b文件的第二行內容不一樣,對於b分支來講,該文件第一行內容和a分支的第一行內容不一樣,那麼這個兩個分支合併的時候,該文件第一行和第二行都出現了差別,可是合併以後該文件只有一個版本了,那麼git是如何肯定最終合成應該怎麼顯示呢?git就是找到兩個分支的分叉點,以分叉點爲參照,因此第一行顯示a分支的修改,第二行顯示b分支的修改。如圖所示。
從圖中能夠看出,若是不以分叉點爲參照,那麼合併的時候,第一行就不知道要顯示分支a修改了第一行仍是原始內容,一樣第二行也不知道要顯示分支b修改了第二行仍是原始內容。
② 衝突
在實際的合併中,可能會出現兩個開發者同時對同一個文件的同一行代碼進行修改的狀況,那麼這個時候git就會出現合併衝突,由於這兩個修改相對於分叉點而言都是最新的修改,而且都是同一行,因此git不知道此時應該用哪一個修改才正確,這個時候咱們就須要手動解決衝突。
// 將branch-1分支上的提交合併到master分支上 > git merge branch-1 master Auto-merging foo.txt CONFLICT (content): Merge conflict in foo.txt Automatic merge failed; fix conflicts and then commit the result. > git diff foo.txt **diff --cc foo.txt** **index 5f5fbe7,da8092b..0000000** **\--- a/foo.txt** **+++ b/foo.txt** @@@ -1,3 -1,3 +1,7 @@@ 1 2 -3 -4 ++<<<<<<< HEAD ++3 ++======= ++4 ++>>>>>>> branch-1 > git add . > git commit -m 'merge branch-1 to master'
合併過程當中出現了衝突,咱們找到出現衝突的那個文件進行手動修改,須要去除衝突標誌,而後將其添加到暫存區並提交便可完成合並。
固然修改好衝突文件並添加到暫存區後,咱們還能夠經過git merge --continue
命令繼續merge操做,會自動彈出提交交互窗口,輸入提交信息退出編輯便可完成合並。
③ 快速合併
正常狀況下進行合併操做,無論能不能自動合併成功,都會產生一次用於記錄合併操做的提交信息,以便咱們對版本庫的發展過程進行追蹤,可是當咱們從某個點分叉出一個分支後,一直都沒有在這個分支上提交過,那麼咱們在這個分支上進行合併的時候就變得很是簡單,只須要把該分支的指針移到一下便可,而不須要產生一次合併提交了,咱們稱這種提交爲快速合併提交。快速合併提交不利於版本庫的追蹤,因此咱們能夠經過--on-ff來強制快速合併產生一次合併提交,如:
> git merge --on-ff master branch-1
因爲merge命令合併後會額外產生一次關於合併的提交,這樣會致使咱們的分支不斷地的分叉又不斷的合併,分支結構將會變得很是複雜,而rebase命令則可用讓咱們將某一次提交從一個節點轉移到另外一個節點,從而幫咱們理順提交歷史。
變基的原理很簡單,git會讓咱們想要移動的提交序列在目標分支上按照相同的順序重現一遍,至關於咱們爲各個原提交作了一個副本,它們擁有相同的修改集、同一做者、日期、以及註釋信息。
① 平滑合併分支git rebase <源分支> <目標分支>
相似於merge命令,做用就是將源分支的內容合併到目標分支上,只不過不會額外產生一次合併提交
// 將bug分支合併到master分支上 > git rebase bug master
② 刪除分支上的某個或某段提交
若是在提交的過程當中發現前面的某個提交有錯誤,那麼咱們能夠直接刪除,git rebase --onto startPoint endPoint <要刪除提交的分支>
這裏的startPoint是取不到,可是endPoint是能夠取到的,因此是一個前開後閉的區間,如:
// 刪除master分支上(5af873,5305f6]之間的提交 > git rebase --onto 5af873 5305f6 master
③ 拷貝提交到某個分支上git rebase startPoint endPoint --onto <目標分支>
首先切換到拷貝的源分支上,找到要拷貝的提交區間,而後經過--onto指定目標分支,執行命令後會產生一個遊離分支,這個遊離分支和咱們要的最終結果是同樣的,可是尚未拷貝到目標分支上,而後切換到目標分支上,此時會提示目標分支遺留了一段提交,即咱們拷貝的那段提交,而後會有遊離分支的head位置,咱們在目標分支上將分支指針重置到遊離分支的head便可。
// 切換到master分支 > git checkout master // 將master分支上(5af873,89da14]的提交複製到bug分支上 > git rebase 5af873 89da14 --onto bug // 查看產生的遊離分支 > git branch * (HEAD detached from 838de4f) bug master // 切換到目標分支 > git checkout bug Warning: you are leaving 2 commits behind, not connected to any of your branches: 7a48d4c commit-4 78c3b66 commit-3 If you want to keep them by creating a new branch, this may be a good time to do so with: git branch <new-branch-name> 7a48d4c // 能夠看到git提示目前分支遺留了從master上拷貝的兩個提交,而且頭部在7a48d4c,而後重置目標分支指針到7a48d4c便可 > git reset --hard 7a48d4c
④ 合併多個commit爲一個完整commit
rebase 提供了一個-i參數,可讓咱們進入交互操做界面,git rebase -i startPoint endPoint
找到要合併爲一個提交的區間段(startPoint,endPoint],而後會進入一個交互操做界面,如:
> git rebase -i 5af873 89da14 pick 82a82f3 commit-3 pick 89da145 commit-4 // 如今咱們能夠將這兩個提交合併爲一個,好比將commit-4合併到commit-3中,那麼咱們能夠將commit-4前面的pick改爲s,s表示合併提交 pick 82a82f3 commit-3 s 89da145 commit-4 > :wq退出編輯模式 // 而後會再彈出一個交互界面用於修改提交合並後的信息,默認有能夠不改 > :wq退出編輯模式 // 查看提交歷史能夠看到兩個提交合併成了一個 > git log
【Git】rebase 用法小結
⑤ 修改好久以前的某一次提交
對於最近的一次提交,咱們能夠經過git commit --amend
命令進行修改,那麼好久以前的修改咱們能夠經過rebase命令進行修改,git rebase -i startPoint
// 由於起點取不到,因此找到要修改的哪次提交的上一次提交做爲起點 > git rebase -i 5af873 pick f42d811 commit-3 pick 07fe470 commit-5 // 將要修改的那次提交前的pick修改成e,表示要編輯 e f42d811 commit-3 pick 07fe470 commit-5 > :wq退出編輯模式 // 進入修改模式 > git commit --amend // 修改好以後保存 > :wq退出 // 此時查看log指針只在修改的位置,由於rebase還沒結束,須要繼續 > git rebase --continue // 再次查看log,能夠看到以前好久的一次提交被修改了
① 多人協同開發的時候避免出現鑽石鏈
在實際開發中,並非全部人都會用rebase淨化提交歷史的,因此會常常出現鑽石鏈的狀況,假若有一個開發進行了一次提交併推送到了雲端;而後另外一個開發者也進行了一次修改,可是兩者修改的不是同一個文件,而後其經過git pull
將上一個開發者提交的內容拉下來,因爲git pull
會進行merge操做,會自動建立一個合併提交,因此第二個開發者將獲得一個鑽石鏈,而後將其推送到雲端;而後另一個開發者也進行了一次修改,他修改的也不是同一個文件,即便他使用的是git pull --rebase
命令,那麼前面兩個開發者產生的鑽石鏈也還在並無消失,這個時候怎麼辦呢?首先用git log --graph
找到分叉點的提交id,而後使用git rebase -i <分叉的位置提交id>
表示對分叉點以後的提交進行變基操做,以後會彈出交互窗口,因爲三我的的提交修改的都不是同一個文件,那麼此時不用進行任何操做,直接退出交互窗口的編輯便可淨化提交歷史。
// 第三個開發者獲得了第一個和第二個開發者合併產生的鑽石鏈 > git log --graph // 找到分叉點提交id > git rebase -i 5af873 // 打開交互命令窗口,直接退出不用進行任何操做 > :wq 退出編輯模式 // 再次查看提交歷史是否已經被淨化 > git log --graph
若是三個開發者修改的都是同一個文件,因爲變基操做會重播一遍,因此這三個提交都會在當前分支上重播一遍,而且是一個一個輪播的,因爲三我的修改的都是同一個文件,因此會有兩次合併衝突,每解決一次衝突,將改好的文件加入到暫存區中,而後執行git rebase --continue
,解決完兩次衝突後再執行git log --graph
查看提交歷史是否已經被淨化了。