git
的人,只會用add
, commit
,push
,pull
這幾個命令,包括恢復版本之類的,多半也會暴力的刪除整個專案,再重新clone
乾淨的程式碼。雖說也能工作了,但是無疑沒有領會到git
的精華。
這篇文章主要說明如何使用分支使我們的開發工作更加順滑,如何讓分支成為你日常工作流不可缺失的一部分。
git branch 用法
git branch //列出所有的分支
git branch <branch> //建立名為<branch>的分支,但是不會切換過去
git branch -d <branch> //刪除指定分支,這是一個“安全”操作,git會阻止你刪除包含未合併更改的分支。
git branch -D <branch> //強制刪除分支
git branch -m <branch> //重新命名當前分支
複製程式碼
在日常開發中,無論是修復一個bug或者新增一個功能,我們都應該新建一個分支來封裝我們的修改。這樣可以保證我們不穩定的程式碼永遠不會提交到主程式碼分支上。
下面我們具體來看在執行分支有關的操作,分支的變化:
建立分支
分支只是指向提交的指標,理解這一點很重要,當你建立新分支,實際上只是建立了一個新的指標,倉庫本身不會受到影響,一開始你的倉庫只有一條分支:
然後你執行下面的命令建立一個分支,用於加一個新feature:git branch new-feature
複製程式碼
當然執行後,你只是建立了這個分支,還需要執行git chekcout new-feature
切換到new-feature
分支,然後再使用git add
,git commit
。
刪除分支
假如你已經開發完了new-feature,並且已經commit程式碼了, 你就可以自由刪除這個分支了。
git branch -d new-feature
複製程式碼
如果分支還沒有合入master,會報下面的錯誤:
error: The branch 'new feature' is not fully merged.
If you are sure you want to delete it, run 'git branch -D crazy-experiment'.
複製程式碼
這時候你可以合併分支(下面會說如何合併分支),如果你真的確定了要刪除分支,可以用-D
執行強制刪除:
git branch -D new-feature
複製程式碼
切換分支(git checkout)
git checkout
命令允許你切換到用git branch
建立的分支。切換分支會更新當前工作目錄中的檔案,還可以用git log
檢視當前分支的歷史。
用法
git checkout <existing-branch> //切換到一個已有分支上
git checkout -b <new-branch> // -b 標記 可以方便讓你先建立一個新的new-branch,再直接切換過去
git checkout -b <new-branch> <existing-branch> //在已有的分支上建立分支,原來的分支使新分支的基
複製程式碼
git branch
和git chekout
是一對好基友,你可以使用git checkout
在不同的功能分支或者bug分支之間切換,而不產生相互影響。
關於分離的HEAD(detached HEAD)
一般我們有時候需要恢復到以前commit的版本上檢視原來的一些檔案時,我們會git checkout commit的hash碼或者tag
, 這時候會提醒我們進入了detached HEAD
。
git checkout 2
,我們的程式碼回到2的狀態了,通常git會顯示下面的warning:
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at 2
複製程式碼
如果在這種狀態下開發,然後又add
,commit
,沒有分支可以讓你回到之前的狀態。當你不可避免的需要checkout
到另外一個分支,想再回來就是不可能的了,因為像圖中的那個X
狀態,根本就不在分支上,你將再也不能引用你之前新增的程式碼了。
重點:永遠記得在開發分支上開發,不要在分離的HEAD上開發,這可以確保你可以引用到你的新提交,如果只是checkout
到以前看看無所謂,如果真的需要在以前的版本上新增什麼程式碼,記得上面warning 中的git checkout -b new_branch_name
,是自己處於一個確切的分支中。
遠離detached HEAD
,我們來看實際上git分支流程是什麼樣子的:
git branch new-feature
git checkout new-feature
複製程式碼
接下來我們做一些程式碼改動,提交:
git add <some file>
git commit -m "A new feature"
##你接著改程式碼,接著提交很多次
複製程式碼
當你git log
會顯示每一次的commit, 和master分支完全獨立,當你checkout
到master分支去,再git log
,會發現new-feature
分支的提交都不在,這就是不影響master分支。
這時候,你可以考慮合併new-feature或者在master分支上開始別的工作。
合併(git merge)
合併是git
將被fork
的歷史放回到一起的方式。 git merge
命令允許你將 git branch
建立的多條分支合併成一個。
用法
git merge <branch> //將指定分支併入當前分支
git merge --no-ff <branch> //將指定分支併入當前分支,但 總是 生成一個合併提交(即使是快速向前合併)。這可以用來記錄倉庫中發生的所有合併。
複製程式碼
一旦在新分支上完成開發,我們需要把新分支的提交合併到主分支,git會根據目前分支之間的結構資訊,選擇不同的演算法來完成合並:
- 快速向前合併
- 三路合併
快速向前合併 當new-feature的分支與原有的master分支呈現線性關係時,執行快速向前合併,git將當前的HEAD指標快速移到目標分支的頂端,master分支也就具有了new-feature分支的歷史了,如圖:
來看一個快速向前合併的例項:
# 開始新功能
git checkout -b new-feature master
# 編輯檔案
git add <file>
git commit -m "開始新功能"
# 編輯檔案
git add <file>
git commit -m "完成功能"
# 合併new-feature分支
git checkout master
git merge new-feature
git branch -d new-feature
複製程式碼
對於合作開發的人少的專案,這是一種主要的工作流,合作開發的人多的話,主master
分支經常都會有新提交,如果你的new-feature
耗時比較久,再提交時,master
分支可能已經過去幾個版本了,這時候就需要下面的三路合併了。
三路合併 但是如果master分支在new-feature分離後,又有了新的提交,即開始分叉了,git只能執行三路合併,三路合併使用一個專門的提交來合併兩個分支的歷史。
所謂的三路也就是:兩個分支的頂端以及他們共同的祖先。 在執行三路合併後: 使用三路合併產生的合併提交作為兩個分支的連線標誌。解決衝突
如果兩個分支對同一個檔案的同一部分均有修改時,git將無法判斷應該使用哪個,這時候合併提交會停止,需要你手動解決這些衝突。你可以使用git status
來檢視哪裡存在衝突,很多時候我都會在目錄下執行grep -rn HEAD
來檢視哪些檔案裡有這個標記,有這個標記的地方都是有衝突的。
當修改完所有的衝突後,git add
所有的衝突檔案,執行git commit
生成一個合併提交,這和提交一個普通快照的流程相同。提交衝突只會存在在三路合併中,快速向前合併中不可能出現針對同一檔案同一部分的不一樣修改。
下面例項來看看三路合併是怎麼產生的:
# 開始新功能
git checkout -b new-feature master
# 編輯檔案
git add <file>
git commit -m "開始新功能"
# 編輯檔案
git add <file>
git commit -m "完成功能"
# 在master分支上開發
git checkout master
# 編輯檔案
git add <file>
git commit -m "在master上新增了一些極其穩定的功能"
# 合併new-feature分支
git merge new-feature
git branch -d new-feature
複製程式碼
這時候,merge會停止,因為無法將 master 直接移動到 new-feature。所以需要你手動合併衝突後再提交。
git merge
會產生合併提交,有的人會選擇使用git rebase
來合併以確保一個乾淨的提交歷史。關於這兩個的區別,我會另寫一篇介紹。