最近使用Git時(shí)感到有些生疏,所以利用晚上空余時(shí)間將廖雪峰Git教程重新系統(tǒng)的學(xué)習(xí)一邊并做了筆記,溫故知新纵苛。
Git簡介
Git是目前世界上最先進(jìn)的分布式版本控制系統(tǒng)。
集中式vs分布式
CVS和SVN是集中式的版本控制系統(tǒng)主经,Git是分布式版本控制系統(tǒng)。
集中式:
版本庫是集中存放在中央服務(wù)器的庭惜,在工作室都用自己的電腦罩驻,要先從中央服務(wù)器取得最新的版本,然后開始干活护赊,干完活再把自己的或推送到中央服務(wù)器惠遏。集中式版本控制系統(tǒng)最大毛病是必須聯(lián)網(wǎng)才能工作砾跃。
分布式:
分布式版本控制系統(tǒng)根本沒有“中央服務(wù)器”,每個(gè)人的電腦上都是一個(gè)完整的版本庫节吮,這樣你工作時(shí)就不須聯(lián)網(wǎng)了抽高,因?yàn)榘姹編炀驮谀汶娔X中。既然每個(gè)人電腦上都有完整的版本庫透绩,那多人何如協(xié)作呢翘骂?比方你在電腦修改了文件A,你同事也在他電腦中修改了文件A帚豪,這時(shí)你們只需把各自修改推送給對方就可以互相看到對方修改了碳竟。
相比集中式,分布式安全性要更高些狸臣。Git優(yōu)勢不單是不必聯(lián)網(wǎng)這么簡單莹桅,Git極其強(qiáng)大的分支管理,把SVN遠(yuǎn)遠(yuǎn)拋在后面烛亦。
版本庫版本管理
版本庫(repository)又名倉庫诈泼,可以簡單理解為一個(gè)目錄,這個(gè)目錄里面所有文件都可以被Git管理起來煤禽,每個(gè)文件的修改厂汗、刪除,Git都能跟蹤呜师,一遍任何時(shí)刻都能追蹤歷史,或者在將來某時(shí)刻可以還原贾节。
- git init
通過git init
命令可以吧當(dāng)前目標(biāo)變成Git可以管理的倉庫汁汗。瞬間Git就把倉庫建好并告訴你這是個(gè)空的倉庫,且目錄下多一個(gè).git目錄栗涂,這個(gè)目錄是Git來跟蹤管理版本庫的知牌,沒事不要手動(dòng)修改這個(gè)目錄的文件。
- git add 與git commit
在該倉庫目錄下先用命令git add readme.txt
告訴Git斤程,把文件添加到倉庫角寸。執(zhí)行上面命令,沒有任何顯示則表明成功忿墅。Unix的哲學(xué)就是“沒有消息就是好消息”扁藕。第二步,用命令git commit
告訴Git疚脐,把文件提交到倉庫亿柑。git commit -m "wrote a readme file"
,其中-m
后面輸入的是本提交的說明,可以輸入任意內(nèi)容棍弄,方便在歷史記錄中查找改動(dòng)望薄。git commit
命令成功后會告訴你疟游,1個(gè)文件被改動(dòng)(新添加的readme文件),插入了兩行內(nèi)容痕支。
為什么Git添加文件需要add
,commit
兩步呢?
因?yàn)?code>commit可以一次提交很多文件颁虐,所以你可以多次
add
不同的文件。
- git status
當(dāng)修改了文件后卧须,運(yùn)行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í)刻掌握倉庫當(dāng)前的狀態(tài),命令告訴我們r(jià)eadme被修改過但還沒準(zhǔn)備提交修改故慈。
- git diff
git diff可以查看文件具體修改了什么內(nèi)容板熊。輸入git diff readme.txt
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.
- git log
當(dāng)commit
了數(shù)次版本后,可以使用git log
命令顯示從最近到最遠(yuǎn)的提交日志察绷。如果覺得輸出信息太多干签,可以加上--pretty=oneline
參數(shù)。
- git reset
我們準(zhǔn)備把readme文件會退到上一個(gè)版本拆撼,首先Git必須知道當(dāng)前版本是哪個(gè)版本容劳,在Git中,用HEAD
表示當(dāng)前版本闸度,上個(gè)版本就是HEAD^
,上上個(gè)版本就是HEAD^^
竭贩,往上100個(gè)版本可寫成HEAD~100
。
輸入git reset -- hard HEAD^
即可回到上個(gè)版本莺禁,此時(shí)用git log
再看版本庫狀態(tài)發(fā)現(xiàn)之前版本消失了留量。如果想再回去,只要順著命令行串口向上找哟冬,找到那版本的commit id
然后再運(yùn)行``git reset -- hard 95d933`就又可以回到未來的那版本了楼熄。
Git版本回退速度非常快浩峡,因?yàn)镚it在內(nèi)部有個(gè)指向當(dāng)前版本的HEAD
指針可岂。HEAD
指向版本就是當(dāng)前版本,因此Git允許我們在版本歷史之間穿梭翰灾。穿梭前用git log
可以查看提交歷史缕粹,以便確定要退回哪個(gè)版本。要重返未來纸淮,用git reflog
查看命令歷史平斩,以便確定要會帶未來的哪個(gè)版本。
工作區(qū)和暫存區(qū)
工作區(qū)(Working Directory):就是你在電腦里能看到的目錄咽块。
版本庫(Repository):工作區(qū)有一個(gè)隱藏目錄.git双戳,這個(gè)不算工作區(qū),而是Git的版本庫。Git的版本庫里存了很多東西飒货,其中最重要的是stage(或叫index)的暫存區(qū)魄衅。還有Git為我們自動(dòng)創(chuàng)建了第一個(gè)分支master
,以及指向master
的一個(gè)指針叫HEAD
塘辅。
前面講吧文件往Git版本庫里添加時(shí)分兩步執(zhí)行:git add
晃虫,實(shí)際就是把文件修改添加到暫存區(qū)。git commit
扣墩,實(shí)際上是把暫存區(qū)的所有內(nèi)容提交到當(dāng)前分支哲银。因?yàn)槲覀儎?chuàng)建Git版本庫時(shí),Git自動(dòng)為我們創(chuàng)建了唯一一個(gè)master
分支,所以現(xiàn)在git commit
就是往這個(gè)分支上提交修改呻惕。
- git checkout
使用git checkout --file
可以把file文件在工作區(qū)的修改全部撤銷荆责,即讓這個(gè)文件回到最近一次git add
或git commit
時(shí)的狀態(tài)。這里必須在checkout
后寫明文件亚脆,否則就變成“切換到另一個(gè)分支”的命令了做院。
如果想撤銷掉已添加到暫存區(qū)的修改,用命令git reset HEAD file
可以把暫存區(qū)的修改撤銷掉濒持,重新放回工作區(qū)键耕。git reset
命令既可以回退版本,也可以把暫存區(qū)的修改回退到工作區(qū)柑营。然后再按上面步驟撤銷工作區(qū)屈雄。
- git rm
在Git中,刪除也是一個(gè)修改操作官套。一般情況下會在終端調(diào)用命令rm file
刪除文件酒奶,Git知道你刪除了文件,因此工作區(qū)和版本庫就不一致了奶赔。此時(shí)可以從版本庫中刪除這個(gè)文件惋嚎,使用git rm
刪掉并且git commit
,文件就從版本庫中刪除了纺阔。如果是刪錯(cuò)了,因?yàn)榘姹編熘羞€有修然,則使用git checkout --file
就可把文件恢復(fù)了笛钝。git checkout
其實(shí)是用版本庫里的版本替換工作區(qū)的版本,無論工作區(qū)是修改還是刪除愕宋,都可以還原玻靡。
遠(yuǎn)程倉庫
Git是分布式版本控制系統(tǒng),同一個(gè)Git倉庫可以分布到不同的機(jī)器上中贝。最早肯定是只有一臺機(jī)器有原始版本庫囤捻,伺候別的機(jī)器可以“克隆”這個(gè)原始版本庫,而且每臺機(jī)器的版本庫都一樣不分主次邻寿。實(shí)際情況常是找一臺電腦充當(dāng)服務(wù)器的角色蝎土,其他每個(gè)人都從這個(gè)“服務(wù)器”倉庫克隆一份到自己電腦上并且各自把各自的提交推送到服務(wù)器倉庫里视哑,也從服務(wù)器倉庫中拉取別人的提交。
GitHub就是提供Git倉庫托管服務(wù)的誊涯,只要注冊一個(gè)GitHub賬號就可以免費(fèi)獲得Git遠(yuǎn)程倉庫挡毅。假設(shè)我們從零開發(fā),最好的方式是先創(chuàng)建遠(yuǎn)程庫暴构,然后再從遠(yuǎn)程庫克隆跪呈。
使用命令git clone
克隆一個(gè)本地庫,如git clone git@github.com:orwater/cleargit.git
分支管理
在版本回退時(shí)我們已經(jīng)知道取逾,每次提交Git都辦它們穿成一條時(shí)間線耗绿,這條時(shí)間線就是分支。截至目前只有一條時(shí)間線砾隅,在Git里這個(gè)分支叫主分支误阻,即master
分支。HEAD
嚴(yán)格來說不是指向提交琉用,而是指向master
,master
才是指向提交的堕绩,所以,HEAD
指向的就是當(dāng)前分支邑时。
一開始時(shí)奴紧,master
分支是一條線,Git用master
指向最新的提交晶丘,再用HEAD
指向master
黍氮,就能確定當(dāng)前分支,以及當(dāng)前分支的提交點(diǎn)浅浮。每次提交沫浆,master
分支都會向前移動(dòng)一部,這樣隨著不斷提交滚秩,master
分支線也越來越長专执。
當(dāng)我們創(chuàng)建新的分支,例如dev
時(shí)郁油,Git新建一個(gè)指針叫dev
本股,指向master
相同的提交,再把HEAD
指向dev
桐腌,就表示當(dāng)前分支在dev
上了拄显。
從現(xiàn)在開始,對工作區(qū)的修改和提交就是針對dev
分支了案站,比如新的提交一次后躬审,dev
指針往前移動(dòng)一步,而master
指針不變。
假如我們在dev
上的工作完成了承边,就可以把dev
合并到master
上遭殉,最簡單的方法是直接把master
指向dev
的當(dāng)前提交完成合并。
合并完分支后炒刁,甚至可以刪除dev
分支恩沽。刪除dev
分支就是刪除dev
指針,最后就剩下一條主分支了翔始。
-
創(chuàng)建分支
首先我們創(chuàng)建dev
分支罗心,然后切換到上面,git checkout -b dev
,-b
參數(shù)表示創(chuàng)建并且切換城瞎。
然后可以用git branch
命令查看當(dāng)前分支渤闷。
可以使用命令git checkout master
切換回主分支。
如果要把dev
分支的工作結(jié)果合并到master
上脖镀,使用git merge
命令用于合并指定分支到當(dāng)前分支飒箭。git merge dev
合并完成后,就可以使用git branch -d dev
刪除分支了蜒灰。
-
解決沖突
創(chuàng)建個(gè)feature1
新分支弦蹂,修改readme文件并提交。然后切換到主分支再對readme文件做不同的修改并提交强窖。
這種情況下凸椿,Git無法執(zhí)行“快速合并”,只能試圖把各自的修改合并起來翅溺,但這種合并可能會有沖突脑漫。
$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
Git提示readme文件存在沖突,必須手動(dòng)解決沖突后再提交咙崎。這時(shí)我們查看readme文件:
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
Git用<<<<<<<
,=======
,>>>>>>>>
標(biāo)記出不同分支的內(nèi)容优幸。這樣進(jìn)行修改后再提交就可以了。
-
分支合并策略
通常褪猛,合并分支時(shí)网杆,如果可能,Git會用Fast forward
模式伊滋,但這種模式下刪除分支后會丟掉分支信息碳却。如果要強(qiáng)制禁用Fast forward
模式,Git就會在merge
時(shí)生成一個(gè)新的commit
,這樣從分支歷史上就可以看出分支信息了,知道曾經(jīng)做個(gè)合并新啼。
使用--no-ff
參數(shù)的git merge
表示禁用Fast forward
模式追城。因?yàn)楸敬魏喜⒁獎(jiǎng)?chuàng)建一個(gè)新的commit
刹碾,所以加上-m
參數(shù)燥撞。
$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 +
1 file changed, 1 insertion(+)
在實(shí)際開發(fā)中,我們應(yīng)該按照幾個(gè)基本原則進(jìn)行分支管理:
首先。master
分支應(yīng)該是非常穩(wěn)定的物舒,也就是僅用來發(fā)布新版本色洞,平時(shí)不能在上面干活。干活都在dev
分支上冠胯,其分支不穩(wěn)定火诸,到摸個(gè)時(shí)候再把分支合并到主分支上,在主分支發(fā)布版本荠察。團(tuán)隊(duì)成員都在dev
上干活置蜀,每個(gè)人都有自己的分支,是不是往dev
分支上合并就可以了悉盆。
- git stash
軟件開發(fā)中盯荤,修復(fù)bug時(shí),在Git中由于分支是如此強(qiáng)大焕盟,所以每個(gè)bug都可以通過一個(gè)新的臨時(shí)分支來修復(fù)秋秤,修復(fù)后合并分支再將臨時(shí)分支刪除。
如果正在工作時(shí)要修復(fù)一個(gè)bug脚翘,就要先創(chuàng)建一個(gè)臨時(shí)分支來修復(fù)灼卢。但手頭工作還沒完成不能提交。這時(shí)應(yīng)使用Git提供的stash
功能来农,可以把當(dāng)前工作現(xiàn)場存儲起來鞋真,等以后恢復(fù)現(xiàn)場后繼續(xù)工作。
$ git stash
Saved working directory and index state WIP on dev: 6224937 add merge
HEAD is now at 6224937 add merge
現(xiàn)在再用git status
查看工作區(qū)就是干凈的了备图。
當(dāng)修復(fù)好bug后再切換回dev
分支要恢復(fù)之前內(nèi)容灿巧,有兩個(gè)方法:
一種是用git stash apply
恢復(fù),但恢復(fù)后揽涮,stash
內(nèi)容并不刪除抠藕,須再調(diào)用git stash drop
來刪除。
另一種方法是用git stash pop
蒋困,恢復(fù)的同事把stash
內(nèi)容也刪除盾似。再用git stash list
查看就看不到任何stash
內(nèi)容了。你可以多次stash
雪标,恢復(fù)時(shí)先用git stash list
查看零院,然后再恢復(fù)指定的stash
,git stash apply stash@{0}
村刨。
-
多人協(xié)作
當(dāng)你從遠(yuǎn)程倉庫克隆時(shí)告抄,實(shí)際上Git自動(dòng)把本地的master
分支和遠(yuǎn)程的master
分支對應(yīng)起來了,并且遠(yuǎn)程倉庫默認(rèn)名稱是origin
嵌牺。要查看遠(yuǎn)程庫的信息打洼,用git remote
龄糊,添加-v
參數(shù)顯示更詳細(xì)信息。
$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)
上面顯示了可以抓取和推送的origin
地址募疮,如果沒有推送權(quán)限就看不到push地址炫惩。
- git push
推送分支,就是把該分支上的所有本地提交推送到遠(yuǎn)程庫阿浓。推送時(shí)他嚷,要制定本地分支,Git就會把該分支推送到遠(yuǎn)程庫對應(yīng)的遠(yuǎn)程分支上芭毙。git push origin master
筋蓖。
但是并不是一定要把本地分支往遠(yuǎn)程推送,那么哪些分支需要推送退敦?
-
master
分支是主分支扭勉,因此要時(shí)刻與遠(yuǎn)程同步。 -
dev
分支是開發(fā)分支苛聘,團(tuán)隊(duì)所有成員都需要在上面工作涂炎,所以要與遠(yuǎn)程同步。 - bug分支只用于在本地修復(fù)bug,就沒必要推到遠(yuǎn)程了设哗。
總之Git中分支可以再本地自己藏著玩唱捣,是否推送視情況而定。
多人協(xié)作的工作模式通常是這樣:
- 首先网梢,可以試圖用
git push origin branch -name
推送自己的修改 - 如果推送失敗震缭,則因?yàn)檫h(yuǎn)程分支比你本地更新,需要先用
git pull
試圖合并 - 如果合并有沖突战虏,則解決沖突拣宰,并在本地提交;
- 沒有沖突或解決沖突后烦感,再用
git push origin branch -name
推送即可巡社。
如果git pull
提示“no tracking information”,則說明本地分支和遠(yuǎn)程分支的鏈接關(guān)系沒有創(chuàng)建,用命令git branch --set-upstream branch -name origin/branch -name
手趣。
標(biāo)簽管理
在發(fā)布一個(gè)版本時(shí)晌该,通常先在版本庫中打一個(gè)標(biāo)簽(tag),這樣就唯一確定了打標(biāo)簽時(shí)刻的版本绿渣。將來無論什么時(shí)候朝群,取某個(gè)標(biāo)簽的版本就是把那個(gè)打標(biāo)簽時(shí)刻的歷史版本取出來。所以中符,標(biāo)簽也是版本庫的一個(gè)快照姜胖。
Git的標(biāo)簽歲談事版本庫的快照,但其實(shí)他就是指向某個(gè)commit的指針淀散,所以創(chuàng)建和刪除標(biāo)簽都是瞬間完成的右莱。相比于難記的commit id
堵第,tag是一個(gè)讓人容易記住的名字,它跟某個(gè)commit綁定在一起隧出。
- git tag
使用命令git tag<name>
可以打一個(gè)新標(biāo)簽,使用命令git tag
可查看所有標(biāo)簽阀捅。默認(rèn)標(biāo)簽是打在最新提交的commit上的胀瞪,如果想之前的commit打標(biāo)簽可以先找到是歷史提交的commit id
,然后打命令git tag v0.9 6224937
即可饲鄙。
最后附一張Git Cheat Sheet
Git分支最佳實(shí)踐
- 主要分支:
中央倉庫有兩個(gè)長期的分支:
- master
- develop
master
用作生產(chǎn)分支凄诞,里面的代碼是準(zhǔn)備部署到生產(chǎn)環(huán)境的。develop
是可交付的開發(fā)代碼忍级,也可以看作用于集成分支帆谍,每晚構(gòu)建從`develop獲取代碼。當(dāng)
develop分支中的代碼足夠穩(wěn)定時(shí)轴咱,就將改動(dòng)合并到
master``分支汛蝙,同時(shí)打上一個(gè)標(biāo)簽,標(biāo)簽名稱為發(fā)布的版本號朴肺。
- 輔助分支
通過輔助分支來幫助并行開發(fā)窖剑,和主要分支不同,這些分支的生命周期是有限的:
- 特性分支
- 發(fā)布分支
- 緊急修復(fù)分支
特性分支:
特性分支可能從develop
分支分出戈稿,最終必須合并回develop
西土。特性分支用于開發(fā)新特性。每個(gè)新特性開一個(gè)新分支鞍盗,最終會合并會develop
需了。特性分支只存在于開發(fā)者的倉庫中。
創(chuàng)建新的特性分支:
git checkout -b myfeature develop
合并回develop
:
git checkout develop
git merge --no-ff myfeature
git branch -d myfeature
git push origin develop
使用--no-ff
確卑慵祝總是新生成一個(gè)提交肋乍,避免丟失曾經(jīng)存在一個(gè)特性分支的歷史信息,也能方便地看出那些提交屬于同一個(gè)特性敷存。
發(fā)布分支:
發(fā)布分支可能從develop
分出住拭,最終必須合并回develop
和master
。發(fā)布分支以release-*
的方式命名历帚。
發(fā)布分支為新的發(fā)部分版本做準(zhǔn)備滔岳,包括一些小bug修正和發(fā)布的元信息(版本號、發(fā)布日期等)的添加挽牢。這樣develop
分支就可以接受針對以后的發(fā)布的新特征谱煤。
在代碼基本可以發(fā)布的時(shí)候從develop
分支分出發(fā)布分支。這是要確保此次發(fā)布包括的特性都已經(jīng)合并到develop
分支了禽拔。
創(chuàng)建發(fā)布分支:
git checkout -b release-1.2 develop
./bump-version.sh 1.2
git commit -a -m "Bumped version number to 1.2"
完成發(fā)布分支:
git checkout master
git merge --no-ff release-1.2
git tag -a 1.2
git checkout develop
git merge --no-ff release-1.2
緊急修復(fù)分支:
可能從master
分出刘离,必須合并回develop
和master
室叉。分支名以hotfix-*
開頭。如果生產(chǎn)系統(tǒng)里面有個(gè)緊急bug要馬上修復(fù)的話硫惕,我們就從master
里分出一個(gè)緊急修復(fù)分支茧痕。這樣某人修復(fù)緊急bug的同時(shí),團(tuán)隊(duì)其他成員可繼續(xù)在develop
上開發(fā)恼除。修復(fù)完bug之后踪旷,需要合并回master
,同時(shí)也需要合并回develop
豁辉。
參考文章 git分支最佳實(shí)踐;