原文地址:http://baijiahao.baidu.com/s?id=1601782494773830327&wfr=spider&for=pc
Git分支概念和分支相關操作
要明白什么是分支,我們從經典的git圖像開始,類似圖片估計大家都見到過:

圖上顯示有三個分支Master、Dev和Chongchong。
其中Master是默認就存在的一個分支,我們叫它主分支。一般很多人會覺得這個Master系統保留的分支應該有其特殊之處,但是實際上Master和其他分支一樣沒有啥特殊之處,無非他是必須有的分支,這個名稱也可以通過git遠程倉庫修改默認配置,改為阿貓阿狗都沒有問題。這個分支也可以刪,但是需要特殊設置下。
分支其實上就是一個指針
簡而言之,在git中分支只是一個指向單個commit的指針。當然,這個指針只是用來表明這個commit和base分支的之間的偏差。從現在開始,本文中,當我們要指明指針的時候,我們將使用分支引用(branch reference),而我們要指明偏差時候用分支。
我們說分支引用除了指定某個基本偏差的commit外,還有一些其他特性,可以讓我無縫高效實現一些git高級操作,比如merge和rebase。
在git中為什么分支操作非常高效而無其他資源負擔的緣故就是因為他不過是個指針。這也是和svn等分支(獨立目錄)的根本區別之一。
Git分支引用詳解
git的版本歷史通過一個的commit往前推進存儲。而分支引用則是相反從后王前引用的,如下圖所示:

從圖上,我們可以看到每一個commit的引用,引用最先指向的是1。通過遍歷每個分支,我們可以看出最后的通常commit是4(然后分支出了branch-1)。
分支引用的其他特性?
有什么東西在這里失蹤了嗎?確定分支是對單個提交的引用,但是當我們進行commit或者在分支內hard reset時候,相應的分支引用會發生什么變化呢?
下面我們實例展示一下,假設我們給branch-1做一個commit:
git checkout branch-1 echo "## 添加新的行" >> README.md git add README.md git commit -m "添加一個新行"
然我們用交互方式解釋一下以上各個命令:
"跳到branch-1分支"
"向README.md文件添加一行,內容為markdown二號標題添加新的行"
"將這些變化放入暫存區域"
"目前branch-1分支,提交變化給branch-1,並將引用指向新的commit"
另一方面,關於hard reset的操作則更容易:
git checkout branch-1 git reset --hard HEAD~1
這更容易解釋:
"我們在branch-1分支中,取消當前的提交,把引用指向前一個commit"。
Rebase操作
Rebase是我們在需要改變提交歷史時候用的操作,主要在以下兩種情況時候用:
1、改寫commit信息,增加squash,刪除或重新組織commit。當有意地將偏差點指向分支引用,則可以改變整個分支的歷史。
2、更改分支開始點,偏離點。改變后,整個分支提交歷史和偏差點都會改變,因為起點都變了。
下面我們以下圖的一個repo圖為實例,做操作演示

改寫branch-1分支的基礎
我們以最簡單的場景開始,改變我們分支的基礎。我們的目標是使branch-1從主分支的最新提交(master/commit 8)開始。
為此,我們只需要指定一個commit或分支引用,然后分支的所有commit將被重新應用在這個提交的基礎上。
git log --pretty --graph --oneline --all # 首先我們用git log命令看下當前的提交歷史 git checkout brach-1 # 切換到分支1 git rebase master # 以master為基礎rebase


需要手動解決下沖突。
git rebase -continue # 繼續
git log --pretty --graph --oneline --all # rebase后的提交歷史

可以對比,rebase后可以看到comit 5和7的哈希值變了。因為rebase后commit 5的祖先發生了變化,並且由於提交是不可變的,所以我們必須創建一個新提交,同樣適用於以下提交。
改寫分支歷史
我們再實例展示下改變brach-1分支的提交歷史。
假設我們要重命名commit 5的注釋為"chongchong"。因為要更改特定的commit,我們必須使用交互模式。在交互模式中,我們為分支設置基礎(commit 4)並讓它保持不變,並在我們的文本編輯器中觸發提示,以選擇我們想要基礎。
那么讓我們來做一下互動式rebase:
git checkout brach-1
git rebase -i 5b64297

在這個例子中,我們將使用reword命令,其他各個命令建議自己測試練習。
r 5b64297 5
pick 369b38f 7
提交修改改的兩行,然后用
git commit --amend
在編輯器中輸入我們要修改的新commit注釋,本例中為 "chongchong"
chongchong

git rebase -continue
完成rebase過程。
git log查看歷史,結果如下:

我們通過上圖可以看到,每一次改變都相當於新建一次commit,而且以此為基礎繼承於它之后commit都會改變(本例的commit 7哈希變化了)。
Merge操作
Merge與rebase操作的處理方式完全不同,實際上merge比rebase更簡單。假設,我們想要想在分支branch-1添加master中的變化(6,8)變化,不用rebase提交歷史變為7結束。這種情況下使用merge,而在我們的日常實踐中也是merge更常用些。
merge有兩種方法:快進和非快進。快進是將分支的指針移動到我們想要合並的分支。快進方法有時候會不能用,比如我們上面的列子中就不能將branch-1移動到master提交,因為這樣會導致commit 5和7丟失。當然如果已經rebase分支到主分支頂部的,則可以用將快進方法將branch-1合並到主分支。
合並操作也很簡單,我們繼續前面的例子,實例展示:
git checkout master
git merge branch-1 -ff
快速合並之后的git圖形

讓我們回到我們初始時候git記錄中

讓我們使用快速merge 和並master合並branch-1中:

git merge master --ff-only

如預期的那樣是無法快進合並。我們用一般方法合並:
git merge master
git log --pretty --graph --oneline -all

如上圖所示,改操作創建了一個新的commit(68476ab),這個提交很特別,它有2個祖先(4和8),它被稱為合並提交。
現在我們的git歷史圖示將如下所示:
Merge vs Rebase
當我們想要反映另一個分支的變化時,我們是使用rebase還是merge呢?這個問題沒有統一的概念,按照個人喜好以及具體的工作場景:
如果你工作在公共分支中,你和基友們在同時協同工作,那么就使用merge,因為它以時間順序保留了所有的commit,所有的變更都很清晰並將新變化最后疊加到最后一次提交的頂部。
如果你在你保持在你特有分支中工作,rebase會保持你的線性歷史,然后, 當將你的分支合並的時候,可以保持你的master更加清晰,簡練。


