分支就是科幻電影里面的平行宇宙,當你正在電腦前努力學習Git的時候,另一個你正在另一個平行宇宙里努力學習SVN。
如果兩個平行宇宙互不干擾,那對現在的你也沒啥影響。不過,在某個時間點,兩個平行宇宙合並了,結果,你既學會了Git又學會了SVN!
分支在實際中有什么用呢?假設你准備開發一個新功能,但是需要兩周才能完成,第一周你寫了50%的代碼,如果立刻提交,由於代碼還沒寫完,不完整的代碼庫會導致別人不能干活了。如果等代碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。
現在有了分支,就不用怕了。你創建了一個屬於你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到開發完畢后,再一次性合並到原來的分支上,這樣,既安全,又不影響別人工作。
其他版本控制系統如SVN等都有分支管理,但是用過之后你會發現,這些版本控制系統創建和切換分支比蝸牛還慢,簡直讓人無法忍受,結果分支功能成了擺設,大家都不去用。
但Git的分支是與眾不同的,無論創建、切換和刪除分支,Git在1秒鍾之內就能完成!無論你的版本庫是1個文件還是1萬個文件。
創建與合並分支
在版本回退里,你已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git里,這個分支叫主分支,即master
分支。HEAD
嚴格來說不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是當前分支。
一開始的時候,master
分支是一條線,Git用master
指向最新的提交,再用HEAD
指向master
,就能確定當前分支,以及當前分支的提交點:
每次提交,master
分支都會向前移動一步,這樣,隨着你不斷提交,master
分支的線也越來越長:
當我們創建新的分支,例如dev
時,Git新建了一個指針叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示當前分支在dev
上:
你看,Git創建一個分支很快,因為除了增加一個dev
指針,改改HEAD
的指向,工作區的文件都沒有任何變化!
不過,從現在開始,對工作區的修改和提交就是針對dev
分支了,比如新提交一次后,dev
指針往前移動一步,而master
指針不變:
假如我們在dev
上的工作完成了,就可以把dev
合並到master
上。Git怎么合並呢?最簡單的方法,就是直接把master
指向dev
的當前提交,就完成了合並:
所以Git合並分支也很快!就改改指針,工作區內容也不變!
合並完分支后,甚至可以刪除dev
分支。刪除dev
分支就是把dev
指針給刪掉,刪掉后,我們就剩下了一條master
分支:
真是太神奇了,你看得出來有些提交是通過分支完成的嗎?
下面開始實戰。
首先,我們創建dev
分支,然后切換到dev
分支:
$ git checkout -b dev Switched to a new branch 'dev'
git checkout
命令加上-b
參數表示創建並切換,相當於以下兩條命令:
$ git branch dev $ git checkout dev Switched to branch 'dev'
然后,用git branch
命令查看當前分支:
$ git branch * dev master
git branch
命令會列出所有分支,當前分支前面會標一個*
號。
然后,我們就可以在dev
分支上正常提交,比如對readme.txt做個修改,加上一行:
create new branch dev..
然后提交:
$ git add readme.txt $ git commit -m "create new branch...." [dev 45ae9a9] create new branch.... 1 file changed, 1 insertion(+)
現在,dev
分支的工作完成,我們就可以切換回master
分支:
$ git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'.
切換回master
分支后,再查看一個readme.txt文件,剛才添加的內容不見了!因為那個提交是在dev
分支上,而master
分支此刻的提交點並沒有變:
現在,我們把dev
分支的工作成果合並到master
分支上:
$ git merge dev Updating 90bc1f7..45ae9a9 Fast-forward readme.txt | 1 +
1 file changed, 1 insertion(+)
git merge
命令用於合並指定分支到當前分支。合並后,再查看readme.txt的內容,就可以看到,和dev
分支的最新提交是完全一樣的。
注意到上面的Fast-forward
信息,Git告訴我們,這次合並是“快進模式”,也就是直接把master
指向dev
的當前提交,所以合並速度非常快。
當然,也不是每次合並都能Fast-forward
,我們后面會講其他方式的合並。
合並完成后,就可以放心地刪除dev
分支了:
$ git branch -d dev Deleted branch dev (was 45ae9a9).
刪除后,查看branch
,就只剩下master
分支了:
$ git branch * master
因為創建、合並和刪除分支非常快,所以Git鼓勵你使用分支完成某個任務,合並后再刪掉分支,這和直接在master
分支上工作效果是一樣的,但過程更安全。
小結
Git鼓勵大量使用分支:
查看分支:git branch
創建分支:git branch <name>
切換分支:git checkout <name>
創建+切換分支:git checkout -b <name>
合並某分支到當前分支:git merge <name>
刪除分支:git branch -d <name>
解決沖突
人生不如意之事十之八九,合並分支往往也不是一帆風順的。
准備新的feature1
分支,繼續我們的新分支開發:
$ git checkout -b feature1 Switched to a new branch 'feature1'
修改readme.txt最后一行,改為:
create new branch feature1..
在feature1
分支上提交:
$ git add readme.txt $ git commit -m "create new branch feature1 first modify" [feature1 b4309b0] create new branch feature1 first modify 1 file changed, 1 insertion(+)
切換到master
分支:
$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits)
Git還會自動提示我們當前master
分支比遠程的master
分支要超前1個提交。
在master
分支上把readme.txt文件的最后一行改為:
goback master....
提交:
$ git add readme.txt $ git commit -m "goback master first modify" [master 0b56936] goback master first modify 1 file changed, 1 insertion(+)
現在,master
分支和feature1
分支各自都分別有新的提交,變成了這樣:
這種情況下,Git無法執行“快速合並”,只能試圖把各自的修改合並起來,但這種合並就可能會有沖突,我們試試看:
$ git merge feature1 Auto-merging readme.txt CONFLICT (content): Merge conflict in readme.txt Automatic merge failed; fix conflicts and then commit the result.
果然沖突了!Git告訴我們,readme.txt文件存在沖突,必須手動解決沖突后再提交。git status
也可以告訴我們沖突的文件:
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) You have unmerged paths. (fix conflicts and run "git commit") Unmerged paths: (use "git add <file>..." to mark resolution) both modified: readme.txt no changes added to commit (use "git add" and/or "git commit -a")
我們可以直接查看readme.txt的內容:
test git modify second study git three add four add modify five add modify six add modify seven add modify eight add modify ... create new branch dev.. <<<<<<< HEAD goback master.... ======= create new branch feature1.. >>>>>>> feature1
Git用<<<<<<<
,=======
,>>>>>>>
標記出不同分支的內容,我們修改如下后保存:
test git modify second
study git
three add
four add modify
five add modify
six add modify
seven add modify
eight add modify ...
create new branch dev..
create new branch feature1..
goback master....
再提交:
$ git add readme.txt $ git commit -m "fixed conflicts" [master 0f3d64a] fixed conflicts
現在,master
分支和feature1
分支變成了下圖所示:
用帶參數的git log
也可以看到分支的合並情況:
$ git log --graph --pretty=oneline --abbrev-commit * 0f3d64a fixed conflicts |\ | * b4309b0 create new branch feature1 first modify * | 0b56936 goback master first modify |/
* 45ae9a9 create new branch.... * 90bc1f7 test name * c1bdf43 test commit * dd34c9a no add but commit,because use other parameter * 4ed30d1 eight modify dify * b45ca96 eight modify * 9332d40 seven modify * 72c6f9b six modify * f64b5a0 five modify * de8fd65 four modify * 83a4b1e three modify * 01c05cf two modify * 1acafa7 first modify * 09c1bba first git
最后,刪除feature1
分支:
$ git branch -d feature1 Deleted branch feature1 (was b4309b0).
小結
當Git無法自動合並分支時,就必須首先解決沖突。解決沖突后,再提交,合並完成。
用git log --graph
命令可以看到分支合並圖。