1. 創(chuàng)建與合并分支
在Git里每次提交會(huì)被串成一條時(shí)間線,這條時(shí)間線就是一個(gè)分支.而HEAD
是指向當(dāng)前分支,當(dāng)前分支在指向最新一次提交;
例如:一開(kāi)始的時(shí)候,master
分支是一條線,Git用master
指向最新提交,在用HEAD
指向master
分支,就能確定當(dāng)前分支,以及當(dāng)前分支的提交點(diǎn);
當(dāng)我們創(chuàng)建新的分支,例如dev
時(shí),Git新建了一個(gè)指針叫dev
,指向master
當(dāng)前相同的提交,再把HEAD
指向dev
,就表示當(dāng)前分支在dev
上;
這時(shí),我們可以發(fā)現(xiàn).Git創(chuàng)建一個(gè)分支很快,因?yàn)槌嗽黾恿艘粋€(gè)
dev
指針,改改HEAD
的指向,工作區(qū)的文件都沒(méi)有任何變化!不過(guò),從現(xiàn)在開(kāi)始,對(duì)工作區(qū)的修改就是針對(duì)
dev
分支的了,比如再次進(jìn)行提交后,dev
指針就會(huì)繼續(xù)向前移動(dòng)指向最新一次的提交.而master
指針不變;
接下來(lái)我們看看合并操作,假如我們?cè)?code>dev上的工作完成了,就可以把
dev
合并到master
上,Git怎么進(jìn)行合并呢?最簡(jiǎn)單的方法,就是直接把master
指向dev
的當(dāng)前提交,就完成了合并:
所以Git合并分支也很快!就改變了指針指向,工作區(qū)內(nèi)容也不變!
合并完分支后,也可以刪除分支,刪除
dev
分支想必你已經(jīng)可以想到,對(duì)的,就是把dev
指針給刪掉,刪掉后我們就剩下了一條master
分支:
接下來(lái)我們開(kāi)始進(jìn)行操練:
$ git checkout -b dev
Switched to a new branch 'dev'
git checkout
命令加上-b
參數(shù)表示創(chuàng)建并切換,相當(dāng)于下面兩條命令:
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
然后使用git branch
命令查看當(dāng)前分支:
$ git branch
* dev
master
git branch
命令會(huì)列出所有分支,當(dāng)前分支前面會(huì)標(biāo)一個(gè)*
號(hào).
然后,我們可以在dev
分支上正常提交:
$ git commit -m "忽略不要的文件"
[dev fae4e76] 忽略不要的文件
2 files changed, 12 insertions(+), 4 deletions(-)
create mode 100644 .gitignore
現(xiàn)在,dev
分支的工作完成,我們可以切換回master
分支:
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
切換回master
分支后,發(fā)現(xiàn)剛才修改的內(nèi)容不見(jiàn)了!那是因?yàn)槟莻€(gè)提交是在dev
分支上,而master
分支此刻的提交點(diǎn)并沒(méi)有變:
現(xiàn)在我們把
dev
分支上的工作成果合并到master
上:
$ git merge dev
Updating e151f7e..fae4e76
Fast-forward
.gitignore | 8 ++++++++
.idea/workspace.xml | 8 ++++----
2 files changed, 12 insertions(+), 4 deletions(-)
create mode 100644 .gitignore
git merge
命令用于合并指定分支到當(dāng)前分支.合并后在查看剛才修改的內(nèi)容,就可以看到,和dev
分支最新提交的內(nèi)容是完全一樣的.
注意到上面的Fast-forward
信息,Git告訴我們,這次合并是"快進(jìn)模式",也就是直接把master
指向dev
當(dāng)前的提交,所以合并速度非车涛玻快.
當(dāng)然,也有意外發(fā)生,不是每次合并都能Fast-forward
的;
合并完成后,就可以放心地刪除dev
分支了:
$ git branch -d dev
Deleted branch dev (was fae4e76).
刪除后,查看branch
,就只剩下master
分支了:
$ git branch
* master
小結(jié)
查看分支:git branch
創(chuàng)建分支:git branch <name>
切換分支:git checkout <name>
創(chuàng)建+切換分支:git checkout -b <name>
合并某分支到當(dāng)前分支:git merge <name>
刪除分支:git branch -d <name>
2. 解決沖突
分支合并的過(guò)程往往并不是一帆風(fēng)順的,準(zhǔn)備新的feature1
分支,我們?cè)谶@個(gè)分支上作出我們要修改的內(nèi)容,然后提交.然后在切換回master
分支上時(shí),要將feature1
分支上提交的內(nèi)容合并過(guò)來(lái),這時(shí)如果其他人已經(jīng)在master
分支上也對(duì)相應(yīng)文件作出了修改,master
和feature1
分支各自有了新的提交,變成了如下這個(gè)樣子:
這種情況下,Git無(wú)法執(zhí)行"快速合并",只能把各自的修改合并起來(lái),但這種合并就可能會(huì)有沖突,如下所示:
$ 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.txt文件沖突了,必須手動(dòng)解決沖突后再提交.git status
也可以告訴我們沖突的文件:
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
我們可以直接查看readme.txt的內(nèi)容:
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.
>>>>>>> feature
Git用<<<<<<<
,=======
,>>>>>>>
標(biāo)記不同分支的內(nèi)容,其中HEAD
表示當(dāng)前分支的內(nèi)容.
解決沖突后,在提交,現(xiàn)在master
和feature1
分支變成了下圖所示:
用
git log --graph
命令可以看到分支合并圖:
$ git log --graph --pretty
* commit b95bbf0ac897b2d88fa4bcf0308913f624d566c7
|\ Merge: c2e47c0 89cdc59
| | Author: alan7c <chong_luo@kingdee.com>
| | Date: Fri Mar 17 17:48:28 2017 +0800
| |
| | conflict fixed
| |
| * commit 89cdc59631411c217e99b8c0b1a6c1ceee63d677
| | Author: alan7c <chong_luo@kingdee.com>
| | Date: Fri Mar 17 17:40:45 2017 +0800
| |
| | AND simple
| |
* | commit c2e47c06db550e03b2c19ee5247b03d949a10541
|/ Author: alan7c <chong_luo@kingdee.com>
| Date: Fri Mar 17 17:42:44 2017 +0800
|
| & simple
|
* commit fae4e76e7b00741fd3894ebdfcf46cb5712b2db2
| Author: alan7c <chong_luo@kingdee.com>
| Date: Fri Mar 17 15:34:00 2017 +0800
|
| 忽略不要的文件
|
* commit e151f7e1a74ca3eb4a466568dbac5de922b3e596
| Author: alan7c <chong_luo@kingdee.com>
| Date: Mon Mar 13 21:19:10 2017 +0800
|
| add README.md
|
* commit 3fafa4a18f01733d1347da2248775db29421ffaa
| Author: alan7c <chong_luo@kingdee.com>
| Date: Mon Mar 13 11:32:54 2017 +0800
最后,我們可以刪除feature1
分支:
$ git branch -d feature1
Deleted branch feature1 (was 89cdc59).
3.分支管理策略
通常合并分支時(shí),如果可能,Git會(huì)用Fast forward
模式,但這種模式下,刪除分支后,會(huì)丟掉分支信息.如果強(qiáng)制禁用Fast forward
模式,Git就會(huì)在merge時(shí)生成一個(gè)新的commit,這樣,從分支歷史上就可以看出分支信息.
那么如何禁用Fast forward
模式呢,就要使用--no-ff
參數(shù):
git merge --no-ff -m "merge with no-ff" dev
因?yàn)楸敬魏喜⒁獎(jiǎng)?chuàng)建一個(gè)新的commit,所以加上-m
參數(shù),把commit描述寫進(jìn)去.
不使用Fast forward
模式,merge后就像這樣:
分支策略
在實(shí)際開(kāi)發(fā)中,我們應(yīng)該按照幾個(gè)基本原則進(jìn)行分支管理:
- 首先要保證
master
分支應(yīng)該是非常穩(wěn)定的,也就是僅用來(lái)分布新版本,平時(shí)不能在上面干活; - 干活都在
dev
分支,也就是說(shuō)dev
分支是不穩(wěn)定的,到某個(gè)時(shí)候如版本發(fā)布時(shí),在把dev
分支合并到master
上,用master
分支發(fā)布新版本.
我們可以和小伙伴們每個(gè)人都在dev
分支上干活,每個(gè)人都有自己的分支,時(shí)不時(shí)的往dev
分支上合并就可以了.
所以團(tuán)隊(duì)合作的分支看起來(lái)就像這樣:
如果我們一個(gè)臨時(shí)分支上完成了開(kāi)發(fā)任務(wù),可是突然產(chǎn)品經(jīng)理說(shuō)不要這個(gè)功能了,所以我們沒(méi)有合并這個(gè)分支而且需要?jiǎng)h除它,使用git branch -d <name>
時(shí),會(huì)銷毀失敗,Git會(huì)提示我們這個(gè)分支還沒(méi)有完成合并,如果刪除,將丟失修改,如果強(qiáng)行刪除,需要使用命令git branch -D <name>
小結(jié)
合并分支時(shí),加上--no-ff
參數(shù)就可以用普通模式合并,合并后的歷史有分支記錄,而Fast forward
合并就看不出來(lái)曾經(jīng)做過(guò)合并.
4.Bug分支
在軟件開(kāi)發(fā)過(guò)程中,bug是普遍存在的.既然會(huì)出現(xiàn)bug,當(dāng)然就要去修復(fù),在Git中分支系統(tǒng)是什么強(qiáng)大的,所以每個(gè)bug都可以通過(guò)一個(gè)新的臨時(shí)分支來(lái)修復(fù),修復(fù)后在刪除這個(gè)臨時(shí)分支,當(dāng)我們接到一個(gè)編號(hào)110的bug時(shí),想當(dāng)然我們會(huì)創(chuàng)建一個(gè)issue-110
來(lái)修復(fù)它,但是等等,當(dāng)前正在dev
分支上進(jìn)行的工作還沒(méi)有提交,可是這個(gè)時(shí)候并不是你不想提交而是工作只進(jìn)行了一半,還沒(méi)法提交,預(yù)計(jì)完成還需要一天時(shí)間,但是,bug必須在兩個(gè)小時(shí)內(nèi)修復(fù),怎么辦呢?
不怕,Git還提供了一個(gè)stash
功能,可以把當(dāng)前工作現(xiàn)場(chǎng)"儲(chǔ)藏起來(lái)",等以后恢復(fù)現(xiàn)場(chǎng)后繼續(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ū),就是干凈的,因此就可以放開(kāi)手腳來(lái)修復(fù)bug了.
假如我們要從master
分支上修復(fù)bug,就從master
創(chuàng)建臨時(shí)分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
$ git checkout -b issue-101
Switched to a new branch 'issue-110'
等到bug修復(fù)后,然后提交:
git add .
git commit -m "fix bug 110"
[issue-110 cc17032] fix bug 110
1 file changed, 1 insertion(+), 1 deletion(-)
修復(fù)完成后,切換到master
分支,并完成合并,最后刪除issue-110
分支:
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
$ git merge --no-ff -m "merged bug fix 110" issue-110
Merge made by the 'recursive' strategy.
readme.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git branch -d issue-110
Deleted branch issue-101 (was cc17032).
太棒了,修復(fù)完bug后,是時(shí)候接著回到dev
分支干活了!
$ git checkout dev
Switched to branch 'dev'
$ git status
# On branch dev
nothing to commit (working directory clean)
工作區(qū)是干凈的,剛才的工作現(xiàn)場(chǎng)存到哪里去了?用git stash list
命令查看:
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
工作現(xiàn)場(chǎng)顯然還在,這時(shí)需要使用恢復(fù)命令來(lái)恢復(fù)現(xiàn)場(chǎng):
一是用git stash apply
恢復(fù),但是恢復(fù)后,stash內(nèi)容并不刪除,你需要用git stash drop
來(lái)刪除;
二是用git stash pop
,恢復(fù)的同時(shí)把stash內(nèi)容也刪了;
小貼士:
你可以多次stash,恢復(fù)的時(shí)候,先用git stash list
查看,然后恢復(fù)指定的stash,用命令:
git stash apply stash@{0}
5.多人協(xié)作
當(dāng)你從遠(yuǎn)程倉(cāng)庫(kù)克隆時(shí),實(shí)際上Git自動(dòng)把本地的master
分支和遠(yuǎn)程的master
分支對(duì)應(yīng)起來(lái)了,并且遠(yuǎn)程倉(cāng)庫(kù)默認(rèn)名稱是origin
.
要查看遠(yuǎn)程庫(kù)的信息,用git remote
:
$ git remote
origin
或者git remote -v
顯示更詳細(xì)的信息:
$ git remote -v
origin git@github.com:Alanluochong/strman-java.git (fetch)
origin git@github.com:Alanluochong/strman-java.git (push)
上面顯示了可以抓取和推送的origin
的地址,如果沒(méi)有推送權(quán)限,就看不到push地址.
推送分支
推送時(shí),要指定本地分支,這樣,Git就會(huì)把該分支推送到遠(yuǎn)程倉(cāng)庫(kù)所對(duì)應(yīng)的遠(yuǎn)程分支上:
$ git push origin master
抓取分支
$ git clone git@github.com:Alanluochong/strman-java.git
默認(rèn)情況下,拉取后只能看到本地有一個(gè)master
分支,如果要在其他分支上開(kāi)發(fā),就必須創(chuàng)建遠(yuǎn)程origin
的相應(yīng)分支到本地,于是可以用這個(gè)命令創(chuàng)建本地分支:
$ git checkout -b dev origin/dev
向遠(yuǎn)程推送本地的提交,有可能推送失敗,因?yàn)槟愕男』锇榈淖钚绿峤缓湍阍噲D推送的提交有沖突,這時(shí)需要使用git pull
把最新的提交從origin/dev
抓下來(lái),然后,在本地合并,解決沖突在推送:
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:alanluochong/Java
fc38031..291bea8 dev -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream dev origin/<branch>
git pull
也失敗了,原因是沒(méi)有指定本地dev
分支與遠(yuǎn)程origin/dev
分支的連接,根據(jù)提示,設(shè)置dev
與遠(yuǎn)程origin/dev
的連接:
$ git branch --set-upstream dev origin/dev
Branch dev set up to track remote branch dev from origin.
小結(jié)
多人協(xié)作的工作模式通常是這樣:
- 首先,在本地創(chuàng)建和遠(yuǎn)程對(duì)應(yīng)的分支,使用
git branch -b branch-name origin/branch-name
,本地分支最好與遠(yuǎn)程名稱一致; - 完成開(kāi)發(fā)后,可以試圖使用
git push origin branch-name
推送自己的修改; - 如果推送失敗,則因?yàn)檫h(yuǎn)程分支比你的本地有新的提交,需要使用
git pull
試圖合并; - 如果合并有沖突,則解決沖突,并在本地提交;
- 沒(méi)有沖突或者解決沖突后,再用
git push origin branch-name
推送就能成功!
如果git pull
提示"no tracking information",則說(shuō)明本地分支和遠(yuǎn)程分支的連接關(guān)系沒(méi)有創(chuàng)建,用命令git branch --set-upstream branch-name origin/branch-name
.