Git 工做流

本文是我使用 Git 一段時間和看過一些資料後的總結,以及我的看法,深感 Git 的規範使用很是重要,不規範的使用會帶來不少麻煩。html

同類工具比較

SVN與Git

原理上

  1. Git直接記錄文件快照,SVN每次記錄哪些文件做了更新、更新哪些行的內容
  2. Git 有本地倉庫和遠程倉庫,SVN沒有本地倉庫
  3. Git 大多數操做是本地執行,SVN操做幾乎都須要鏈接網絡

操做上

  1. Git提交後保存在本地倉庫,須要推到遠程倉庫;SVN提交後在遠程倉庫
  2. Git有各類「反悔」命令,SVN幾乎沒有
  3. Git有真正的branch,svn的branch實際上是工做空間的副本

Git的使用現狀

  • 意義不明的提交信息

image

  • 糟糕的版本樹圖

image

存在的問題

  • 合併解決衝突容易出錯
  • 回滾時難以經過提交信息尋找回滾版本號
  • 合併點可能看不出這次合併影響的文件

Git 工做流

Gitflow 工做流

Gitflow 爲不一樣的分支分配一個很明確的角色,並定義分支之間如何和何時進行交互。分別有歷史分支、功能分支、發佈分支和維護分支。git

歷史分支

使用兩個分支來記錄項目的歷史。 master分支記錄了正式發佈的歷史,而develop分支做爲功能的集成分支。所以,master分支的每次提交都應分配一個版本號。github

功能分支

功能分支是從developcheckout出來的新分支,每一個功能對應一個分支。shell

1.假設開發a功能:bash

git checkout -b feature-a develop
複製代碼

2.當新功能完成時,合併回develop分支。網絡

git checkout develop
git merge --no-ff feature-a
git push
git branch -d feature-a
複製代碼

發佈分支

1.當develop分支開發到須要發佈時,從develop分支拉出一個發佈分支,命名爲release-*release/*ide

git checkout -b release-0.1 develop
複製代碼

2.該分支用於發佈循環,只作bug修復、文檔生成等面向發佈的任務。新功能再也不添加到這個分支上。 3.一旦發佈完成,把發佈分支mergemaster分支上。svn

git checkout master
git merge --no-ff release-0.1
git push
複製代碼

4.打tag記錄版本號,方便跟蹤每次發佈。工具

git tag -a 0.1 -m "release 0.1 publish" master
git push --tags
複製代碼

5.把這些重新建發佈分支以來作的修改mergedevelop分支。測試

git checkout develop
git merge --no-ff release-0.1
git push 
複製代碼

6.最後刪除發佈分支

git branch -d release-0.1
複製代碼

維護分支/熱修復

當線上版本出現bug時,就須要用到維護分支,它用於快速給產品發佈版本打補丁。

1.從master分支拉一個維護分支(這是惟一從master分支拉出來的分支)。

git checkout -b hotfix master
複製代碼

2.修復完成後,立刻合併回masterdevelop

git checkout master
git merge --no-ff hotfix
git push
git checkout develop
git merge --no-ff hotfix
git push 
git branch -d hotfix
複製代碼

3.master用新版本號打tag

git tag -a 0.2 -m "release 0.2 publish" master
git push --tags
複製代碼

圖解

image
圖片來自網絡

優勢

  • 單個功能獨立開發,並行開發互不干擾
  • masterdevelop分支分別記錄發佈和功能開發的歷史
  • 因爲有發佈分支,其餘暫不發佈的功能的開發不受發佈的影響,能夠繼續提交
  • 維護分支能快速打補丁,不影響正在開發的功能

缺點

  • 複雜,分支繁多
  • Git GUI不支持,純命令行
  • 對開發者要求高(理解工做流,熟悉Git命令)
  • 全部功能分支基於不穩定的develop
  • 須要維護兩個長期分支masterdevelop

GitHub Flow

GitHub 使用的工做流

  1. 全部在master上的東西都是可發佈的(已發佈或立刻發佈)
  2. 開發新功能時,從master拉一個名稱清晰的新分支
  3. 在本地提交到這個分支的同時把它push到遠程倉庫
  4. 當你須要獲得反饋或幫助,或者該分支準備merge時,打開一個pull request
  5. 該分支被review且贊成合併後,合併到master
  6. pushmaster後,應該當即發佈

優勢

  • 操做簡單
  • 主幹的代碼有質量保證

缺點

  • 測試線和正式環境的發佈沒有區分

Git-Develop

develop做爲固定的持續集成和發佈分支

  1. 每個功能都從master拉一個功能分支。
  2. 在這個功能分支上開發,功能完成到發佈時,提交code review,經過後自動合併到develop
  3. 待全部計劃發佈的變動分支代碼都合併到develop後,rebase masterdevelop,完成發佈。
  4. 應用發佈成功後打一個tag
  5. develop分支的發佈版本合併回master

優勢

  • 操做相對簡單
  • 流程稍做改動,便可區分測試線和正式環境
  • 每次開發都基於正式版本最新的代碼(master),和當前開發的其餘分支不產生依賴關係
  • master始終是已發佈狀態

缺點

暫時想不到。。。

Pull Requests

Pull requests不是一種工做流,而是一個能讓開發者更方便地進行協做的功能,能夠在提議的修改合併到正式項目以前對修改進行討論。這種方式對分支的合併有一些限制,例如只有項目維護者有權限合併分支到倉庫中。其工做方式:

  1. 開發者在本地倉庫新建一個功能分支。
  2. 功能完成後,開發者push分支修改到遠程倉庫中。
  3. 開發者發起Pull requests
  4. 團隊成員收到通知,進行code review,討論和修改。
  5. 項目維護者合併功能到倉庫中並關閉Pull Requests

Git使用技巧

git commit --amend

git commit --amend最多見的用法是上次提交信息寫錯,或提交文件多了或漏了之時,從新提交覆蓋上一次提交。

其實它還有一個用法,就是用來合併提交。例如上次提交的修改並不徹底,再做修改以後能夠用該命令把本次提交與上次提交合並在一塊兒。

git reset

在須要回滾一次或屢次提交時,能夠用git reset。因爲該命令比較危險,建議用於已經把最新提交推到遠程倉庫上的本地分支。

  • git reset HEAD [filename]

把已在暫時區的文件取消,恢復到已修改未暫存狀態。

  • git reset HEAD~[n]

git reset後面可帶參數HEAD~[n](n >= 1)。表示回退到n個提交以前。同時,它也能夠用來合併提交。下面的寫法與git commit --amend結果是同樣的。

git reset HEAD~1
git commit
複製代碼

下面的用法則是合併了屢次提交

git reset HEAD~2
git commit
複製代碼
  • git reset [version]

git reset後面也能夠帶版本號,直接回退到指定版本。

  • git reset的三種參數

    1. 使用參數--hard,如git reset --hard [version]會執行如下操做:
      1. 替換引用的指向。引用指向新的提交ID。
      2. 替換暫存區。替換後,暫存區的內容和引用指向的目錄樹一致。
      3. 替換工做區。替換後,工做區的內容變得和暫存區一致,也和HEAD所指向的目錄樹內容相同。
    2. 使用參數--soft,如 git reset --soft [version]會執行上述的操做a。即只更改引用的指向,不改變暫存區和工做區。
    3. 使用參數--mixed或者不使用參數(默認爲--mixed),如 git reset [version]會執行上述的操做a和b。即更改引用的指向及重置暫存區,可是不改變工做區。

git merge --no-ff

--no-ff是不快速合併的意思

與git merge的區別

git merge的結果:

被merge的分支和當前分支在圖形上併爲一條線,被merge的提交點逐一合併到當前分支。

image

git merge --no-ff的結果:

被merge的分支和當前分支不在一條線上,被merge的提交點還在原來的分支上,同時在當前分支上產生一個合併點。

image

git rebase

git rebase通常解釋爲變基,也有解釋爲衍合,我的以爲變基比較容易理解。

與git merge的區別

git merge是把兩個分支的代碼合併到一塊兒,其實git rebase也是相同的做用,可是表現上是兩種不一樣的形式。

例如如今 dev 提交了一次,master 在此以後也提交了一次,兩個分支的狀態以下:

image

  • 提交點順序

能夠看出git merge後,不管加不加--no-ff參數,提交點的順序都和提交的時間順序相同,即 master 的提交在 dev 以後,如圖:

image

git rebase後,順序就變成被rebase的分支(master)全部提交都在前面,進行rebase的分支(dev)提交都在被rebase的分支以後,在同一分支上的提交點仍按時間順序排列,如圖:

image

  • 變基

從上面的圖能夠看出,dev 在rebase master 後,分支發生了變化,本來是兩個分支,rebase的結果看起來是: dev 是基於 master 的分支,且產生了一些新提交。

通常來講,rebase後的 dev 和遠程的origin/dev會發生分離,在命令行界面中會提示:

Your branch and 'origin/dev' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
複製代碼

這時須要用git push -f強制推送,覆蓋遠程分支。若使用了提示中的git pull,結果會變成合並,併產生一個合併提交點。

注:慎用git push -f!

git pull --rebase

注意git pull時請加上--rebase,理由下面會說。

與git pull的區別

在通常狀況下,加與不加--rebase是沒有區別的。然而,結合上面說的git rebase功能,能夠知道某個分支可能與其遠程分支發生分離,而當你pull時,你的本地分支仍是和原來的遠程分支同樣,這時若是使用git pull,則會變成你的本地分支和遠程分支合併,正確的作法是git pull --rebase,纔會拉取到最新的分支。

因此推薦在任什麼時候候pull遠程分支,最好加上--rebase參數。

git reflog

查看提交記錄的命令是git log,而git reflog的功能是查看本地操做記錄,如此一來能夠看到本地的commit, merge, rebase等操做記錄。

6fe46ab HEAD@{0}: rebase finished: returning to refs/heads/dev
6fe46ab HEAD@{1}: rebase: dev modify a
2c92bcb HEAD@{2}: rebase: checkout master
9b26f5d HEAD@{3}: reset: moving to 9b26f5db1e8597b884c45114fbbff36c440da274
5531fc0 HEAD@{4}: merge master: Merge made by the 'recursive' strategy.
9b26f5d HEAD@{5}: checkout: moving from master to dev
複製代碼

Git工具推薦

  • SourceTree
  • GUI Clients

參考資料

Pro Git,Git工具書: http://iissnan.com/progit/html/zh/ch1_5.html

Git 工做流指南,介紹了幾種主流工做流: https://github.com/xirong/my-git/blob/master/git-workflow-tutorial.md

Gitflow 有害論: http://insights.thoughtworkers.org/gitflow-consider-harmful/

GitHub Flow: http://scottchacon.com/2011/08/31/github-flow.html

Google 的「主幹開發」(trunk-based development): http://www.ruanyifeng.com/blog/2016/07/google-monolithic-source-repository.html

相關文章
相關標籤/搜索