分支在實際中有什麼用呢?假設你準備開發一個新功能,可是須要兩週才能完成,第一週你寫了50%的代碼,馬上提交,因爲代碼尚未寫完,不完整的代碼庫會致使別人不能幹活了。若是等代碼所有寫完再一次提交,又存在丟失天天進度的風險。
如今有了分支,就不能怕了,你建立了一個屬於你本身的分支,別人看不到,還繼續在原來的分支上正常工做,而你在本身的分支上幹活,想提交就提交,直到開發完畢後,在一次性合併到原來的分支上,這樣,既安全,又不影響別人工做。git
Git的分支與其餘版本控制系統不一樣,不管建立,切換和刪除分支,Git在1秒鐘以內就能完成!不管你的版本庫是1個文件仍是1萬個文件。github
在版本回退裏,咱們已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git裏,這個分支叫主分支(master分支),HEAD嚴格來講不是指向提交,而是指向master,master纔是指向提交的,因此,HEAD指向的就是當前分支。安全
① 一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD執行maser,就能肯定當前分支,以及當前分支的提交點,每次提交,master分支都會向前一步:
② 當咱們建立新的分支,例如dev時,Git建立了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
能夠看到,Git建立一個分支很快,由於除了增長一個dev指針,改改HEAD的指向,工做區的文件都沒有任何變化。app
③ 不過,從如今開始,對工做區的修改和提交就是針對dev分支了,好比新提交一次後,dev指針往前移動一步,而master指針不變:分佈式
④ 假如咱們在dev上的工做完成了,就能夠把dev合併到master上。Git合併很簡單,就是把master指向dev的當前提交,就完成了合併:
因此Git合併分支也很快,就改改指針,工做區內容不變。ide
⑤ 合併完分支後,甚至能夠刪除dev分支。刪除dev分支就是把dev指針給刪掉,咱們就剩下了一條master分支:fetch
下面開始進行實踐:
首先,咱們建立dev分支,而後切換到dev分支:this
[jonson@localhost mygit]$ git checkout -b dev Switched to a new branch 'dev' #git checkout命令加上-b參數表示建立並切換,至關於如下兩條命令: $ git branch dev $ git checkout dev
而後,用git branch命令查看當前分支:3d
[jonson@localhost mygit]$ git branch * dev master #git branch命令會列出全部分支,當前分支前面會標一個*符號
而後,咱們就能夠在dev分支上正常提交,好比對test.txt文件作修改:版本控制
[jonson@localhost mygit]$ echo "create a new brach" >> text.txt [jonson@localhost mygit]$ cat text.txt # this is jonson first repo create a new brach 而後提交: [jonson@localhost mygit]$ git add text.txt [jonson@localhost mygit]$ git commit -m "branch test" [dev e2d7f2d] branch test 1 file changed, 1 insertion(+)
如今,dev分支的工做完成,咱們就能夠切換回master分支:
[jonson@localhost mygit]$ git checkout master Switched to branch 'master' [jonson@localhost mygit]$ git branch dev * master [jonson@localhost mygit]$ cat text.txt # this is jonson first repo
切換回master分支後,在查看剛剛修改的文件,剛纔添加的內容不見了。緣由是那個提交是在dev分支上,而master分支此刻的提交點並無變:
合併分支:
如今,咱們把dev分支的工做成果合併到master分支上:
[jonson@localhost mygit]$ git merge dev Updating 7646ab6..e2d7f2d Fast-forward text.txt | 1 + 1 file changed, 1 insertion(+) [jonson@localhost mygit]$ cat text.txt # this is jonson first repo create a new brach
git merge
命令用於合併指定分支到當前分支。合併後,再查看文件的內容,就能夠看到,和dev分支的最新提交是徹底同樣的。
注意:上面的Fast-forward
信息,Git告訴咱們,此次合併是「快進模式」,也就是直接把master執行dev的當前提交,因此合併速度很是快,固然,也不是每次合併都是Fast-forward
,後面會講其餘方式的合併。
#合併完成後,若是須要刪除dev分支,能夠執行如下命令:
[jonson@localhost mygit]$ git branch -d dev Deleted branch dev (was e2d7f2d). [jonson@localhost mygit]$ git branch * master #刪除後,查看branch,就只剩下master分支了。
小結
Git鼓勵大量使用分支:
查看分支:git branch 建立分支:git branch <name> 切換分支:git checkout <name> 建立+切換分支:git checkout -b <name> 合併某分支到當前分支:git merge <name> 刪除分支:git branch -d <name>
人生不如意之事十有八九,合併分支每每也不是一路順風的。
1)準備新的分支(feature1)
[jonson@localhost mygit]$ git checkout -b feature1 Switched to a new branch 'feature1'
修改test.txt文件並提交:
[jonson@localhost mygit]$ echo "create two branch" >> text.txt [jonson@localhost mygit]$ cat text.txt # this is jonson first repo create a new brach create two branch [jonson@localhost mygit]$ git add text.txt [jonson@localhost mygit]$ git commit -m "two branch" [feature1 78292e2] two branch 1 file changed, 1 insertion(+)
切換到master分支:
[jonson@localhost mygit]$ 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分支上也進行修改並提交:
[jonson@localhost mygit]$ echo "create a three branch" >> text.txt [jonson@localhost mygit]$ git add text.txt [jonson@localhost mygit]$ git commit -m "three branch" [master e27d699] three branch 1 file changed, 1 insertion(+)
如今,master
分支和feature1
分支各自都分別有新的提交,變成了這樣:
這種狀況下,Git沒法執行「快速合併」,只能試圖把各自的修改合併起來,但這種合併就可能會有衝突,咱們試試看:
[jonson@localhost mygit]$ git merge feature1 Auto-merging text.txt CONFLICT (content): Merge conflict in text.txt Automatic merge failed; fix conflicts and then commit the result.
果真衝突了,Git告訴咱們,test.txt文件存在衝突,必須手動解決衝突後再提交。git status
能夠告訴咱們衝突的文件:
[jonson@localhost mygit]$ git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits. # # Unmerged paths: # (use "git add/rm <file>..." as appropriate to mark resolution) # # both modified: readme.txt # no changes added to commit (use "git add" and/or "git commit -a")
咱們能夠直接查看test.txt的內容:
[jonson@localhost mygit]$ cat text.txt # this is jonson first repo create a new brach <<<<<<< HEAD create a three branch ======= create two branch >>>>>>> feature1
git用‘<<<<<<<’,’=======’, ‘>>>>>>>’標記出不一樣分支的內容,咱們修改以下後保存:
[jonson@localhost mygit]$ cat text.txt # this is jonson first repo create a new brach <<<<<<< HEAD create two branch ======= create two branch >>>>>>> feature1
再提交:
[jonson@localhost mygit]$ git commit -m "new branch" [master 966dd22] new branch
如今,master分支和feature1分支變成了下圖所示:
工做完成後,刪除feature1分支:
[jonson@localhost mygit]$ git branch -d feature1 Deleted branch feature1 (was 78292e2).
小結:
當Git沒法自動合併分支時,就必須解決衝突。解決衝突後,再提交,最後合併完成。
一般,合併分支時,若是可能,Git會用Fast forward
模式,但這種模式下,刪除分支後,會丟掉分支信息。若是要強制禁用Fast forward
模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就能夠看出分支信息。下面來進行實踐:
1)首先,仍然建立並切換dev分支:
[jonson@localhost mygit]$ git checkout -b dev Switched to a new branch 'dev' [jonson@localhost mygit]$ git branch * dev master
修改test.txt文件,並提交一個新的commit:
[jonson@localhost mygit]$ echo "create four branch" >> text.txt [jonson@localhost mygit]$ git add text.txt [jonson@localhost mygit]$ git commit -m "add merge" [dev e730d1b] add merge 1 file changed, 1 insertion(+)
如今咱們切換回master:
[jonson@localhost mygit]$ git checkout master Switched to branch 'master'
準備合併dev分支,請注意--no-ff
參數,表示禁用Fast forward
:
[jonson@localhost mygit]$ git merge --no-ff -m "merge with no-ff" dev Merge made by the 'recursive' strategy. text.txt | 1 + 1 file changed, 1 insertion(+)
由於本次合併要建立一個新的commit,因此加上-m參數,把commit描述寫進去。合併後,咱們用git log
看看分支歷史:
能夠看到,不使用Fast forward
模式,merge後就像這樣:
小結:
Git分支十分強大,在團隊開發中應該充分應用。
合併分支時,加上--no-ff
參數就能夠用普通模式合併,合併後的歷史有分支,能看出來曾經作過合併,而fast forward
合併就看不出來曾經作過合併。
軟件開發中,一定會有bug。有了bug就須要修復,在Git中,因爲分支是如此的強大,因此,每一個bug均可以經過一個新的臨時分支來修復,修復後,合併分支,而後將臨時分支刪除。
當你接到一個修復一個代碼101的bug的任務時,很天然的,你想建立一個臨時分支來修復它, 可是,當前正在dev上進行的工做尚未提交:
[jonson@localhost mygit]$ echo "create five branch" >> text.txt [jonson@localhost mygit]$ git status # On branch dev # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: text.txt # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # new-repo/ no changes added to commit (use "git add" and/or "git commit -a")
此時,問題出現了,工做只進行到一半,還無法提交,預計完成還需1天時間。可是,必須在兩個小時內修復該bug,怎麼辦?
幸虧,Git還提供了一個stash功能,能夠把當前工做現場「儲藏」起來,等之後恢復現場後繼續工做:
[jonson@localhost mygit]$ git stash Saved working directory and index state WIP on dev: e730d1b add merge HEAD is now at e730d1b add merge
如今,能夠查看工做區,能夠看到就是乾淨的了,所以能夠放心地建立分支來修復bug。
首先肯定要在哪一個分支上修復bug,假定須要在master分支上修復,就從master建立臨時分支(issue-101):
[jonson@localhost mygit]$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) [jonson@localhost mygit]$ git checkout -b issue-101 Switched to a new branch 'issue-101'
如今修復bug,就假如須要把「create four branch」改爲「create five branch」,而後提交:
[jonson@localhost mygit]$ sed -i 's/four/five/' text.txt [jonson@localhost mygit]$ git add text.txt [jonson@localhost mygit]$ git commit -m "fix bug 101" [issue-101 6484832] fix bug 101 1 file changed, 1 insertion(+), 1 deletion(-)
修復完成後,切換到master分支,並完成合並,最後再刪除這個臨時分支:
[jonson@localhost mygit]$ git checkout master [jonson@localhost mygit]$ git merge --no-ff -m "merged bug fix 101" issue-101 Merge made by the 'recursive' strategy. text.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) [jonson@localhost mygit]$ git branch -d issue-101 Deleted branch issue-101 (was 6484832).
這下問題解決了,原計劃兩個小時的bug修復只花了5分鐘!如今,又能夠接着回到dev分支幹活了:
[jonson@localhost mygit]$ git checkout dev Switched to branch 'dev' [jonson@localhost mygit]$ git status # On branch dev # Untracked files: # (use "git add <file>..." to include in what will be committed) # # new-repo/ nothing added to commit but untracked files present (use "git add" to track)
工做區此時是乾淨的,由於剛纔的工做現場咱們用git stash
儲藏起來了,能夠用git stash list
命令看看:
[jonson@localhost mygit]$ git stash list stash@{0}: WIP on dev: e730d1b add merge
因此如今咱們須要恢復一下,有兩種方式:
一種是用git stash apply
恢復,可是回覆後,stash內容並刪除,你還須要用git stash drop
來刪除。
另外一種是用git starh pop,恢復的同時把stash內容也刪了(因此通常採用這種方法):
再用git stash list 查看,就看不到stash內容了,而且也恢復到以前的工做狀態:
#當你屢次stash時,若是你要恢復指定的stash,用如下命令:git stash apply stash@{0}
小結:
修復bug時,咱們會經過建立新的bug分支進行修復,而後合併,最後刪除。
當手頭工做沒有完成時,先把工做現場git stash
儲藏起來,而後去修復bug,再git stash pop
,刪除stash內容,並恢復到工做現場。
當你從遠程庫克隆時,實際上Git自動把本地master分支和遠程的master分支對應起來了,而且,遠程倉庫的默認名稱是origin。
#要查看遠程庫的詳細信息,用`git remote -v`: [jonson@localhost mygit]$ git remote -v origin git@github.com:sqm-sys/jonson-repo.git (fetch) origin git@github.com:sqm-sys/jonson-repo.git (push)
上面顯示了能夠抓取和推送的origin的地址。若是沒有推送權限,就看不到push的地址。
推送分支
推送分支,就是把該分支上的全部本地提交推送到遠程庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠程庫對應的遠程分支上:
[jonson@localhost mygit]$ git push origin master 推送其餘分支: [jonson@localhost mygit]$ git push origin dev
可是,並非必定要把本地分支往遠程推送,那麼,哪些分支須要推送,哪些不須要呢?
抓取分支
多人協做時,你們都會往master和dev分支上推送各自的修改。
如今,模擬一個你的同事,能夠在另外一臺電腦(注意要把SSH key添加獲得GitHub)下克隆:
[zhangsan@localhost ~]$ git clone git@github.com:sqm-sys/jonson-repo.git Cloning into 'jonson-repo'... remote: Enumerating objects: 26, done. remote: Counting objects: 100% (26/26), done. remote: Compressing objects: 100% (12/12), done. remote: Total 26 (delta 5), reused 25 (delta 4), pack-reused 0 Receiving objects: 100% (26/26), done. Resolving deltas: 100% (5/5), done.
須要的注意的是,你的同事從遠程庫clone時,默認狀況下,只能看到本地的master分支,而看不到其餘分支:
[zhangsan@localhost ~]$ cd jonson-repo/ [zhangsan@localhost jonson-repo]$ git branch * master
如今,你的同事要在dev分支上開發,就必須建立遠程origin的dev分支到本地(能夠執行如下命令):
[zhangsan@localhost jonson-repo]$ git checkout -b dev origin/dev Branch dev set up to track remote branch dev from origin. Switched to a new branch 'dev'
此時,他就能夠在dev上分支上進行修改了,而後,時不時的把dev分支push到遠程:
[zhangsan@localhost jonson-repo]$ echo "create six branch" >> text.txt [zhangsan@localhost jonson-repo]$ git add text.txt [zhangsan@localhost jonson-repo]$ git commit -m "add six branch" [dev f50cefa] add six branch 1 file changed, 1 insertion(+) [zhangsan@localhost jonson-repo]$ git push origin dev Counting objects: 5, done. Delta compression using up to 2 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 287 bytes | 0 bytes/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), completed with 1 local object. To git@github.com:sqm-sys/jonson-repo.git c7d965a..f50cefa dev -> dev
你的同事已經向origin/dev分支推送了他的提交,而此時你也對一樣的文件做了修改,並試圖推送:
[jonson@localhost mygit]$ git branch * dev master [jonson@localhost mygit]$ echo "ha ha ha " >> text.txt [jonson@localhost mygit]$ git add text.txt [jonson@localhost mygit]$ git commit -m "this is haha" [dev 94c4cde] this is haha 1 file changed, 1 insertion(+) [jonson@localhost mygit]$ git push origin dev #進行推送 To git@github.com:sqm-sys/jonson-repo.git ! [rejected] dev -> dev (fetch first) error: failed to push some refs to 'git@github.com:sqm-sys/jonson-repo.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first merge the remote changes (e.g., hint: 'git pull') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
推送失敗,緣由是你的同事的最新提交和你試圖推送的提交有衝突,解決辦法也很簡單,Git已經提示咱們:先用git pull
把最新的提交從origin/dev
抓下來,而後,在本地合併,解決衝突,再推送:
能夠看到git pull
失敗了,緣由是沒有指定本地dev分支與遠程origin/dev分支的連接,根據提示,設置dev和origin/dev的連接:
[jonson@localhost mygit]$ git branch --set-upstream-to=origin/dev Branch dev set up to track remote branch dev from origin.
建立連接後,在執行pull:
[jonson@localhost mygit]$ git pull Auto-merging text.txt CONFLICT (content): Merge conflict in text.txt Automatic merge failed; fix conflicts and then commit the result.
這回git pull
成功,可是合併有衝突,須要手動解決,解決的方法以前的解決衝突徹底同樣(將衝突的內容進行修改)。解決後,提交,再push:
[jonson@localhost mygit]$ git add text.txt [jonson@localhost mygit]$ git commit -m "merge & six branch" [dev 4ba822d] merge & six branch [jonson@localhost mygit]$ git push origin dev Counting objects: 10, done. Delta compression using up to 2 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (6/6), 616 bytes | 0 bytes/s, done. Total 6 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), done. To git@github.com:sqm-sys/jonson-repo.git f50cefa..4ba822d dev -> dev
push成功,所以,多人協做的工做模式一般是這樣:
1)首先,能夠試圖用'git push origin <branch-name>' 推送本身的修改;
2)若是推送失敗,則由於遠程分支比你的本地更新,須要先用git pull
試圖合併;
3)若是合併衝突,則解決衝突,並在本地提交;
4) 沒有衝突或者解決掉衝突後,再用'git push origin <branch-name>' 推送就能成功;
若是git pull
提示「no tracking information」,則說明本地分支和遠程分支的連接關係沒有建立,用命令'git branch --set-upstream-to=origin/<branch-name> '。這就是多人協做的工做模式。。。