Git分支管理策略
一舵抹、主分支Master
首先肪虎,代碼庫應(yīng)該有一個(gè)、且僅有一個(gè)主分支惧蛹。所有提供給用戶使用的正式版本扇救,都在這個(gè)主分支上發(fā)布。
Git主分支的名字香嗓,默認(rèn)叫做Master迅腔。它是自動(dòng)建立的,版本庫初始化以后靠娱,默認(rèn)就是在主分支在進(jìn)行開發(fā)沧烈。
二、開發(fā)分支Develop
主分支只用來分布重大版本像云,日常開發(fā)應(yīng)該在另一條分支上完成锌雀。我們把開發(fā)用的分支,叫做Develop迅诬。
這個(gè)分支可以用來生成代碼的最新隔夜版本(nightly)腋逆。如果想正式對(duì)外發(fā)布,就在Master分支上侈贷,對(duì)Develop分支進(jìn)行"合并"(merge)闲礼。
Git創(chuàng)建Develop分支的命令:
git checkout -b develop master
將Develop分支發(fā)布到Master分支的命令:
切換到Master分支
git checkout master
# 對(duì)Develop分支進(jìn)行合并
git merge --no-ff develop
這里稍微解釋一下,上一條命令的--no-ff參數(shù)是什么意思铐维。默認(rèn)情況下柬泽,Git執(zhí)行"快進(jìn)式合并"(fast-farward merge),會(huì)直接將Master分支指向Develop分支嫁蛇。
使用--no-ff參數(shù)后锨并,會(huì)執(zhí)行正常合并,在Master分支上生成一個(gè)新節(jié)點(diǎn)睬棚。為了保證版本演進(jìn)的清晰第煮,我們希望采用這種做法解幼。關(guān)于合并的更多解釋,請(qǐng)參考Benjamin Sandofsky的《Understanding the Git Workflow》包警。
三撵摆、臨時(shí)性分支
前面講到版本庫的兩條主要分支:Master和Develop。前者用于正式發(fā)布害晦,后者用于日常開發(fā)特铝。其實(shí),常設(shè)分支只需要這兩條就夠了壹瘟,不需要其他了鲫剿。
但是,除了常設(shè)分支以外稻轨,還有一些臨時(shí)性分支灵莲,用于應(yīng)對(duì)一些特定目的的版本開發(fā)。臨時(shí)性分支主要有三種:
- 功能(feature)分支
* 預(yù)發(fā)布(release)分支
* 修補(bǔ)bug(fixbug)分支
這三種分支都屬于臨時(shí)性需要殴俱,使用完以后政冻,應(yīng)該刪除,使得代碼庫的常設(shè)分支始終只有Master和Develop线欲。
四明场、 功能分支
接下來,一個(gè)個(gè)來看這三種"臨時(shí)性分支"询筏。
第一種是功能分支榕堰,它是為了開發(fā)某種特定功能竖慧,從Develop分支上面分出來的嫌套。開發(fā)完成后,要再并入Develop圾旨。
功能分支的名字踱讨,可以采用feature-*的形式命名。
創(chuàng)建一個(gè)功能分支:
git checkout -b feature-x develop
開發(fā)完成后砍的,將功能分支合并到develop分支:
git checkout develop
git merge --no-ff feature-x
刪除feature分支:
git branch -d feature-x
五痹筛、預(yù)發(fā)布分支
第二種是預(yù)發(fā)布分支,它是指發(fā)布正式版本之前(即合并到Master分支之前)廓鞠,我們可能需要有一個(gè)預(yù)發(fā)布的版本進(jìn)行測(cè)試帚稠。
預(yù)發(fā)布分支是從Develop分支上面分出來的,預(yù)發(fā)布結(jié)束以后床佳,必須合并進(jìn)Develop和Master分支滋早。它的命名,可以采用release-*的形式砌们。
創(chuàng)建一個(gè)預(yù)發(fā)布分支:
git checkout -b release-1.2 develop
確認(rèn)沒有問題后杆麸,合并到master分支:
git checkout master
git merge --no-ff release-1.2
# 對(duì)合并生成的新節(jié)點(diǎn)搁进,做一個(gè)標(biāo)簽
git tag -a 1.2
再合并到develop分支:
git checkout develop
git merge --no-ff release-1.2
最后,刪除預(yù)發(fā)布分支:
git branch -d release-1.2
六昔头、修補(bǔ)bug分支
最后一種是修補(bǔ)bug分支饼问。軟件正式發(fā)布以后,難免會(huì)出現(xiàn)bug揭斧。這時(shí)就需要?jiǎng)?chuàng)建一個(gè)分支莱革,進(jìn)行bug修補(bǔ)。
修補(bǔ)bug分支是從Master分支上面分出來的未蝌。修補(bǔ)結(jié)束以后驮吱,再合并進(jìn)Master和Develop分支。它的命名萧吠,可以采用fixbug-*的形式左冬。
創(chuàng)建一個(gè)修補(bǔ)bug分支:
git checkout -b fixbug-0.1 master
修補(bǔ)結(jié)束后,合并到master分支:
git checkout master
git merge --no-ff fixbug-0.1
git tag -a 0.1.1
再合并到develop分支:
git checkout develop
git merge --no-ff fixbug-0.1
最后纸型,刪除"修補(bǔ)bug分支":
git branch -d fixbug-0.1
版本回退-撤銷文件修改
{針對(duì)文件修改恢復(fù)}
工作區(qū)修改一個(gè)文件后拇砰,又想回到修改前(git add前)
1. 當(dāng)然可以直接手動(dòng)再在工作區(qū)中將文件修改回去
2. 修改后,通過命令git status查看
$ git status# On branch master# 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: readme.txt#no changes added to commit (use "git add" and/or "git commit -a")
這時(shí)Git會(huì)告訴你狰腌,git checkout -- file可以丟棄工作區(qū)的修改:
$ git checkout -- readme.txt
Note:
- git checkout -- file命令中的--很重要除破,沒有--,就變成了“切換到另一個(gè)分支”的命令琼腔,我們?cè)诤竺娴姆种Ч芾碇袝?huì)再次遇到git checkout命令瑰枫。
2. 命令git checkout -- readme.txt意思就是,把readme.txt文件在工作區(qū)的修改全部撤銷丹莲,這里有兩種情況:
一種是readme.txt自修改后還沒有被放到暫存區(qū)光坝,現(xiàn)在,撤銷修改就回到和版本庫一模一樣的狀態(tài)甥材;一種是readme.txt已經(jīng)添加到暫存區(qū)后盯另,又作了修改,現(xiàn)在洲赵,撤銷修改就回到添加到暫存區(qū)后的狀態(tài)鸳惯。總之叠萍,就是讓這個(gè)文件回到最近一次git commit或git add時(shí)的狀態(tài)芝发。
- 工作區(qū)、暫存區(qū)的概念不清楚的可見于Git版本控制教程 - Git本地倉庫
如果在工作區(qū)中修改了文件還git add到暫存區(qū)(但是在commit之前)
用git status查看一下苛谷,修改只是添加到了暫存區(qū)辅鲸,還沒有提交:
$ git status# On branch master# Changes to be committed:# (use "git reset HEAD <file>..." to unstage)## modified: readme.txt#
Git同樣告訴我們,用命令git reset HEAD file可以把暫存區(qū)的修改撤銷掉(unstage)抄腔,重新放回工作區(qū):
$ git reset HEAD readme.txtUnstaged changes after reset:M readme.txt
git reset命令既可以回退版本瓢湃,也可以把暫存區(qū)的修改回退到工作區(qū)理张。當(dāng)我們用HEAD時(shí),表示最新的版本绵患。
再用git status查看一下雾叭,現(xiàn)在暫存區(qū)是干凈的,工作區(qū)有修改落蝙。
然后丟棄工作區(qū)的修改
$ git checkout -- readme.txt $ git status# On branch masternothing to commit (working directory clean)
不但修改了文件還從暫存區(qū)提交commit到了版本庫 - 版本回退
版本回退可以回退到上一個(gè)版本织狐。不過,這是有條件的筏勒,就是你還沒有把自己的本地版本庫推送到遠(yuǎn)程移迫。Git是分布式版本控制系統(tǒng)。
在工作中對(duì)某個(gè)文件(如readme.txt)進(jìn)行多次修改交commit管行。
可以通過版本控制系統(tǒng)命令告訴我們提交的歷史記錄厨埋,在Git中,我們用git log命令查看:
$ git logcommit 3628164fb26d48395383f8f31179f24e0882e1e0Author: Michael Liao <askxuefeng@gmail.com>Date: Tue Aug 20 15:11:49 2013 +0800 append GPL commit ea34578d5496d7dd233c827ed32a8cd576c5ee85Author: Michael Liao <askxuefeng@gmail.com>Date: Tue Aug 20 14:53:12 2013 +0800 add distributed commit cb926e7ea50ad11b8f9e909c05226233bf755030Author: Michael Liao <askxuefeng@gmail.com>Date: Mon Aug 19 17:51:55 2013 +0800 wrote a readme file
Note:
- git log命令顯示從最近到最遠(yuǎn)的提交日志捐顷,我們可以看到3次提交荡陷,最近的一次是append GPL,上一次是add distributed迅涮,最早的一次是wrote a readme file废赞。
2. 如果嫌輸出信息太多,看得眼花繚亂的叮姑,可以試試加上--pretty=oneline參數(shù):
$ git log --pretty=oneline3628164fb26d48395383f8f31179f24e0882e1e0 append GPLea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributedcb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file
你看到的一大串類似3628164...882e1e0的是commit id(版本號(hào))唉地,和SVN不一樣,Git的commit id不是1传透,2耘沼,3……遞增的數(shù)字,而是一個(gè)SHA1計(jì)算出來的一個(gè)非常大的數(shù)字旷祸,用十六進(jìn)制表示耕拷,而且你看到的commit id和我的肯定不一樣讼昆,以你自己的為準(zhǔn)托享。為什么commit id需要用這么一大串?dāng)?shù)字表示呢?因?yàn)镚it是分布式的版本控制系統(tǒng)浸赫,后面我們還要研究多人在同一個(gè)版本庫里工作闰围,如果大家都用1,2既峡,3……作為版本號(hào)羡榴,那肯定就沖突了。
每提交一個(gè)新版本运敢,實(shí)際上Git就會(huì)把它們自動(dòng)串成一條時(shí)間線校仑。如果使用可視化工具(如GitX忠售、github的客戶端、pycharm)查看Git歷史迄沫,就可以更清楚地看到提交歷史的時(shí)間線稻扬。
現(xiàn)在我們想要把readme.txt回退到上一個(gè)版本
如“add distributed”的那個(gè)版本,怎么做呢羊瘩?首先泰佳,Git必須知道當(dāng)前版本是哪個(gè)版本,在Git中尘吗,用HEAD表示當(dāng)前版本腺毫,也就是最新的提交3628164...882e1e0(注意我的提交ID和你的肯定不一樣)歉提,上一個(gè)版本就是HEAD,上上一個(gè)版本就是HEAD,當(dāng)然往上100個(gè)版本寫100個(gè)比較容易數(shù)不過來淤击,所以寫成HEAD~100。
現(xiàn)在估盘,我們要把當(dāng)前版本“append GPL”回退到上一個(gè)版本“add distributed”喷市,就可以使用git reset命令:
$ git reset --hard HEAD^HEAD is now at ea34578 add distributed
這時(shí)readme.txt的內(nèi)容就成了版本add distributed
我們用git log再看看現(xiàn)在版本庫的狀態(tài):
$ git logcommit ea34578d5496d7dd233c827ed32a8cd576c5ee85Author: Michael Liao <askxuefeng@gmail.com>Date: Tue Aug 20 14:53:12 2013 +0800 add distributed commit cb926e7ea50ad11b8f9e909c05226233bf755030Author: Michael Liao <askxuefeng@gmail.com>Date: Mon Aug 19 17:51:55 2013 +0800 wrote a readme file
最新的那個(gè)版本append GPL已經(jīng)看不到了!
恢復(fù)文件后酗宋,要是我們又想回到修改后的文件呢积仗?(命令行窗口還沒有被關(guān)掉)
{這個(gè)是git reset --hard后,又反悔了蜕猫,想回到修改后的狀態(tài)}
只要上面的命令行窗口還沒有被關(guān)掉寂曹,你就可以順著往上找啊找啊,找到那個(gè)append GPL的commit id是3628164...回右,于是就可以指定回到未來的某個(gè)版本:
$ git reset --hard 3628164HEAD is now at 3628164 append GPL
版本號(hào)沒必要寫全隆圆,前幾位就可以了,Git會(huì)自動(dòng)去找翔烁。
Git的版本回退速度非趁煅酰快,因?yàn)镚it在內(nèi)部有個(gè)指向當(dāng)前版本的HEAD指針蹬屹,當(dāng)你回退版本的時(shí)候侣背,Git僅僅是把HEAD從指向append GPL:
改為指向add distributed:
然后順便把工作區(qū)的文件更新了。所以你讓HEAD指向哪個(gè)版本號(hào)慨默,你就把當(dāng)前版本定位在哪贩耐。
恢復(fù)文件后,要是我們又想回到修改后的文件呢厦取?(命令行窗口早就關(guān)掉了)
{這個(gè)是git reset --hard后潮太,又反悔了,想回到修改后的狀態(tài)}
想恢復(fù)到新版本怎么辦?找不到新版本的commit id怎么辦铡买?當(dāng)你用$ git reset --hard HEAD^回退到add distributed版本時(shí)更鲁,再想恢復(fù)到append GPL,就必須找到append GPL的commit id奇钞。
Git提供了一個(gè)命令git reflog用來記錄你的每一次命令:[Git高級(jí)教程:git log與git reflog]
$ git reflogea34578 HEAD@{0}: reset: moving to HEAD^3628164 HEAD@{1}: commit: append GPLea34578 HEAD@{2}: commit: add distributedcb926e7 HEAD@{3}: commit (initial): wrote a readme file
第二行顯示append GPL的commit id是3628164岁经,現(xiàn)在,你又可以乘坐時(shí)光機(jī)回到未來了蛇券。