Git中的分支
具體請參考:https://git-scm.com/book/zh/v1/Git-%E5%88%86%E6%94%AF-%E4%BD%95%E8%B0%93%E5%88%86%E6%94%AF
Git命令請參考:https://www.yiibai.com/git/git_config.html
1、分支的概念
Git 中的分支,其實本質上僅僅是個指向 commit 對象的可變指針。Git 會使用 master 作為分支的默認名字。在若干次提交后,你其實已經有了一個指向最后一次提交對象的 master 分支,它在每次提交的時候都會自動向前移動。
如下圖所示,在你提交了三次后,Git 中的默認分支(主分支)移動到了第三個提交對象上。圖中綠色的方框代表一個提交對象,紫色的可以看做是每個提交對象的有關操作(下面內容并不涉及到,可以忽略),灰色的是目前的分支。

2、創建新分支(git branch branchName)
$ git branch branchName //創建的是本地分支,當該分支已經存在時會提示已經存在了
創建新分支時會在當前分支的代碼上復制一份放到新分支上。
這會在當前 commit 對象上新建一個分支指針,如下圖:

我們可以通過 HEAD 指針來知道當前工作在哪個分支上。在 Git 中,它是一個指向你正在工作中的本地分支的指針(可以將 HEAD 想象為當前分支的別名。)。

運行 git branch 命令,僅僅是建立了一個新的分支,但不會自動切換到這個分支中去。如上例中我們仍然是在 master 分支上工作,如果繼續提交也將會只提交在 master 分支上。
3、切換分支(git checkout 分支名)
使用 git checkout 加上分支名來切換分支。不過在切換分支之前,我們必須先把目前所在的分支的修改提交 commit ,否則會報錯(因為此時切換分支會導致你在本地未commit的修改將會全部丟失)。
在切換分支前必須先將修改 commit,或者使用 git stash 命令放在堆棧中,等需要的時候再使用 git stash pop 進行應用。切換分支的時最好保持一個清潔的工作區域。
$ git checkout branchName //git checkout origin/master 切換到遠程分支上
也可以切換到遠程分支上,此時也可以在本地修改然后再推送到遠程分支上,此時推送使用:git push origin HEAD:遠程分支名
3.1、創建并切換到新分支上(git checkout -b branchName)
要切換到其他分支,可以執行 git checkout 命令。
$ git checkout testing //切換到 testing 分支上 $ git checkout -b testing //新建testing分支并切換到該分支上,相當于同時執行了 git branch testing 和 git checkout testing
這樣 HEAD 就指向了 testing 分支

3.2、在不同分支上工作的原理
如果我們繼續在 testing 分支上工作時,比如新提交了一次,結果如下圖所示:

每次提交后 HEAD 隨著分支一起向前移動,現在 testing 分支向前移動了一格,而 master 分支仍然指向原先 git checkout 時所在的 commit 對象。
切換回 master 分支,git checkout master ,結果是如下圖的:

這條命令做了兩件事。它把 HEAD 指針移回到 master 分支,并把工作目錄中的文件換成了 master 分支所指向的快照內容(你的工作目錄上的文件將會自動變成在 master 分支上最新的提交版本一樣)。也就是說,現在開始所做的改動,將始于本項目中一個較老的版本。它的主要作用是將 testing 分支里作出的修改暫時取消,這樣你就可以向另一個方向進行開發。
如果我們在 master 分支上工作,比如在 master 分支上提交了一次之后,我們的項目提交歷史產生了分叉,因為剛才我們創建了一個分支,轉換到其中進行了一些工作,然后又回到原來的主分支進行了另外一些工作。這些改變分別孤立在不同的分支里:我們可以在不同分支里反復切換,并在時機成熟時把它們合并到一起。如下圖:

在Git中創建和銷毀分支,在分支上切換等速度非常快,Git 鼓勵開發者頻繁使用分支
4、合并分支(git merge)
4.1、需要創建新分支的情景
假設我們正在項目的本地 master 分支上工作(一般本地master分支也關聯遠程master分支),并且已經提交了幾次更新

但是項目出現了點問題,為了解決該問題,新建了一個新分支并取名為 iss53,希望在該分支上解決問題過后再合并到master分支上。
$ git checkout -b iss53

接著你開始嘗試修復問題,在提交了若干次更新后,iss53 分支的指針也會隨著向前推進,因為它就是當前分支

這時項目上出現一個緊急 bug 必須修改,這時我們正在 iss53分支上工作,我們并不需要把目前的工作 push 到遠程倉庫,唯一需要的僅僅是切換回 master 分支去修改 bug 然后再提交到遠程倉庫的master分支上。
此時工作目錄中的內容和你在解決問題 #53 之前一模一樣,你可以集中精力進行緊急修補。這一點值得牢記:Git 會把工作目錄的內容恢復為檢出某分支時它所指向的那個提交對象的快照。它會自動添加、刪除和修改文件以確保目錄的內容和你當時提交時完全一樣。
接下來我們進行修補bug,我們一般會在 master 分支上新建一個分支來修改 bug ,而不會直接在 master 分支上進行修改。比如創建了一個用來修補 bug 的 hotfix 分支,并在該分支上進行了一系列修補工作:
$ git checkout -b hotfix

4.2、合并分支(git merge 目標分支)
當我們確保在hotfix 上的分支修補是成功的后就可以回到 master 分支將它給合并起來,然后發布到遠程倉庫上。用 git merge 命令來進行合并(git merge命令用于合并指定分支到當前所處的分支)
$ git checkout master $ git merge hotfix //合并分支前,先把當前分支的修改提交commit,不然會報錯
合并時可能會彈出需要你輸入此次合并說明的編輯界面,先按 i 鍵,輸入你的說明,然后 esc ,再輸入 :wq 即可。也可以直接esc,然后 :wq 退出。
每次合并都會在本地分支上自動生成一次提交(而被合并的分支并不會生成一個提交對象),你輸入的信息或者使用默認信息將會作為本次提交的說明信息。如果合并有沖突的話,Git 不會自動生成一個提交版本,而是顯示讓你手動解決沖突,然后你需要自己手動進行一次提交。
(請注意,合并時將可能出現“Fast forward”的提示。由于當前 master 分支所在的提交對象是要并入的 hotfix 分支的直接上游,Git 只需把 master 分支指針直接右移。換句話說,如果順著一個分支走下去可以到達另一個分支的話,那么 Git 在合并兩者時,只會簡單地把指針右移,因為這種單線的歷史分支不存在任何需要解決的分歧,所以這種合并過程可以稱為快進(Fast forward)。)

合并之后最新的修改都已經在 master 分支上了,我們就可以將 master 分支push 到遠程倉庫上。
(注意:合并分支時并不是兩個分支內容一樣了,只是把目標分支的內容合并到了當前分支,下面的4.2可以看出差別)
4.2、合并兩個分叉分支的情況
在上面的情況,master分支和 hotfix 分支并沒有分叉,合并時只是 master 指針向右移了而已。(值得注意的是hotfix 分支與 master 分支的合并并不會影響到 iss53 分支,如果需要納入此次修補,可以用 git merge master 把 master 分支合并到 iss53;或者等 iss53 完成之后,再將 iss53 分支中的更新并入 master。)
在刪除了 hotfix 分支后,我們現在回到之前未完成任務的 iss53 分支上繼續工作。

在完成了在 iss53 分支上的任務后,可以合并回 master 分支。實際操作同前面合并 hotfix 分支一樣,只需回到 master 分支,運行 git merge 命令指定要合并進來的分支:
$ git checkout master
$ git merge iss53
不過這次合并操作的底層實現,并不同于之前 hotfix 的并入方式。因為這次你的開發歷史是從更早的地方開始分叉的,當前 master 分支所指向的提交對象(C4)并不是 iss53 分支的直接祖先。這次,Git 沒有簡單地把分支指針右移,而是對三方合并后的結果重新做一個新的快照,并自動創建一個指向它的提交對象(C6)

從上圖可以看到,iss53 分支的指針并沒有指向 C6,因為是在master分支上進行的合并,只是把 iss53 分支上的內容合并到 master分支上來,此時master 分支上的文件將包含 iss53 分支上的修改,而iss53 分支上并沒有因此而發生任何改變。
5、解決沖突
有時候合并操作并不會如此順利,如果在不同的分支中都修改了同一個文件的同一部分,Git 就無法干凈地把兩者合到一起此時就會產生沖突(邏輯上說,這種問題只能由人來裁決。),比如下面的master 分支和 FZ02 分支:

Git 將FZ02分支上的內容合并過來了master分支上,但沒有提交,它會停下來等你解決沖突。
要看看哪些文件在合并時發生沖突,可以用 git status

任何包含未解決沖突的文件都會以未合并(unmerged)的狀態列出。Git 會在有沖突的文件里加入標準的沖突解決標記,可以通過它們來手工定位并解決這些沖突。下面是在 visual studio code 上打開的沖突文件:

<<< HEAD 到 ======= 是自己目前在本地所修改的內容,====== 到 >>>>>> 是你從遠程倉庫上 pull 拉下來的內容。
你可以把一部分內容刪掉,或者兩部分內容都保存或者整合起來都由自己決定,還要刪除 <<<<<<<,======= 和 >>>>>>> 等沖突標記。在解決了所有文件里的所有沖突后,運行 git add 將把它們標記為已解決狀態,因為一旦暫存,就表示沖突已經解決。

在產生沖突時你可以看到正處于 merging 狀態,在手動解決完沖突文件后,首先應該把沖突文件緩存,然后將本次版本提交,提交的這個版本就是作為 master 分支上合并后的提交對象。提交后將自動退出 merging 狀態。
5.1、產生沖突時選擇退出合并(git reset --hard HEAD)
合并時若有沖突會進入merging狀態,git reset --hard HEAD 命令可以退出該狀態,相當于退出合并操作,就當做什么也沒發生。
6、刪除分支(git branch -d 分支名)
在改完 bug 以后,你想要繼續回去修改 iss53 的問題。由于當前 hotfix 分支和 master 都指向相同的提交對象,所以 hotfix 已經完成了歷史使命,可以刪掉了。使用 git branch 的 -d 選項執行刪除操作
$ git branch -d hotfix //刪除 hotfix 分支 $ git checkout iss53 //回到 iss53 分支繼續工作
刪除分支時,有可能會報:Cannot delete branch 'xxx' checked out at 'xxxx'。這是因為刪除分支時,當前分支不能停留在要刪除的分支上,要切換到其他任意分支,才能刪除目標分支。
6.1、強制刪除未被合并的分支(git branch -D 分支名)
如果某一分支暫未被合并簡單地用 git branch -d 刪除該分支會提示錯誤,因為那樣做會丟失數據。這時如果我們仍然想刪除該分支的話可以使用強制刪除命令,比如在 hotfix 分支未被合并到 master 分支前將其刪除:
$ git branch -D hotfix
7、查看分支(git branch)
7.1、查看本地分支(git branch)
git branch 命令不僅僅能創建和刪除分支,如果不加任何參數,它會給出當前所有分支的清單

注意看 master 分支前的 * 字符:它表示當前所在的分支。也就是說,如果現在提交更新,master 分支將隨著開發進度前移。
7.2、查看遠程分支(git branch -r)

origin 代表的是默認的遠程庫,一般是你克隆的時候的庫,Git默認起名叫 origin。還有一個庫 yc02 是因為我在這個本地倉庫上關聯了兩個遠程庫。
origin/FZ02 表示遠程倉庫 origin 上的 FZ02分支
7.3、查看所有分支(git branch -a)

7.3、查看本地分支最新的提交信息(git branch -v)
若要查看各個分支最后一個提交對象的信息,運行 git branch -v

7.4、查看已經與當前分支合并的分支(git branch --merged)
要從該清單中篩選出你已經(或尚未)與當前分支合并的分支,可以用 --merged 。最少都會顯示出自己

只會列出剛剛才與當前分支合并的分支名,比如上面的 FZ02 與 master分支合并,這時運行上面代碼會顯示 FZ02 分支,如果合并后 FZ02 分支再次發生修改并提交了,那么再次在 master 分支上運行該代碼不會顯示出 FZ02 分支。
一般來說,列表中沒有 * 的分支通常都可以用 git branch -d 來刪掉。原因很簡單,既然已經把它們所包含的工作整合到了其他分支,刪掉也不會損失什么。
7.5、查看沒有與當前分支合并的分支(git branch --no-merged)
意義與 git branch --merged 相反。

7.6、查看本地分支分別對應關聯的遠程分支(git branch -vv)

前面的部分是本地分支,后面的是對應關聯的遠程分支,沒有關聯的遠程分支的話后面就是最近的提交信息。
8、建議的分支管理
僅在 master 分支中保留完全穩定的代碼,即已經發布或即將發布的代碼。與此同時,建立一個名為 develop 的平行分支專門用于后續的開發,或僅用于穩定性測試,當然并不是說一定要絕對穩定,不過一旦進入某種穩定狀態,便可以把它合并到 master 里。
在確保這些已完成的特性分支(比如下面的 topic 分支)能夠通過所有測試,并且不會引入更多錯誤之后,就可以并到develop分支中,然后再可以并到master分支中,等待下一次的發布。

分支管理:
參考:http://www.ruanyifeng.com/blog/2012/07/git.html
其實就是一般只有一個主分支 master,然后有一個開發分支 develop,日常的開發一般都是在 develop 分支上進行。如果進入了某個穩定階段就可以把dev 分支合并到 master 分支上。常設分支就只有上面兩條,但是有一些特殊情況可能要在 dev 分支上開設臨時分支。
一種是為了開發某種功能,然后從 dev 分支上分出來,等開發完成再合并到 dev 分支上。
一種是為了修補bug,這個分支可能是因為發布版本出現了bug,所以從 master 分支上分出來一個分支,在該分支上解決 bug 后,再將該分支合并到master分支上,而且也要合并到 dev 分支上。
9、遠程分支
從Git遠程倉庫上克隆下來本地,Git 會自動為你將此遠程倉庫命名為 origin,并下載其中所有的數據,建立一個指向遠程倉庫的 master 分支的指針,在本地命名為 origin/master。接著,Git 建立一個屬于本地 master 分支,跟遠程倉庫 origin 上 master 分支處于同一版本上。如下圖:

一次 Git 克隆會建立你自己的本地分支 master 和遠程分支 origin/master,并且將它們都指向 origin 上的 master 分支。
如果其他人向該遠程倉庫推送了他們的更新,那么遠程倉庫上的 master 分支就會向前推進。而你在本地 master 分支工作,如果你不和服務器通訊,你的遠程分支指針 origin/master 將保持原位不會移動,而本地master指針在不斷向前推進。

10、拉取遠程分支(git fetch 遠程倉庫名)
$ git fetch <遠程倉庫名>
該命令將指定的遠程倉庫的所有遠程分支的更新都同步到本地的遠程分支中,由此你在本地的所有遠程分支將與遠程倉庫的分支同步。所取回的更新,在本地主機上要用”遠程主機名/分支名”的形式讀取。比如origin主機的master分支,就可以用origin/master讀取。
但是注意,此時與遠程倉庫對應的本地分支并沒有得到更新,所以要想查看拉取回來的更新必須要切換到本地的遠程分支上才能查看:git checkout origin/master。
而要想達到合并,可以使用下面的命令:
$ git fetch origin
$ git merge origin/master
默認情況下,git fetch取回所有分支的更新。如果只想取回特定分支的更新,可以指定分支名,如下所示
$ git fetch <遠程主機名> <分支名> //git fetch origin master 只取回指定分支的更新 $ git fetch //默認拉取 origin 主機的所有分支更新
11、拉取遠程分支并且合并(git pull)
git fetch 命令只是將本地的遠程分支與遠程倉庫的分支進行了一個同步更新,本地與遠程分支關聯的分支并沒有得到更新,而要想更新還得手動 git merge。而 git pull 命令相當于同時執行了這兩條命令(同時也把該遠程倉庫的所有遠程分支的更新都同步到本地的遠程分支中了)。
取回指定的某一遠程分支并與本地指定的某一分支合并:
$ git pull <遠程主機名> <遠程分支名>:<本地分支名> //git pull origin next:master 取回origin主機的next分支,與本地的master分支合并
git pull 相關的命令只會合并對應的本地分支使其更新,其他的本地分支并沒有得到更新,但是本地的遠程分支都得到了更新(相當于執行了 git fetch origin 然后只合并指定的本地分支)
11.1、取回指定的遠程分支并與當前分支合并(git pull origin <遠程分支名>)
$ git pull origin branch02 //遠程分支 branch02 與當前分支合并 相當于先 git fetch origin 然后再 git merge origin/next
11.2、已建立追蹤關系直接拉取(git pull origin、git pull)
如果當前分支已經與遠程某一分支之間建立了一種追蹤關系(tracking)的話,可以直接省略遠程分支名,Git會自動識別出當前分支與遠程分支的關聯,并且只拉取與之關聯的遠程分支的更新,其他分支不會得到更新。
(若沒有建立追蹤關系直接使用該命令將報錯)
$ git pull origin
不用遠程倉庫名時默認指定 origin 倉庫(常用命令)
$ git pull //拉取與當前分支關聯的遠程分支的更新并且合并
12、推送(git push)
要想和其他人分享某個本地分支,你需要把它推送到一個你擁有寫權限的遠程倉庫。你創建的本地分支不會因為你的寫入操作而被自動同步到你引入的遠程服務器上,你需要主動執行推送分支的操作。你要想把本地某一分支直接推送到遠程倉庫中(即直接用git push 命令),你必須將本地分支與遠程的某一分支關聯起來,否則推送不會成功,或者可以手動指定要推送的遠程分支。
比如下面我新建了一個 bh03 分支,沒有把該分支與遠程某一分支關聯,推送內容的話就會顯示錯誤:

當然對于無意分享上去的分支,可以只保留為私人的本地分支,而只推送那些協同工作要用到的特性分支。
12.1、推送本地分支到指定的遠程分支(git push origin 本地分支名:遠程分支名)
使用該命令時不需要提前把本地分支與遠程分支關聯,比如:

若遠程沒有上面指定的bh05 分支,將自動創建一個 bh05分支。但注意:使用該命令后指定的本地分支 bh03 與遠程分支 bh05 并不會自動關聯上了,以后的 bh03 要想推送到 bh05 上都需指定遠程分支名,否則還是推送不成功。
12.2、推送到與本地分支同名的遠程分支(git push origin 分支名)
若本地分支與遠程分支有同名分支,直接使用 git push origin 分支名 命令將自動把指定的本地分支推送到同名的遠程分支上,執行該命令不需切換到本地的要推送的分支上,在其他分支推送也可以。
該命令省略的是遠程分支名,相當于 git push origin 分支名:遠程分支名 。若遠程倉庫沒有與本地分支同名的分支,將在遠程倉庫創建一個同名的分支。該命令也不會自動關聯只是推送,若要關聯要加 -u 參數

12.3、已建立追蹤關系,直接推送(git push origin、git push)
如果當前分支與遠程分支之間存在追蹤關系,則本地分支和遠程分支都可以省略。(若當前分支與遠程分支不存在追蹤關系,直接使用該命令將報錯)
$ git push origin
不用遠程倉庫名時默認指定 origin 倉庫(常用命令)
$ git push
不帶任何參數的 git push,默認只推送當前分支,這叫做simple方式。此外,還有一種matching方式,會推送所有有對應的遠程分支的本地分支。在Git 2.0版本之前,默認采用matching方法,現在改為默認采用simple方式。如果要修改這個設置,可以采用git config命令。
$ git config --global push.default matching # 或者 $ git config --global push.default simple
12.4、使用 push 命令刪除遠程分支(git push origin :master)
如果省略本地分支名,則表示刪除指定的遠程分支,因為這等同于推送一個空的本地分支到遠程分支。
$ git push origin :master //表示刪除origin主機的master分支。 # 等同于 $ git push origin --delete master
15、將本地分支與遠程分支關聯
15.1、本地分支已經存在(git push -u <本地分支名>:<遠程分支名>,常用命令)
將某個已存的本地分支關聯到遠程的分支上,該命令也會執行推送,所以如果指定遠程分支上的版本比本地分支的高將會報錯
$ git push -u origin <本地分支名>:<遠程分支名> $ git push -u origin master //關聯到與本地遠支同名的遠程分支
執行該命令,若遠程分支沒有指定的分支將會自動新建一個遠程分支。注意:git push origin <本地分支名>:<遠程分支名>命令只是推送并不會關聯。
15.2、本地分支已存在,使用git branch -u 關聯
將某個已存的本地分支關聯遠程分支,先切換到該本地分支上,然后執行: git branch -u origin/遠程分支名
或者不用切換到該本地分支上,直接執行: git branch -u origin/遠程分支名 本地分支名
15.3、本地分支已存在,通用命令(--set-upstream-to)
直接執行: git branch --set-upstream-to=origin/remote_branch your_branch
若已經切換到要關聯的本地分支上,后面的本地分支名可以省略
15.4、本地分支不存在(git checkout -b 新分支名 origin/遠程分支名)
將已存的遠程分支拉取下來并創建一個本地分支,本地分支與遠程分支關聯并更新:
$ git checkout -b newBrach origin/master
16、Git 中的 HEAD 和 origin/HEAD
通過查看分支可以看到目前的 HEAD 的指向

可以看到上面有一個 * 符號,還有一個 origin/HEAD
* 號就是 HEAD 的標識,表示 HEAD。HEAD 是一個指針,指向你當前所在的分支。如上圖,* 后面的分支就是我目前所在分支。當你切換分支時,HEAD 也會發生改變,會指向切換過去的分支上。
但是如果切換到遠程分支上,就會出現 detached HEAD 的情況。如下圖,我切換到了遠程的 master 分支上:

參考:https://gitbook.tw/chapters/faq/detached-head.html
origin/HEAD :
origin/HEAD也是一個指針,表示默認分支,應該表示的是遠程倉庫的默認分支吧

浙公網安備 33010602011771號