分佈式版本控制系統(git分支管理)

1,分支管理

分支在實際中有什麼用呢?假設你準備開發一個新功能,可是須要兩週才能完成,第一週你寫了50%的代碼,馬上提交,因爲代碼尚未寫完,不完整的代碼庫會致使別人不能幹活了。若是等代碼所有寫完再一次提交,又存在丟失天天進度的風險。
如今有了分支,就不能怕了,你建立了一個屬於你本身的分支,別人看不到,還繼續在原來的分支上正常工做,而你在本身的分支上幹活,想提交就提交,直到開發完畢後,在一次性合併到原來的分支上,這樣,既安全,又不影響別人工做。git

Git的分支與其餘版本控制系統不一樣,不管建立,切換和刪除分支,Git在1秒鐘以內就能完成!不管你的版本庫是1個文件仍是1萬個文件。github

2,建立與合併分支

在版本回退裏,咱們已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git裏,這個分支叫主分支(master分支),HEAD嚴格來講不是指向提交,而是指向master,master纔是指向提交的,因此,HEAD指向的就是當前分支。安全

① 一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD執行maser,就能肯定當前分支,以及當前分支的提交點,每次提交,master分支都會向前一步:
分佈式版本控制系統(git分支管理)
② 當咱們建立新的分支,例如dev時,Git建立了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
分佈式版本控制系統(git分支管理)
能夠看到,Git建立一個分支很快,由於除了增長一個dev指針,改改HEAD的指向,工做區的文件都沒有任何變化。app

③ 不過,從如今開始,對工做區的修改和提交就是針對dev分支了,好比新提交一次後,dev指針往前移動一步,而master指針不變:
分佈式版本控制系統(git分支管理)分佈式

④ 假如咱們在dev上的工做完成了,就能夠把dev合併到master上。Git合併很簡單,就是把master指向dev的當前提交,就完成了合併:
分佈式版本控制系統(git分支管理)
因此Git合併分支也很快,就改改指針,工做區內容不變。ide

⑤ 合併完分支後,甚至能夠刪除dev分支。刪除dev分支就是把dev指針給刪掉,咱們就剩下了一條master分支:
分佈式版本控制系統(git分支管理)fetch

下面開始進行實踐:
首先,咱們建立dev分支,而後切換到dev分支:this

[jonson@localhost mygit]$ git checkout  -b dev
Switched to a new branch 'dev'
#git checkout命令加上-b參數表示建立並切換,至關於如下兩條命令:
$ git branch dev
$ git checkout dev

而後,用git branch命令查看當前分支:3d

[jonson@localhost mygit]$ git branch
* dev     
  master
#git branch命令會列出全部分支,當前分支前面會標一個*符號

而後,咱們就能夠在dev分支上正常提交,好比對test.txt文件作修改:版本控制

[jonson@localhost mygit]$ echo "create a new brach" >> text.txt 
[jonson@localhost mygit]$ cat text.txt 
# this is jonson first repo
create a new brach
而後提交:
[jonson@localhost mygit]$ git add text.txt 
[jonson@localhost mygit]$ git commit -m "branch test"
[dev e2d7f2d] branch test
 1 file changed, 1 insertion(+)

如今,dev分支的工做完成,咱們就能夠切換回master分支:

[jonson@localhost mygit]$ git checkout master
Switched to branch 'master'
[jonson@localhost mygit]$ git branch
  dev
* master
[jonson@localhost mygit]$ cat text.txt 
# this is jonson first repo

切換回master分支後,在查看剛剛修改的文件,剛纔添加的內容不見了。緣由是那個提交是在dev分支上,而master分支此刻的提交點並無變:
分佈式版本控制系統(git分支管理)

合併分支:
如今,咱們把dev分支的工做成果合併到master分支上:

[jonson@localhost mygit]$ git merge dev
Updating 7646ab6..e2d7f2d
Fast-forward
 text.txt | 1 +
 1 file changed, 1 insertion(+)
[jonson@localhost mygit]$ cat text.txt 
# this is jonson first repo
create a new brach

git merge命令用於合併指定分支到當前分支。合併後,再查看文件的內容,就能夠看到,和dev分支的最新提交是徹底同樣的。

注意:上面的Fast-forward信息,Git告訴咱們,此次合併是「快進模式」,也就是直接把master執行dev的當前提交,因此合併速度很是快,固然,也不是每次合併都是Fast-forward,後面會講其餘方式的合併。

#合併完成後,若是須要刪除dev分支,能夠執行如下命令:

[jonson@localhost mygit]$ git branch -d dev
Deleted branch dev (was e2d7f2d).
[jonson@localhost mygit]$ git branch
* master
#刪除後,查看branch,就只剩下master分支了。

小結
Git鼓勵大量使用分支:

查看分支:git branch
建立分支:git branch <name>
切換分支:git checkout <name>
建立+切換分支:git checkout -b <name>
合併某分支到當前分支:git merge <name>
刪除分支:git branch -d <name>

3,解決衝突

人生不如意之事十有八九,合併分支每每也不是一路順風的。
1)準備新的分支(feature1)

[jonson@localhost mygit]$ git checkout -b feature1
Switched to a new branch 'feature1'

修改test.txt文件並提交:

[jonson@localhost mygit]$ echo "create two branch" >> text.txt 
[jonson@localhost mygit]$ cat text.txt 
# this is jonson first repo
create a new brach
create two branch
[jonson@localhost mygit]$ git add text.txt 
[jonson@localhost mygit]$ git commit -m "two branch"
[feature1 78292e2] two branch
 1 file changed, 1 insertion(+)

切換到master分支:

[jonson@localhost mygit]$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
#git提示咱們當前master分支比遠程的master分支超前1個提交。

接下來,在master分支上也進行修改並提交:

[jonson@localhost mygit]$ echo "create a three branch" >> text.txt 
[jonson@localhost mygit]$ git add text.txt 
[jonson@localhost mygit]$ git commit -m "three branch"
[master e27d699] three branch
 1 file changed, 1 insertion(+)

如今,master分支和feature1分支各自都分別有新的提交,變成了這樣:
分佈式版本控制系統(git分支管理)
這種狀況下,Git沒法執行「快速合併」,只能試圖把各自的修改合併起來,但這種合併就可能會有衝突,咱們試試看:

[jonson@localhost mygit]$ git merge feature1
Auto-merging text.txt
CONFLICT (content): Merge conflict in text.txt
Automatic merge failed; fix conflicts and then commit the result.

果真衝突了,Git告訴咱們,test.txt文件存在衝突,必須手動解決衝突後再提交。git status能夠告訴咱們衝突的文件:

[jonson@localhost mygit]$ 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")

咱們能夠直接查看test.txt的內容:

[jonson@localhost mygit]$ cat text.txt 
# this is jonson first repo
create a new brach
<<<<<<< HEAD
create a three branch
=======
create two branch
>>>>>>> feature1

git用‘<<<<<<<’,’=======’, ‘>>>>>>>’標記出不一樣分支的內容,咱們修改以下後保存:

[jonson@localhost mygit]$ cat text.txt 
# this is jonson first repo
create a new brach
<<<<<<< HEAD
create two branch
=======
create two branch
>>>>>>> feature1

再提交:

[jonson@localhost mygit]$ git commit -m "new branch"
[master 966dd22] new branch

如今,master分支和feature1分支變成了下圖所示:
分佈式版本控制系統(git分支管理)
工做完成後,刪除feature1分支:

[jonson@localhost mygit]$ git branch -d feature1
Deleted branch feature1 (was 78292e2).

小結:
當Git沒法自動合併分支時,就必須解決衝突。解決衝突後,再提交,最後合併完成。

4,分支管理策略

一般,合併分支時,若是可能,Git會用Fast forward模式,但這種模式下,刪除分支後,會丟掉分支信息。若是要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就能夠看出分支信息。下面來進行實踐:
1)首先,仍然建立並切換dev分支:

[jonson@localhost mygit]$ git checkout -b dev
Switched to a new branch 'dev'
[jonson@localhost mygit]$ git branch
* dev
  master

修改test.txt文件,並提交一個新的commit:

[jonson@localhost mygit]$ echo "create four branch" >> text.txt 
[jonson@localhost mygit]$ git add text.txt 
[jonson@localhost mygit]$ git commit -m "add merge"
[dev e730d1b] add merge
 1 file changed, 1 insertion(+)

如今咱們切換回master:

[jonson@localhost mygit]$ git checkout master
Switched to branch 'master'

準備合併dev分支,請注意--no-ff參數,表示禁用Fast forward

[jonson@localhost mygit]$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 text.txt | 1 +
 1 file changed, 1 insertion(+)

由於本次合併要建立一個新的commit,因此加上-m參數,把commit描述寫進去。合併後,咱們用git log 看看分支歷史:
分佈式版本控制系統(git分支管理)
能夠看到,不使用Fast forward模式,merge後就像這樣:
分佈式版本控制系統(git分支管理)

小結:
Git分支十分強大,在團隊開發中應該充分應用。
合併分支時,加上--no-ff參數就能夠用普通模式合併,合併後的歷史有分支,能看出來曾經作過合併,而fast forward合併就看不出來曾經作過合併。

5,Bug分支

軟件開發中,一定會有bug。有了bug就須要修復,在Git中,因爲分支是如此的強大,因此,每一個bug均可以經過一個新的臨時分支來修復,修復後,合併分支,而後將臨時分支刪除。

當你接到一個修復一個代碼101的bug的任務時,很天然的,你想建立一個臨時分支來修復它, 可是,當前正在dev上進行的工做尚未提交:

[jonson@localhost mygit]$ echo "create five branch" >> text.txt 
[jonson@localhost mygit]$ git status
# On branch dev
# 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:   text.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   new-repo/
no changes added to commit (use "git add" and/or "git commit -a")

此時,問題出現了,工做只進行到一半,還無法提交,預計完成還需1天時間。可是,必須在兩個小時內修復該bug,怎麼辦?
幸虧,Git還提供了一個stash功能,能夠把當前工做現場「儲藏」起來,等之後恢復現場後繼續工做:

[jonson@localhost mygit]$ git stash
Saved working directory and index state WIP on dev: e730d1b add merge
HEAD is now at e730d1b add merge

分佈式版本控制系統(git分支管理)
如今,能夠查看工做區,能夠看到就是乾淨的了,所以能夠放心地建立分支來修復bug。

首先肯定要在哪一個分支上修復bug,假定須要在master分支上修復,就從master建立臨時分支(issue-101):

[jonson@localhost mygit]$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
[jonson@localhost mygit]$ git checkout -b issue-101
Switched to a new branch 'issue-101'

如今修復bug,就假如須要把「create four branch」改爲「create five branch」,而後提交:

[jonson@localhost mygit]$ sed -i 's/four/five/' text.txt 
[jonson@localhost mygit]$ git add text.txt 
[jonson@localhost mygit]$ git commit -m "fix bug 101"
[issue-101 6484832] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

修復完成後,切換到master分支,並完成合並,最後再刪除這個臨時分支:

[jonson@localhost mygit]$ git checkout master
[jonson@localhost mygit]$ git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
 text.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
[jonson@localhost mygit]$ git branch -d issue-101
Deleted branch issue-101 (was 6484832).

這下問題解決了,原計劃兩個小時的bug修復只花了5分鐘!如今,又能夠接着回到dev分支幹活了:

[jonson@localhost mygit]$ git checkout dev
Switched to branch 'dev'
[jonson@localhost mygit]$ git status
# On branch dev
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   new-repo/
nothing added to commit but untracked files present (use "git add" to track)

工做區此時是乾淨的,由於剛纔的工做現場咱們用git stash儲藏起來了,能夠用git stash list 命令看看:

[jonson@localhost mygit]$ git stash list
stash@{0}: WIP on dev: e730d1b add merge

因此如今咱們須要恢復一下,有兩種方式:
一種是用git stash apply 恢復,可是回覆後,stash內容並刪除,你還須要用git stash drop來刪除。
另外一種是用git starh pop,恢復的同時把stash內容也刪了(因此通常採用這種方法):
分佈式版本控制系統(git分支管理)
再用git stash list 查看,就看不到stash內容了,而且也恢復到以前的工做狀態:
分佈式版本控制系統(git分支管理)
#當你屢次stash時,若是你要恢復指定的stash,用如下命令:
git stash apply stash@{0}

小結:
修復bug時,咱們會經過建立新的bug分支進行修復,而後合併,最後刪除。
當手頭工做沒有完成時,先把工做現場git stash儲藏起來,而後去修復bug,再git stash pop,刪除stash內容,並恢復到工做現場。

6,多人協做

當你從遠程庫克隆時,實際上Git自動把本地master分支和遠程的master分支對應起來了,而且,遠程倉庫的默認名稱是origin。

#要查看遠程庫的詳細信息,用`git remote -v`:
[jonson@localhost mygit]$ git remote -v
origin  git@github.com:sqm-sys/jonson-repo.git (fetch)
origin  git@github.com:sqm-sys/jonson-repo.git (push)

上面顯示了能夠抓取和推送的origin的地址。若是沒有推送權限,就看不到push的地址。

推送分支
推送分支,就是把該分支上的全部本地提交推送到遠程庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠程庫對應的遠程分支上:

[jonson@localhost mygit]$ git push origin master
推送其餘分支:
[jonson@localhost mygit]$ git push origin dev

可是,並非必定要把本地分支往遠程推送,那麼,哪些分支須要推送,哪些不須要呢?

  • master分支是主分支,所以須要時刻與遠程同步;
  • dev 分支是開發分支,團隊全部成員都須要在上面工做,因此也須要與遠程同步;
  • bug分支只用於在本地修復bug,就不必推到遠程了,除非領導要看看你每週到底修復了幾個bug;

抓取分支
多人協做時,你們都會往master和dev分支上推送各自的修改。

如今,模擬一個你的同事,能夠在另外一臺電腦(注意要把SSH key添加獲得GitHub)下克隆:
分佈式版本控制系統(git分支管理)

[zhangsan@localhost ~]$ git  clone git@github.com:sqm-sys/jonson-repo.git
Cloning into 'jonson-repo'...
remote: Enumerating objects: 26, done.
remote: Counting objects: 100% (26/26), done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 26 (delta 5), reused 25 (delta 4), pack-reused 0
Receiving objects: 100% (26/26), done.
Resolving deltas: 100% (5/5), done.

須要的注意的是,你的同事從遠程庫clone時,默認狀況下,只能看到本地的master分支,而看不到其餘分支:

[zhangsan@localhost ~]$ cd jonson-repo/
[zhangsan@localhost jonson-repo]$ git branch
* master

如今,你的同事要在dev分支上開發,就必須建立遠程origin的dev分支到本地(能夠執行如下命令):

[zhangsan@localhost jonson-repo]$ git checkout -b dev origin/dev
Branch dev set up to track remote branch dev from origin.
Switched to a new branch 'dev'

此時,他就能夠在dev上分支上進行修改了,而後,時不時的把dev分支push到遠程:

[zhangsan@localhost jonson-repo]$ echo "create six branch" >> text.txt 
[zhangsan@localhost jonson-repo]$ git add text.txt 
[zhangsan@localhost jonson-repo]$ git commit -m  "add six branch"
[dev f50cefa] add six branch
 1 file changed, 1 insertion(+)
[zhangsan@localhost jonson-repo]$ git push origin dev
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 287 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To git@github.com:sqm-sys/jonson-repo.git
   c7d965a..f50cefa  dev -> dev

你的同事已經向origin/dev分支推送了他的提交,而此時你也對一樣的文件做了修改,並試圖推送:

[jonson@localhost mygit]$ git branch
* dev
  master
[jonson@localhost mygit]$ echo "ha ha ha " >> text.txt 
[jonson@localhost mygit]$ git add text.txt 
[jonson@localhost mygit]$ git commit -m "this is haha"
[dev 94c4cde] this is haha
 1 file changed, 1 insertion(+)
[jonson@localhost mygit]$ git push origin dev   #進行推送
To git@github.com:sqm-sys/jonson-repo.git
 ! [rejected]        dev -> dev (fetch first)
error: failed to push some refs to 'git@github.com:sqm-sys/jonson-repo.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first merge the remote changes (e.g.,
hint: 'git pull') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

推送失敗,緣由是你的同事的最新提交和你試圖推送的提交有衝突,解決辦法也很簡單,Git已經提示咱們:先用git pull把最新的提交從origin/dev抓下來,而後,在本地合併,解決衝突,再推送:
分佈式版本控制系統(git分支管理)
能夠看到git pull失敗了,緣由是沒有指定本地dev分支與遠程origin/dev分支的連接,根據提示,設置dev和origin/dev的連接:

[jonson@localhost mygit]$ git branch --set-upstream-to=origin/dev
Branch dev set up to track remote branch dev from origin.

建立連接後,在執行pull:

[jonson@localhost mygit]$ git pull
Auto-merging text.txt
CONFLICT (content): Merge conflict in text.txt
Automatic merge failed; fix conflicts and then commit the result.

這回git pull成功,可是合併有衝突,須要手動解決,解決的方法以前的解決衝突徹底同樣(將衝突的內容進行修改)。解決後,提交,再push:

[jonson@localhost mygit]$ git add text.txt 
[jonson@localhost mygit]$ git commit -m "merge & six branch"
[dev 4ba822d] merge & six branch
[jonson@localhost mygit]$ git push origin dev
Counting objects: 10, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 616 bytes | 0 bytes/s, done.
Total 6 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To git@github.com:sqm-sys/jonson-repo.git
   f50cefa..4ba822d  dev -> dev

push成功,所以,多人協做的工做模式一般是這樣:

1)首先,能夠試圖用'git push origin <branch-name>' 推送本身的修改;
2)若是推送失敗,則由於遠程分支比你的本地更新,須要先用git pull試圖合併;
3)若是合併衝突,則解決衝突,並在本地提交;
4) 沒有衝突或者解決掉衝突後,再用'git push origin <branch-name>' 推送就能成功;
若是git pull提示「no tracking information」,則說明本地分支和遠程分支的連接關係沒有建立,用命令'git branch --set-upstream-to=origin/<branch-name> '。這就是多人協做的工做模式。。。

相關文章
相關標籤/搜索