這篇文章將介紹Git分支. 首先, 看看如果建立分支, 這就像是request一個新的專案歷史. 接著, 來看看git checkout是如果能被用來選擇一個分支的. 最後, 學習用git merge來合併分支.
git branch
我們可以把分支看作是request一個全新的工作目錄, 快取區, 專案歷史的方法. 新的commit會在當前分支的歷史中被紀錄下來.
git branch命令可以用來建立, 檢視, 重新命名, 刪除分支.
用法
git branch
列出你倉儲的所有的分支.
git branch <branch>
建立一個新分支. 不會自動switch到這個新分支中.
git branch -d <branch>
刪除指定的分支. 這是一個安全的操作. 因為當你的分支沒有被merge時, Git會阻止你刪除這個分支.
git branch -D <branch>
強行刪除指定的分支, 不管他有沒有被merge.
git branch -m <branch>
修改你當前分支的名字為<branch>.
討論
在Git世界裡, branch是一個很通常的部分, 你可以在你開發過程中的每一天都使用他. 當你想加一個新特性或者修復一個bug-不管是大是小-你都可以開一個branch來封裝你這次修改. 這能保證你不會將不穩定的程式碼提交到主程式碼中去, 還可以在merge到主branch前清理你的feature歷史.
上圖有兩個分離的開發線, 一個是litte feature, 一個是longer-running feature. 你不止能同時開發這兩個feature branch, 你還可以繼續開發你的主分支, 並且保持你的主分支不受feature分支的影響.
小貼示
Git branch的實現比SVN的model更輕量級一些. 他不是把檔案從一個目錄拷貝到另外一個目錄, Git將branch做為一個到commit的引用儲存. 因此, branch代表一系列的commit--而不是一個commit的容器.
SVN的merge是基於檔案的, Git基於一個抽象的commit.
例子
建立branch
這對於理解branch是指向commit的非常重要. 當你建立一個branch, Git需要做的是隻是建立一個指標. 如果一開始你的倉儲如下:
然後, 你使用下面的命令建立一個branch:
git branch crazy-experiment
倉儲你的歷史依然沒有發生任何變化. 你所得到的僅僅是一個指向當前commit的指標:
注意你現在只是建立了一個branch, 還沒有switch到這個新的branch下面工作. 你需要使用git checkout來選在這個新的branch, 才能切換到這個新的branch下工作.
刪除branch
一旦你在一個branch中的工作完成了, 並且已經把他merge到了主branch, 你就可以刪除這個branch, 而且不用擔心丟失任何歷史了:
git branch -d crazy-experiment
如果這個branch沒有merge到主分支, 上面的命令會輸出如下錯誤資訊:
error: The branch 'crazy-experiment' is not fully merged.
If you are sure you want to delete it, run 'git branch -D crazy-experiment'.
這個保護了你在未merge的時候就刪除branch, 從而不小心丟失歷史. 如果你真的想刪除這個branch, 可以使用-D標記:
git branch -D crazy-experiment
用這個刪除branch不會有任何警告.
git checkout
git checkout命令能讓你導航到你想要的branch中去. checkout 一個分支, 更新工作目錄的檔案已匹配這個branch, 告訴git紀錄這個branch中的所有的新的commit. 可以將這個看成是選擇開發線(line of development)的一種方式.
在之前的文章, 我們講過了如何使用checkout來檢視老的commit. checkout branch是將你的工作目錄更新到你選擇的branch的狀態. 新的修改都會被儲存在專案歷史中--這不是一個只讀的操作.
用法
git checkout <existing-branch>
checkout指定的branch, 這個branch必須是已經存在了的. 這個命令會切換<existing-branch>為你的當前branch, 並更新你的工作目錄為這個branch的狀態.
git checkout -b <new-branch>
建立&&checkout到<new-branch>. -b標記告訴Git在git checkout <new-branch>之前先執行git branch <new-branch>
git checkout -b <new-branch> <existing-branch>
基於<existing-branch>來建立<new-branch>, 而不是基於當前branch來建立.
討論
git checkout和git branch是一對好基友. 當你想開發一個新特性的時候, 你通過git branch來建立一個branch, 然後使用git checkout切換到這個branch下. 你可以在一個倉儲中在多個branch下工作, 只要使用checkout切換到你想要的branch下就行了.
Detached HEADs
git checkout更新HEAD指向你指定的branch或commit. 當你checkout一個commit, 會轉為一個detached HEAD狀態.
detached狀態意味著你做的所有的狀態和專案發展的其它部分都是分離的. 在detached HEAD狀態中開發的東西不能被merge.
你應該在一個branch下開發--永遠不要在一個detached HEAD狀態中開發.
例子
下面的例子演示了一個branch的基本的開發流程. 當你開發一個新特性的時候, 你首先建立一個branch然後switch到這個branch下:
git branch new-feature
git checkout new-feature
然後, 你可以commit新快照了:
# Edit some files
git add <file>
git commit -m "Started work on a new feature"
# Repeat
new-feature裡面的紀錄、歷史都完全是和master隔離的. 當你想回到master的時候可以執行以下命令:
git checkout master
git merge
當前的branch會被更新,merge. 目標branch不會有任何影響. git merge經常是和git checkout一起配套使用的, git branch -d 用來刪除目標branch.
用法
git merge <branch>
merge<branch>到當前的branch. Git會自動決定merge的演算法.
git merge --on-ff <branch>
merge<branch>到當前的branch, 不同的是會生成一個merge commit(甚至他是一個fast-forward merge).
討論
一旦你在一個隔離的branch中開發的新特性完成了, 你將把他們merge到你的主branch中去. 基於你倉儲的結構, Git有幾種演算法: fast-forward merge或3-way merge.
fast-forward merge
3-way merge
解決衝突
如果你在merge的時候兩個branch同一檔案的同一行都有修改, Git無法分辨出那一個版本是你想要的. 當這種情況出現的時候, 你得手動解決衝突然後才能merge.
當你遇到了一個merge衝突的時候, 執行git status來顯示你需要處理哪些檔案. 例如, 如果兩個branch都修改了hello.py的同一處, 你將看到如下的資訊:
# On branch master
# Unmerged paths:
# (use "git add/rm ..." as appropriate to mark resolution)
#
# both modified: hello.py
#
現在你可以去hello.py檔案裡面選擇哪個版本是你想要的. 當你準備結束這個merge, 你需要使用git add這些衝突的檔案來告訴git衝突已經解決了. 然後, 你可以使用git commit生成一個merge commit.
注意了merge衝突只會出現在3-way merge這種情況. 在fast-forward merge中不可能會有衝突.
例子
fast-forward merge
# Start a new feature
git checkout -b new-feature master # Edit some files
git add <file>
git commit -m "Start a feature" # Edit some files
git add <file>
git commit -m "Finish a feature" # Merge in the new-feature branch
git checkout master
git merge new-feature
git branch -d new-feature
3-way merge
# Start a new feature
git checkout -b new-feature master # Edit some files
git add <file>
git commit -m "Start a feature" # Edit some files
git add <file>
git commit -m "Finish a feature" # Develop the master branch
git checkout master # Edit some files
git add <file>
git commit -m "Make some super-stable changes to master" # Merge in the new-feature branch
git merge new-feature
git branch -d new-feature