Git版本控制、分支策略與代碼評審

Git版本控制、分支策略與代碼評審

Git介紹

  • Git是一種免費的、開源的分佈式版本控制系統(DVCS)
  • Git是目前使用最爲普遍的版本控制系統
  • Git是由Linus Torvalds開發的 (這位IT界的大神同時也是Linux的做者)
  • Git的第一個版本(v0.99)在2005年7月發佈,最新版本是v2.17(截止2018年4月)
  • Git支持Windows, Linux/Unix, Mac
  • 和商業版本控制系統相比,Git免費、開源且更強大、穩定、快速
  • 和傳統版本控制系統(好比CVS, SVN)相比,Git具備支持輕量級分支、分佈式開發、高性能等優勢。
  • Git支持命令行操做,能夠很是方便地集成到CI/CD pipeline中

參考文檔:html

Git工具與代碼託管網站

在Windows上安裝使用Git

登陸Git官網: https://git-scm.com/ 下載Git for Windows,安裝後有3個程序:github

  • Git Bash (模擬Linux終端)
  • Git CMD (Windows終端)
  • Git GUI (Windows界面)

推薦使用Git Bash,Git CMD和Git GUI能夠忽略。web

在Linux/Unix和Mac上使用Git和在Windows上使用Git Bash同樣,再也不贅述。編程

IDE支持Git

如今主流的IDE(好比Intellij Idea和Eclipse) 都對Git有很是好的支持。segmentfault

推薦經過IDE + Git Bash 方法來使用Git。windows

代碼託管網站

如何選擇?bash

  • 土豪本身搭建GitHub Enterprise
  • Atlassian鐵粉用BitBucket
  • 中產用GitLab Enterprise
  • 屌絲本身搭建GitLab CE (我是屌絲我自豪)
  • 最屌絲用國內的代碼託管網站…

Git原理

Git工做流

下圖列出了一個簡明的Git工做流:分佈式

這裏寫圖片描述

Git 分支

概述

  • 分支(branch)能夠理解爲是倉庫(repository)的一種隔離視圖。
  • 分支的目的爲了支持多人、多版本開發。

  • Git支持多分支開發,並鼓勵使用本地多分支,這些分支之間是彼此徹底獨立的。

  • 和傳統的版本控制工具(好比CSV, SVN)相比,Git對分支的隔離是邏輯隔離而不是物理隔離,這意味着建立分支很是快速、存儲分支不會佔用過多磁盤空間
  • 常見的分支操做包括:建立分支、切換分支、合併分支、刪除分支。

參考文檔:

理解如何使用Git分支

這裏以GitHub Flow爲例 來演示常見的分支操做。

打開GitHub Flow ,點擊動畫上的不一樣區域來理解常見的分支操做:

  1. 建立分支

    • 給分支名取一個簡短且有意義的名字
  2. 提交改動

    • 填寫有意義的commit message: what & why

    • 推薦的commit message格式爲 <Item Id>: <Description> ,好比:

      512: 增長了對NFS的支持

  3. 發起合併請求

    • 同時發起code review和討論
    • 不通過review的代碼不容許合併進public branch
  4. 代碼評審和討論

    • 審查代碼是否知足功能、規範、測試結果、測試覆蓋等要求
    • 審查合格才容許合併合進public branch,不合格的代碼退回從新修改
    • 鼓勵使用同行評審(Peer Review)而不是上下級評審,鼓勵積極反饋、互動
    • 評審對事不對人,多建議,不批評,不指責
    • 評審過程對reviewer和代碼提交者雙方都是一個學習提高的過程
  5. 部署

    • 使用通過完整測試的分支來部署到生產環境
    • 部署後發生問題須要回退時,用上一個版本進行回退
  6. 合併回master分支

    • 部署成功,沒有問題後才合併到master分支

GitHub Flow是一種很是好的Git分支策略,但不是惟一的Git分支策略。

在後面的分支策略中會對Git分支策略做深刻探討。

Git經常使用命令

參考文檔

除了查看網上的參考文檔以外,還能夠在Git Bash中執行git --helpgit <command> --help 來查看Git幫助文檔。

Git使用場景

下面列出經常使用的Git使用場景。

場景1: 在GitLab上新建一個repository

若是不想讓公衆都能看到你的代碼,須要將repository設置爲private,並將相應權限授給團隊成員。

強烈推薦爲每一個repository建立README.md和.gitignore文件。

強烈推薦對repository的public branch進行保護,只有經過merge request才能合併代碼進public branch。

場景2:將GitLab上一個repository clone到本地

GitLab支持SSH和HTTP(S)兩種方式。

  • SSH方式須要將本地的SSH public key添加到GitLab帳號的setting中
  • HTTP(S)方式第一次push到GitLab時須要提供用戶名和密碼登錄

設置git config的user.nameuser.email :

# 查看git config
git config --list

# 設置Global的user.name和user.email
git config --global user.name <user_name>
git config --global user.email <user_email>

# 查看git config確認
git config --list

從GitLab上clone一個repository到本地:

# SSH 方式
git clone <repo_git_url>

# HTTP 方式
git clone <repo_http_url>

克隆後的缺省分支是GitLab repository上的缺省分支,通常是master分支。

場景3:建立一個本地分支

Git建立分支時,缺省從當前分支(base branch)來建立,若是當前分支不是想要的base branch,須要先切換分支。

# 切換當前分支(base branch)
git checkout <base_branch>

# 建立一個新分支,並切換到該分支
git checkout -b <new_branch>

場景4:將本地分支的改動push到GitLab

# 更新working directory
git pull

# 改動代碼...此處省略2048個字

# 查看working tree狀態
git status

# 比較改動先後,查看改動了什麼 (用IDE來比較更加直觀)
git diff <file>

# 添加新建的文件到Git Stage中
git add <file>

# 或添加所有新建的文件到Git Stage中
git add .

# 提交改動到local repository
git commit -m "<commit_message>"

# 推送到remote repository,建立remote new branch,並設置upstream reference
git push -u origin <new_branch>

若是第一次push時不經過-u 來push新的branch,在git pull時會提示要先設置upstream reference。

# 設置upstream reference
git branch --set-upstream-to=origin/<branch> <branch>

以後的push能夠不須要再加-u參數:

git push origin <branch>

場景5:用戶A/B同時修改同一個文件,A先提交,B後提交,解決衝突後push到GitLab

此時B在git push 時會遇到錯誤,錯誤信息爲:

error: failed to push some refs to …
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 integrate the remote changes
hint: (e.g., ‘git pull …’) before pushing again.
hint: See the ‘Note about fast-forwards’ in ‘git push –help’ for details.

B執行git pull 試圖自動合併A先前提交的改動,自動合併失敗的狀況下須要手工合併:

Auto-merging …
CONFLICT (content): Merge conflict in …
Automatic merge failed; fix conflicts and then commit the result.

B打開並編輯發生衝突的文件,手工修改後,保存文件。

參照場景4,從新提交完成合並衝突(合併衝突也是一種改動)的文件到GitLab。

場景6:在本地改動後,在git add 前回退

撤銷本地改動:

git checkout -- <file>

推薦使用IDE的revert功能,更加簡單直觀。

Intellij Idea: Version Control -> Local Changes -> Right click the file -> Revert

場景7:在本地改動後,在git push 前回退

用遠程repository強制替換本地文件:

git fetch origin
git reset --hard origin/<branch>

推薦使用IDE的Reset功能,更加簡單直觀。

Intellij Idea: Version Control -> Log -> Right click the last commit marked with remote branch (origin/) -> Reset current branch to here -> Select 「Hard」

場景8:在本地改動後,在git push後回退

方法一:

手工回退後,從新提交改動去覆蓋remote branch。

方法二:

使用IDE的revert功能。

Intellij Idea: Version Control -> Log -> Right click the commit -> Revert, and then commit and push a 「revert commit」 to override both remote and local branches.

方法三:

# 查看git commit log,複製要回退的版本commit id
git log --graph --oneline --decorate --all

# 將本地回退到指定版本
git reset --hard <commit_id>

# 查看working tree狀態,此時本地落後於遠程1個commit
git status

# 強制用本地覆蓋遠程(危險操做!!)
git push -f origin <branch>

# 再次查看working tree狀態,此時本地和遠程應該同步
git status

推薦使用方法二

方法三比較危險,不推薦使用;強烈建議經過protect branch來防止強制用本地覆蓋遠程分支。

場景9:查看文件修改歷史

# 按樹形結構顯示
git log --graph --oneline --decorate --all [file]

# 按列表顯示
git log --pretty=oneline [file]

# 簡單粗暴顯示
git log [file]

推薦使用IDE的查看歷史功能。

Intellij Idea:

  • 查看所有文件修改歷史:Version Control -> Log
  • 查看單個文件修改歷史:Right click the file -> Git -> Show history

場景10:在GitLab上設置分支保護,要求只有經過merge request才能合併代碼

用Admin或Owner或Master帳號登陸,打開指定project -> Settings -> Repository -> Protected Branches

選擇要protect的branch (支持wildcard):

  • 要求每一個人都要經過merge request才能合併代碼進入public branch:選擇」Allowed to merge」爲」Developers + Masters」,選擇」Allow to push」 爲」No one「
  • 對public branch都要設置protect branch

參考文檔:

https://docs.gitlab.com/ee/user/project/protected_branches.html

在我使用的GitLab CE 10.6.4 貌似有一個bug,當選擇」Allowed to
push」爲」Masters「時,Developer角色的用戶仍然能夠push。
這個問題的緣由多是在向gitlab做git push時,讀取的是在Windows上Manage WindowsCredentials中的另外一個較高權限的username,因此gitlab才容許做git push

場景11:本地改動後,push到本地分支,而後在GitLab上發起merge request

參照場景4,建立一個新的本地分支,改動後push到遠程的該分支。

而後在GitLab上發起merge request:

  1. 打開項目,點擊左邊的Merge Requests,選擇New merge request
  2. 選擇Source branch和Target branch
  3. 點擊」Compare branches and continue「
  4. 選擇Assignee (不要選擇Assign to me)
  5. 若是是臨時branch,勾選「Remove source branch when merge request is accepted」
  6. 檢查無誤後,點擊「Submit merge request」按鈕

GitLab CE貌似不能作到防止本身來merge本身的merge request,這一點和GitHub Enterprise仍是有差距的。

因此只能流程上要求你們不要merge本身的merge request。

若是發起merge request後,在該merge request關閉以前,繼續往source branch分支push代碼,這些commit也會被自動包含在該merge request裏。

場景12:代碼審查,經過merge request

  1. 打開項目,點擊左邊的Merge Request,選擇Open查看Open狀態的merge request

  2. 點擊Commit查看詳細信息

  3. 點擊Changes查看具體的改動

  4. 寫comment,並和代碼提交者討論。

  5. 審查經過後,點擊」Merge」按鈕

場景13:代碼審查,拒絕merge request

若是審查不經過,寫清楚comment,並在和代碼提交者討論後,要求其進行修改。

場景14:將本地的一個新項目放在GitLab上

在GitLab上建立一個同名的空project (不包含任何文件),設置好用戶權限。

用Owner/Master角色帳號將本地項目push到GitLab:

# 設置Global的user.name和user.email (若是須要)
git config --global user.name <user_name>
git config --global user.email <user_email>

# 將本地項目轉成Git項目
git init

# 檢查對應的remote repository
git remote -v

# 添加遠程的remote repository
git remote add origin <repo_git_url>

# 確認remote repository
git remote -v

# 推送到remote branch
git status
git add .
git commit -m "Inital commit"
git push -u origin <branch>

在我使用的GitLab CE 10.6.4 有一個bug,當建立好一個空的項目後,用Owner帳號將本地項目做git push,會遇到」You are not allowed to push code to this project「的錯。

這個問題的緣由是在向gitlab做git push時,讀取的是在Windows上Manage Windows Credentials中的另外一個錯誤的username,因此被gitlab拒絕了。

解決的方法是,在Manage Windows Credentials中刪除原來的credentials,做git push時會提示輸入用戶名和密碼,輸入owner的用戶名和密碼後,就能夠git push成功了。

這應該是GitLab的一個設計缺陷,也就是GitLab假設在一個GitLab上,你老是隻用一個帳號來登錄。

參考文檔:https://gitlab.com/gitlab-com/support-forum/issues/207

使用GitHub來學習提高

打開GitHub Explore 來查看GitHub上最流行的項目,也能夠經過搜索來查看好比最流行的Java項目、最流行的JavaScript項目等等。

國內有一些走歪道的程序員上淘寶買GitHub加star服務(鄙視),因此看到一些流行的中文項目時,要持保留態度。

大部分開源軟件都把代碼託管在GitHub上,能夠經過GitHub來clone開源軟件的代碼到本地做源代碼研讀,是提高編程的內功的有效方法。若是想加快clone速度能夠在git clone後加上--depth=1 的參數來只clone最新版本。

你也能夠把你的點子變成一個項目放到GitHub上,分享給開源社區。

分支策略

分支策略就是在哪一個分支上開發、在哪一個分支上部署、分支同步和分支合併的原則。

項目團隊應該遵循統一的分支策略。

現代主流的分支策略包括:

注意:這裏講的分支不包括我的的private branch,無論哪一種分支策略都要求先在private branch開發測試後再發起merge request/pull request, 經過代碼評審後才能合併進入public branch。

主流分支策略比較

下表對上面的三種現代主流的分支策略進行了比較:

這裏寫圖片描述

Trunk based development官網將Trunk based development和一些現代主流的分支策略進行了比較:

https://trunkbaseddevelopment.com/alternative-branching-models/

下面這篇文章也詳細地論述了比較了不一樣的分支策略:

https://www.continuousdeliveryconsulting.com/blog/version-control-strategies/

如何選擇分支策略

分支策略沒有好壞,適合本身的才最重要。

一般的建議是,項目團隊選擇一種比較符合本身團隊的主流分支策略,在實際工做中加以調整,最終發展成符合團隊開發節奏的分支策略。

分支越多,複雜度越大,分支同步與分支合併的成本越大,團隊內部的溝通成本也越大。

選擇分支策略的原則:

  • 始終保持master處在可發佈狀態
  • 始終保持構建處在成功狀態
  • 下降代碼合併的複雜度和風險
  • 下降團隊溝通成本
  • 符合團隊的現狀

代碼評審

前面章節也已經描述瞭如何做代碼評審,這裏再對代碼評審的一些原則做小結:

  • Four eyes check
  • 不要既當運動員,又當裁判員

  • 審查代碼是否知足功能、規範、測試結果、測試覆蓋等等

  • 藉助工具來提高代碼審查的效率
  • 審查合格才容許合進public branch,不合格的代碼退回從新修改
  • 不怕犯錯,可是不要犯重複的錯
  • 提交審查前,本身先檢查一遍
  • 鼓勵使用同行評審(Peer Review)而不是上下級評審,鼓勵積極反饋、互動
  • 評審對事不對人,多建議,不批評
  • 己所不欲,勿施於人
  • 評審過程對reviewer和代碼提交者雙方都是一個學習提高的過程
  • 保持開放的心態,相互學習,共同成長

思考: 什麼文件不該該放在Git上?

你應該把幾乎一切文件都做版本控制,除了:

  • 敏感信息,好比用戶名、密碼、Token等
  • 編譯後產生的二進制文件,好比jar/war包、class文件
  • 測試結果
  • 日誌
  • 臨時文件
  • IDE自動生成的配置文件

  • 空文件夾(若是必定要加入Git,在該目錄下添加一個.gitkeep 文件)

使用.gitignore來讓Git忽略上述的文件和文件夾。

參考文檔: