在版本回退裡已經知道,每次送出,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,隻有一條時間線,在Git裡,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向送出,而是指向master,master才是指向送出的,是以,HEAD指向的就是目前分支。
分支的作用很強大,假設你準備開發一個新功能,但是需要兩周才能完成,第一周寫了部分代碼,如果立刻送出,由于代碼還沒寫完,不完整的代碼庫會導緻别人不能幹活了。如果等代碼全部寫完再一次送出,又存在丢失每天進度的巨大風險。現在有了分支,你可以建立一個屬于你自己的分支,别人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上幹活,想送出就送出,直到開發完畢後,再一次性合并到原來的分支上,這樣,既安全又不影響别人工作。
分支建立與切換
Git 是怎麼建立新分支的呢? 很簡單,它隻是為你建立了一個可以移動的新的指針。一開始的時候,master分支是一條線,Git用master指向最新的送出,再用HEAD指向master,就能确定目前分支,以及目前分支的送出點。每次送出,master分支都會向前移動一步,這樣,随着你不斷送出,master分支的線也越來越長。首先,我們來建立dev分支并切換到dev分支上
$ git checkout -b dev
git checkout 指令加上 –b參數表示建立并切換,相當于如下2條指令:
$ git branch dev
$ git checkout dev
也就是說,建立分支的指令是git branch,這會在目前所在的送出對象上建立一個指針。而要切換到一個已存在的分支,你需要使用 git checkout 指令。請牢記:當你切換分支的時候,Git 會重置你的工作目錄,使其看起來像回到了你在那個分支上最後一次送出的樣子。 Git 會自動添加、删除、修改檔案以確定此時你的工作目錄和這個分支最後一次送出時的樣子一模一樣。
你可以簡單地使用 git log 指令檢視各個分支目前所指的對象。 提供這一功能的參數是 --decorate。
可以看到目前 “master” 和 “dev” 分支均指向校驗和以 9b1105c 開頭的送出對象。
檢視分支
然後,用git branch指令檢視目前分支(加上-a參數可以檢視遠端分支,遠端分支會用紅色表示出來(如果你開了顔色支援的話)):
$ git branch
git branch檢視分支,會列出所有的分支,目前分支前面會添加一個星号。然後,我們就可以在dev分支上正常送出,比如對test.txt做個修改,加上一行“777777”,然後送出:
現在,dev分支的工作完成,我們就可以切換回master分支:
切換回master分支後,再檢視一個test.txt檔案,剛才添加的内容不見了!因為那個送出是在dev分支上,而master分支此刻的送出點并沒有變:
現在我們可以把dev分支上的内容合并到分支master上了,可以在master分支上,使用如下指令 git merge dev :
$ git merge dev
git merge指令用于合并指定分支到目前分支。合并後,再檢視readme.txt的内容,就可以看到,和dev分支的最新送出是完全一樣的。
注意到上面的Fast-forward資訊,Git告訴我們,這次合并是“快進模式”,也就是直接把master指向dev的目前送出,是以合并速度非常快。
當然,也不是每次合并都能Fast-forward,我們後面會講其他方式的合并。
删除分支
合并完成後,就可以放心地删除dev分支了(在Git v1.7.0 之後,删除遠端分支可以使用git push origin --delete <branchName>,否則,可以使用這種文法,推送一個空分支到遠端分支,其實就相當于删除遠端分支:git push origin :<branchName>):
$ git branch -d dev
删除後,檢視branch,就隻剩下master分支了:
因為建立、合并和删除分支非常快,是以Git鼓勵你使用分支完成某個任務,合并後再删掉分支,這和直接在master分支上工作效果是一樣的,但過程更安全。
解決沖突
Git雖然很智能了,但畢竟不是人,在合并分支的時候難免會遇到各種問題,這些問題還是需要我們自己解決。現在我們再建一個分支dev1,
在test.txt添加一行内容“888888”,然後送出,如下所示:
現在切換到master分支上,也在test.txt最後一行添加内容,内容為"999999",如下所示:
Git還會自動提示我們目前master分支比遠端的master分支要超前1個送出,在master上送出:
現在,master分支和dev1分支各自都分别有新的送出。這種情況下,Git無法執行“快速合并”,隻能試圖把各自的修改合并起來,但這種合并就可能會有沖突:
Git告訴我們,test.txt檔案存在沖突,必須手動解決沖突後再送出。git status也可以告訴我們沖突的檔案:
可以直接檢視readme.txt的内容:
Git用<<<<<<<,=======,>>>>>>>标記出不同分支的内容,其中<<<HEAD是指主分支修改的内容,>>>>>dev1 是指dev1上修改的内容,打開test.txt檔案,修改下如下後儲存:
現在已經是我們想要的效果了,送出:
用帶參數的git log也可以看到分支的合并情況:
最後,删除dev1分支:
.
分支管理
通常合并分支時,Git會用“Fast forward”模式,但這種模式下,删除分支會丢掉分支資訊。如果強制禁用“Fast forward"模式,Git就會在merge時生成一個新的commit,這樣,從分支曆史上就可以看出分支資訊。現在我們來使用帶參數 –no-ff來禁用”Fast forward”模式。
首先,仍然建立并切換dev分支:
修改test.txt檔案,并送出一個新的commit:
現在切換回master,合并dev分支,使用“--no-ff”參數。因為本次合并要建立一個新的commit,是以加上-m參數,把commit描述寫進去。
合并後,我們用git log看看分支曆史:
分支政策:首先master主分支應該是非常穩定的,也就是用來釋出新版本,一般情況下不允許在上面幹活,幹活一般情況下在建立的dev分支上幹活,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本釋出時,再把dev分支合并到master上,在master上釋出1.0版本。你和你的小夥伴們每個人都在dev分支上幹活,每個人都有自己的分支,時不時地往dev分支上合并就可以了。
現在已經建立、合并、删除了一些分支,讓我們看看一些常用的分支管理工具。
git branch 指令不隻是可以建立與删除分支。 如果不加任何參數運作它,會得到目前所有分支的一個清單:
注意 master 分支前的 * 字元:它代表現在檢出的那一個分支(也就是說,目前 HEAD 指針所指向的分支)。 這意味着如果在這時候送出,master 分支将會随着新的工作向前移動。 如果需要檢視每一個分支的最後一次送出,可以運作 git branch -v 指令:
--merged 與 --no-merged 這兩個有用的選項可以過濾這個清單中已經合并或尚未合并到目前分支的分支。 如果要檢視哪些分支已經合并到目前分支,可以運作 git branch --merged:
因為之前已經合并了 dev 分支,是以現在看到它在清單中。 在這個清單中分支名字前沒有 * 号的分支通常可以使用 git branch -d 删除掉——你已經将它們的工作整合到了另一個分支,是以并不會失去任何東西。
檢視所有包含未合并工作的分支,可以運作 git branch --no-merged指令。
Bug分支
開發過程中,遇到bug是難免的,有了bug就需要修複,在Git中,分支是很強大的,每個bug都可以通過一個臨時分支來修複,修複完成後,合并分支,然後将臨時的分支删除掉。假設我在開發中接到一個404 bug,我們可以建立一個404分支來修複它,但是,目前的dev分支上的工作還沒有送出。不是我不想送出,而是工作沒有完成,我們還無法送出,怎麼辦呢?還好,Git還提供了一個stash功能,可以把目前工作現場 ”隐藏起來”,等以後恢複現場後繼續工作
現在,用git status檢視工作區,就是幹淨的(除非有沒有被Git管理的檔案),是以可以放心地建立分支來修複bug。首先确定要在哪個分支上修複bug,假定需要在master分支上修複,就從master建立臨時分支issue-404,現在修複bug,然後送出:
修複完成後,切換到master分支,并完成合并,最後删除issue-404分支:
現在,我們可以回到dev分支上了
工作區是幹淨的,那麼我們工作現場去哪裡呢?我們可以使用指令 git stash list來檢視下:
工作現場還在,Git把stash内容存在某個地方了,但是需要恢複一下,可以使用如下2個方法:
1. git stash apply恢複,恢複後,stash内容并不删除,你需要使用指令git stash drop來删除。
2. 另一種方式是使用git stash pop,恢複的同時把stash内容也删除了。
現在再用git stash list檢視,就看不到任何stash内容了
多人協作
當你從遠端倉庫克隆時,實際上Git自動把本地的master分支和遠端的master分支對應起來了,并且,遠端倉庫的預設名稱是origin。
要檢視遠端庫的資訊 使用 git remote,使用使用 git remote –v檢視遠端庫的詳細資訊
上面顯示了可以抓取和推送的origin的位址。如果沒有推送權限,就看不到push的位址。
推送分支
就是把該分支上的所有本地送出推送到遠端庫。推送時,要指定本地分支,使用指令git push origin master,這樣,Git就會把該分支推送到遠端庫對應的遠端分支上,如果要推送其他分支,比如dev,就改成git push origin dev
但并不是一定要把本地分支往遠端推送,那麼,哪些分支需要推送,哪些不需要呢?
master分支是主分支,是以要時刻與遠端同步;
dev分支是開發分支,團隊所有成員都需要在上面工作,是以也需要與遠端同步;
bug分支隻用于在本地修複bug,就沒必要推到遠端了,除非老闆要看看你每周到底修複了幾個bug;
抓取分支
多人協作時,大家都會往master分支上推送各自的修改。
首先我的mygit目錄的dev分支也要推送到遠端去,如下:
現在我們可以模拟另外一個同僚,可以在另一台電腦上(注意要把SSH key添加到github上)或者同一台電腦上另外一個目錄克隆,建立一個目錄名字叫mygit2,克隆遠端的庫到本地來
現在目錄下生成如下所示:
當你的小夥伴從遠端庫clone時,預設情況下,你的小夥伴隻能看到本地的master分支
現在我們的小夥伴要在dev分支上做開發,就必須把遠端的origin的dev分支克隆到本地來,于是可以使用指令建立本地dev分支:git checkout –b dev origin/dev
現在小夥伴們就可以在dev分支上做開發了,開發完成後把dev分支推送到遠端庫時。
小夥伴已經向origin/dev分支推送了他的送出,而碰巧你也對同樣的檔案作了修改,并試圖推送:
推送失敗,因為你的小夥伴的最新送出和你試圖推送的送出有沖突,解決辦法也很簡單,Git已經提示我們,先用git pull把最新的送出從origin/dev抓下來,然後,在本地合并,解決沖突,再推送:
git pull也失敗了,原因是沒有指定本地dev分支與遠端origin/dev分支的連結,根據提示,設定dev和origin/dev的連結:
這回git pull成功,但是合并有沖突,需要手動解決,解決的方法和分支管理中的 解決沖突完全一樣。解決後,送出,再push,先來看下test.txt内容
手動解決完了再送出,再push到遠端庫裡面去
是以,多人協作的工作模式通常是這樣:
首先,可以試圖用git push origin branch-name推送自己的修改;
如果推送失敗,則因為遠端分支比你的本地更早,需要先用git pull試圖合并;
如果合并有沖突,則解決沖突,并在本地送出;
沒有沖突或者解決掉沖突後,再用git push origin branch-name推送就能成功!
如果git pull提示“no tracking information”,則說明本地分支和遠端分支的連結關系沒有建立,用指令git branch --set-upstream branch-name origin/branch-name。
這就是多人協作的工作模式,一旦熟悉了,就非常簡單。