章節目錄
1. 基礎篇:
- 為什麼要使用版本控制系統
- Git 分散式版本控制系統的優勢
- Git 安裝和設定
- 瞭解Git儲存庫(Repo)
- 起步 1 – 建立分支和儲存程式碼
- 起步 2 – 瞭解Git歷史記錄
- 起步 3 – 拉取請求 Pull Request 工作機制
Git的版本歷史記錄採用了與傳統集中式版本管理系統完全不同的方式進行組織,在剛開始使用Git的時候我們往往會不知所措,比如看到這樣的歷史記錄。
看到這個七拐八拐的圖形,你可能完全不知道它代表了什麼。其實這正是Git的特別之處,Git之所以能夠實現之前我們所說那些靈活快速的操作,都是因為它採用這種類似鏈路的版本記錄方式。在這一篇中,我們就一起了解一下這個圖形是如何生成的。
這裡,我們要模擬一個常見的開發場景:
- 你正在開發一個網站的某個新功能,按照Git的推薦方式,你建立了feature1分支來承載這個新功能的程式碼變更。
- 當你已經在這個功能上完成幾次程式碼提交的時候,你的專案經理告訴你這個網站現在出現了一個嚴重的線上問題,需要緊急修復。
- 為了能夠快速修復這個線上問題,你不能等待下一個版本釋出,必須在現有線上版本上進行修復。這時你暫停了feature1分支的開發工作,切換回到master分支,從新拉取一個名為hotfix的分支並在這個分支上開始了線上問題的修復。
- 當你完成了修復並測試通過後,你將hotfix分支合併到master以便立即從新釋出線上版本。
- 完成以上工作後,你切換回到feature1分支繼續進行新功能的開發直到新的功能也經過測試可以釋出。
- 最後你將feature1分支也合併回到master分支,釋出到線上環境。
為了演示這個過程,首先我使用 Visual Studio 建立了一個新的Web網站專案,完成了4次提交併推送到了TFS的Git倉庫。
初始狀態
初始的歷史記錄顯示如下:
用簡單的示意圖表示,是這樣的:
如果使用命令列檢視歷史記錄可以鍵入如下命令
>>> git log --oneline --graph
以上的–oneline引數讓git只輸出第一行的註釋資訊,簡化歷史記錄更加可讀;–graph引數用來在命令列中輸出圖形鏈路,現在我們還看不到效果,後面就會有變化。
輸出的結果如下
注:如果你在使用以上命令的過程中遇到亂碼,請參考:常見問題#4
這表示當前的master分支指向版本庫最新的一次提交(C4),同時之前的所有提交都是順序完成的,形成了一個單一主線的鏈路。在TFS的版本記錄檢視上你所看到的圖形一列表示的是同一個意思。
建立feature1分支並提交2次變更
現在,按照我們在上一篇中介紹的方法,建立feature1分支,並在這上面完成2次新的提交。
此時再次使用上面的命令列出歷史記錄,你會看到如下結果:
這個時候你就會有點納悶了,為什麼明明做了分支,歷史記錄仍然是單一的直線呢?不是應該分叉了嗎?看一下示意圖你就明白了。
Git確實記錄了你的分支資訊,只不過它非常聰明的只是修改了feature1所指向的提交而已;這時你的master分支仍然指向C4提交,而feature1分支則指向C6提交了。
以上git log所輸出的資訊中也非常明確的標明瞭這一點,看一下歷史記錄的第1行和第3行括號裡面內容就明白了。
建立hotfix分支並提交另外2次變更
現在那個討厭的專案經理給你下達了新的任務,要修復線上問題。因為線上所部署的正是master分支上的最新版本,所以你需要切換到master分支(也就是C4提交所代表的版本)進行修改。
這時你可以使用以下命令完成hotfix分支的建立
>>> git checkout master >>> git checkout -b hotfix
完成這個操作後,我們在hotfix分支上輸出歷史記錄。
你會看到現在hotfix和master分支同時指向了一個提交記錄(C4),示意圖如下:
現在我們在hotfix分支上新增2個新的變更,並輸出歷史記錄
現在hotfix所指向的提交變成了 C8,如果再看我們的示意圖就可以看到分叉的效果了
但是為什麼git 輸出的歷史記錄中仍然是一條直線呢?這是因為對於master, hotfix和feature1任何一個分支而言,他們的改動對於自己都是單向的唯一鏈路。
合併hotfix分支到master
現在你已經完成了問題修復,可以將程式碼合併到master併發布到線上環境了,讓我們將hotfix分支的程式碼合併到master主幹上。你需要執行下面命令
>>> git checkout master >>> git merge hotfix
這2條命令的意思是回到master分支並將hotfix分支的改動合併進來,因為hotfix就是基於master分支進行的,所以合併會直接完成不會有任何的衝突出現。
這裡出現了一個詞Fast-forward,因為hotfix分支是直接在master分支上完成的,所以git其實僅僅修改了master分支的指向到hotfix的當前指向(也就是C8),輸出一下log你就看得很清楚了。
對比剛才在hotfix上的log輸出,你會看到這裡唯一的變化就是本地master分支的指向移動到了最後一條提交上。你也許會注意到這裡還有一個origin/master的指向仍然在之前的提交之上,這時因為我們的Git庫已經提交到了TFS的中心儲存庫,而這個origin/master所代表的是這個中心儲存庫上的master分支所處的位置,因為我們在以上過程中沒有給中心儲存庫推送過程式碼,所以這個中心儲存庫的master分支仍然指向的是最初的位置。
到這裡你會明白,Git的分支其實就是一個指標而已,通過這個指標對提交的指向,Git可以非常靈活的切換版本。
到了這裡,我們的示意圖就變成了這個樣子了
合併feature1分支到master分支
現在你已經修復了線上問題,可以回到feature1上繼續工作了。假設我們提交C9以後feature1的開發工作也已經完成可以合併了。與剛才合併hotfix的方法一樣,我們只需要返回master分支並執行合併命令
>>> git checkout master >>> git merge feature1
執行的效果如下:
現在如果我們再次在master上輸出歷史記錄,你將看到如下效果
你會注意到git建立了一個新的提交 b5bcb0b 並將這個提交的註釋設定為 Merge branch ‘feature1’,它的意思是我為你建立了一個新的提交,在這個提交裡面儲存了feature1合併進來的程式碼變更。
注意:在執行merge動作的時候出現了3個Auto-merging的資訊,這表明Git自動為你完成了3個檔案的合併,在實際使用中這樣做可能是會出現問題的,因為Git實際上只是根據檔案程式碼行的資訊判斷是否可以完成自動合併,這種合併並不是永遠安全可靠的。這一點我們在進階篇中會特別介紹。
現在,我們的示意圖就變成了這個樣子
最後,我們可以同步程式碼到TFS中心儲存庫,並檢視伺服器上的歷史記錄。你會看到與我們的示意圖非常類似的圖形顯示。
小結
瞭解Git處理歷史記錄的方式對於我們用好Git非常重要,特別是在企業級開發中處理複雜的多版本並行開發的過程中。以上我們提到的幾個可能令你困惑的地方,比如hotfix分支合併的時候Git僅僅移動指標的行為,以及最後合併feature1分支時Git建立用於合併的提交的方式都是我們在日常開發中常見的情況。理解了Git的特殊處理方式有助於你在遇到類似情況的時候作出正確的判斷,並對後面介紹的很多複雜場景更容易理解。
示例程式碼:
以上示例程式碼已經推送到 https://github.com/lean-soft/git-history-demo
參考:
相關文章:
- DevOps文件中心的技術實踐演進
- 微軟研發雲全家桶VSTS登陸中國
- Markdown/reST 文件釋出流水線
- 幾款好用的Git GUI客戶端工具
- 使用 SSH 連線 TFS/VSTS 的GIT倉庫
- GitHub + VSTS 開原始碼雙向同步
請關注微信公眾號 【devopshub】,獲取更多關於DevOps研發運維一體化的資訊