tags: Git

Git 使用建議

小弟之前有花蠻多時間研究 Git 的操作及用法,略有一些心得,提出一些建議供各位參考:

1. Git commit message

Message 撰寫相關資訊建議:

  • 第一行為標題/主題(subject),盡量不要超過 50 個字元,內容簡單一句話帶過但需包含修改重點
    • 標題開頭為類別
    • 標題不以句點結尾
  • 第二行為一個空白行, 空白行分隔標題與內容
  • 第三行為內文,每行建議 72 字以內
  • 內文解釋至少包含 why、how

Format

<type>:<空格><action><空格><subject>
//空一行
<--! 修改內容描述開始 -->
原因(why):

調整項目(how):

<--! 修改內容描述結束 -->
<issue tracking ID>

Type 類別定義

  • Feature
  • Docs
  • UI
  • Barcode
  • FPGA
  • CnvEngine

需討論Fucntion 的分類

Action 定義

  • add (新增;addition)
  • mod (修改;modify)
  • rev (修正;Revise)
  • del (刪除;delete)
  • refactor (重構)

Example:

UI: 修改 Qt 內建 Dialog 物件使用 Page 改寫 原因: 由於虛擬鍵盤的 page scoll 功能只能在page component 使用, 若 Dialog 內建包含 Inputbox/Textfield 物件,開啟鍵盤將會擋住該物件,看不到輸入的數值. 調整項目: 1. 改寫 DialogBase.qml 2. 修改所有引用 DialogBase 物件的qml 檔 issue #1234

建立 reopsitory 共用的 "git commit meassge template" 檔案

vi .gitCommitTemplate/git-commit-template

新增message 內容,例如:

<type>:<空格><action><空格><subject> 原因: 1. 調整項目: 1. # issue #xxxx # type: # Feature # Docs # UI # Barcode # FPGA # CnvEngine # action: # add (新增;addition) # mod (修改;modify) # rev (修正;Revise) # del (刪除;delete) # refactor (重構)

"#" 開頭為註解, git commit時, "#" 開頭那一行不會被當成訊息資訊

Syntax:

git config --global commit.template <.git-commit-template file path>

example:

git config --global commit.template .gitCommitTemplate/git_commit_template

建立 message hooks (具強制性)

  • 使用 Aangular Js 的 git hook script
#!/usr/bin/env python
"""
Git commit hook:
 .git/hooks/commit-msg
 Check commit message according to angularjs guidelines:
  * https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
"""

import sys
import re

valid_commit_types = ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', ]
commit_file = sys.argv[1]
help_address = 'https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#'

with open(commit_file) as commit:
    lines = commit.readlines()
    if len(lines) == 0:
        sys.stderr.write("\nEmpty commit message\n")
        sys.stderr.write("\n - Refer commit guide: %s\n\n" % help_address)
        sys.exit(1)

    # first line
    line = lines[0]
    m = re.search('^(.*)\((.*)\): (.*)$', line)

    if not m or len(m.groups()) != 3:
        sys.stderr.write("\nFirst commit message line (header) does not follow format: type(scope): message\n")
        sys.stderr.write("\n - Refer commit guide: %s\n\n" % help_address)
        sys.exit(1)
    commit_type, commit_scope, commit_message = m.groups()
    if commit_type not in valid_commit_types:
        sys.stderr.write("\nCommit type not in valid ones: %s\n" % ", ".join(valid_commit_types))
        sys.stderr.write("\n - Refer commit guide: %s\n\n" % help_address)
        sys.exit(1)

    if len(lines) > 1 and lines[1].strip():
        sys.stderr.write("\nSecond commit message line must be empty\n")
        sys.stderr.write("\n - Refer commit guide: %s\n\n" % help_address)
        sys.exit(1)

    if len(lines) > 2 and not lines[2].strip():
        sys.stderr.write("\nThird commit message line (body) must not be empty\n")
        sys.stderr.write("\n - Refer commit guide: %s\n\n" % help_address)
        sys.exit(1)

sys.exit(0)

自動生成 changelog file

若 Commit 符合 Angular 所定義的格式,可使用以下工具自動產生CHANGELOG.md:

Angular Format:

<type>(<scope>): <subject> <BLANK LINE> <body> <BLANK LINE> <footer>

commitizen

https://juejin.im/post/5afc5242f265da0b7f44bee4

https://gist.github.com/leohxj/7bc928f60bfa46a3856ddf7c0f91ab98

https://gist.github.com/motin/5896c5b04d039aac48e6a2985d12171b
https://github.com/GoogleChrome/lighthouse/blob/master/.eslintrc.js

參考資料

2. Git 同步更新 source code

Git Pull (git fetch + git merge)

  • git fetch: 從遠端下載最新的commit
  • git merge: 合併你所指定的commit
  • git pull: 做完 fetch 在自動幫你做 merge

Git Merge

git merge 預設提供兩種方式:

  • fast-forward merge(-ff)
  • none fast-forward merge(-no-ff)

fast-forward merge (-ff):

快轉合併,由於Git 有時間軸的概念,通常要合併的commit 節點建立時間都比你現在來的晚,Git 提供fast-forward 的功能,讓你可以快轉到你要合併的commit,並以該commit 為base ,在後面再建立一個新的commit.

Note:
fast-forward 你可以把他想成撥放器,當你要看某個時間的片段(commit),你需要執行快轉的動作。或是你可以想像你目前的commit 跟欲合併的commit 是兩個平時時空,當你要進入他的世界,你需要把時間/時空調成跟他一樣,這樣才能看的到它,並跟他手牽手 做愛做的事 做該做的事 :smile:

圖示說明:

none fast-forward merge(-no-ff) :

執行 Merge 時會自動產生一個 Merge 的 commit 並產生分支支線圖(小耳朵).

圖示說明:

建議事項:

對於需要經常性同步的 Repository 建議使用 fast-forward merge 的方式

需要經常性同步的 Repository並共同維護同一個分支,通常都是 Commit 會有先後順序的問題,看誰先 Commit (先佔先行的概念),後面同步code 時,需要做merge 來更新,這個case 個人覺得 none fast-forward merge 就不是很恰當,同一個檔案一直被修改及合併,查看log 將會變得複雜,之後要用 git blame 查詢某個檔案的修改歷程,將會看到一的大堆merge 的 commit,於 trace Bug 的時候可能會造成一些困擾.

Example:

建議操作方式:

  1. 使用 git pull

後面帶參數 rebase

git pull --rebase

設定預設值為 rebase 的方法:

  • 方法1. 指定某個branch 執行 git pull 時預設為 rebase 模式 (git version >= 1.7.9)
git config --global branch.<name>.rebase true
git config --global pull.rebase true

如果在master執行 git config global pull.rebase true .git/config 設定檔將會產生:

[branch "master"] remote = origin merge = refs/heads/master rebase = true
  • 方法2. 所有 branch 預設執行 git pull 時自動執行rebase (git version >= 1.7.9)
git config --global branch.autosetuprebase always
  1. 使用 git fetch + git merge
git fetch origin/master git merge -ff origin/master
  1. 使用 Smartgit
  • 按下左上方的 Pull 按鈕

  • 預設為rebase mode

什麼時候要用 none fast-forward merge ?

通常用在開發一個複雜的獨立新功能,需要比較長的時間開發,不從主要分支做同步更新(各走各的路,個是平行時空)。當這個分支開發完成,需要被合併的到主要的分支的時候,建議使用 none fast-forward merge(-no-ff) 來產生線圖,較容易分辨是從主要分支的哪的節點分出來的且產生一個合併的commit 及容易區分相關性.

合併衝突 (Merge conflict)

在執行合併(Merge)時,不外乎會發生程式碼衝突(conflict),請參考以下資訊:
merge時衝突(Conflict)操作參考

參考資料

https://ihower.tw/blog/archives/3843

Git 分支合併

3. Git workflow

分支策略(效仿Git flow Script的策略)

    1. Develop 為開發中之版本,永不穩定。所有 Feature 開發都從這分支出去,完成後在 merge回來
    1. Master 為發行版本,穩定版分支。Commit 只存在 develop 和 Release 的 merge commit (永遠處在 production-ready 狀態,除非重大 bug,則會分出 hotfix 分支)

Ref:
Git flow Script

操作參考:

更新 Source Code.

使用 git pull rebase

    1. 若你目前修改還未commit 建議您先將worktree 有異動的檔案先執行Stash

      Stash 操作參考

    1. 若當下你已有數個commit節點,你需要同步code 這時你執行git pull rebase 若有衝突需確認哪個版本才是正確的(git 不會知道你要用哪個版本)

      merge時衝突(Conflict)操作參考

Develop 開發流程操作

  • 方法1. 直接在 Develop 建立修改的 commit

  • 方法2(建議使用). 使用 git flow script (切出另一個分支 進行修改)

    • git flow init (只需設定一次)
    • git flow feature start 新功能分支
    • git flow feature finish 新功能分支

    git flow操作參考

Note:

  1. git 分支名稱可以為中文
  2. 使用 git flow script 的好處:
    • 操作簡單: Start and Finish
    • 由於是切出一個分支(切換另一個時空),所以Develop 分支若有增加 commit,可以自行執行 git pull 更新 Develop 分支,你所處的 "新功能分支" 將不會有影響。等你功能開發完成,你可以執行 finish 的操作將你的修改merge 至 Develop 分支。

4. 團隊共同開發時不要把 Git Server(e.g. Gitlab/GitHub/Gitea),當作程式碼的 FTP.

  1. Push 出去就是詔告天下,在沒有 Code review 機制下,需自行 review 修改的code。
  2. 不要 Push 一個未完成的修改 (可在本地自己一個測試分支之後再merge及rebase 至develop 分支)
  3. Commit 不是寫給自己看的, 也不是 memo

5. 建立git code review 機制

https://hackmd.io/@MaxChen/Codereview

ref:

https://willh.gitbook.io/gitpro/7d615cb13a55ac231e35e8658a897e0e
https://gitbook.tw/interview

https://backlog.com/blog/git-commit-messages-bold-daring/