###### 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
...
::: warning
需討論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 (重構)
```
::: info
"#" 開頭為註解, 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
``` python =
#!/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)
```
* 使用 commitlint
* https://github.com/conventional-changelog/commitlint
* 使用validate-commit-msg
https://github.com/conventional-changelog-archived-repos/validate-commit-msg
### 自動生成 changelog file
若 Commit 符合 [Angular 所定義的格式](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#),可使用以下工具自動產生CHANGELOG.md:
:::info
==Angular Format:==
``` =
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
:::
* 使用 conventional-changelog
* https://github.com/conventional-changelog/conventional-changelog
* 使用 cz-conventional-changelog
* https://github.com/commitizen/cz-conventional-changelog
### `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
### 參考資料
* https://wadehuanglearning.blogspot.com/2019/05/commit-commit-commit-why-what-commit.html
* https://allen-hsu.github.io/2017/07/02/git-message-template-and-githook/
* https://backlog.com/blog/git-commit-messages-bold-daring/
* https://blog.louie.lu/2017/03/21/如何寫一個-git-commit-message
* http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html
* 约定式提交
https://www.conventionalcommits.org/zh-hans/v1.0.0-beta.4/#%e7%ba%a6%e5%ae%9a%e5%bc%8f%e6%8f%90%e4%ba%a4%e8%a7%84%e8%8c%83
* Git Commit Message 這樣寫會更好,替專案引入規範與範例
[https://wadehuanglearning.blogspot.com/2019/05/commit-commit-commit-why-what-commit.html](https://wadehuanglearning.blogspot.com/2019/05/commit-commit-commit-why-what-commit.html)
## 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):
::: info
快轉合併,由於Git 有時間軸的概念,通常要合併的commit 節點建立時間都比你現在來的晚,Git 提供fast-forward 的功能,讓你可以快轉到你要合併的commit,並以該commit 為base ,在後面再建立一個新的commit.
> Note:
==fast-forward 你可以把他想成撥放器,當你要看某個時間的片段(commit),你需要執行快轉的動作。或是你可以想像你目前的commit 跟欲合併的commit 是兩個平時時空,當你要進入他的世界,你需要把時間/時空調成跟他一樣,這樣才能看的到它,並跟他手牽手 ~~做愛做的事~~ 做該做的事 :smile:==
:::
圖示說明:

#### none fast-forward merge(-no-ff) :
::: info
執行 Merge 時會自動產生一個 Merge 的 commit 並產生分支支線圖(小耳朵).
:::
圖示說明:

#### 建議事項:
==對於需要經常性同步的 Repository 建議使用 fast-forward merge 的方式==
::: info
需要經常性同步的 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
```
::: info
設定預設值為 rebase 的方法:
* 方法1. 指定某個branch 執行 git pull 時預設為 rebase 模式 (git version >= 1.7.9)
``` git
git config --global branch.<name>.rebase true
```
``` git
git config --global pull.rebase true
```
如果在master執行 git config --global pull.rebase true .git/config 設定檔將會產生:
``` config=
[branch "master"]
remote = origin
merge = refs/heads/master
rebase = true
```
* 方法2. 所有 branch 預設執行 git pull 時自動執行rebase (git version >= 1.7.9)
``` git
git config --global branch.autosetuprebase always
```
:::
2. 使用 git fetch + git merge
``` =
git fetch origin/master
git merge -ff origin/master
```
3. 使用 Smartgit
* 按下左上方的 Pull 按鈕

* 預設為rebase mode

### 什麼時候要用 none fast-forward merge ?
::: info
通常用在開發一個複雜的獨立新功能,需要比較長的時間開發,不從主要分支做同步更新(各走各的路,個是平行時空)。當這個分支開發完成,需要被合併的到主要的分支的時候,建議使用 none fast-forward merge(-no-ff) 來產生線圖,較容易分辨是從主要分支的哪的節點分出來的且產生一個合併的commit 及容易區分相關性.
:::
### 合併衝突 (Merge conflict)
:::info
> 在執行合併(Merge)時,不外乎會發生程式碼衝突(conflict),請參考以下資訊:
>[merge時衝突(Conflict)操作參考](https://max_chen.gitlab.io/knowledgebase/Git/data/Base/GITMerge-Conflict//)
:::
### 參考資料
https://ihower.tw/blog/archives/3843
[Git 分支合併](https://max_chen.gitlab.io/knowledgebase/Git/data/Base/GITMerge-branchMerge/)
## 3. Git workflow
### 分支策略(效仿[Git flow Script](https://max_chen.gitlab.io/knowledgebase/Git/data/Base/GitWorkflow-gitflow/)的策略)
* 1. Develop 為開發中之版本,永不穩定。所有 Feature 開發都從這分支出去,完成後在 merge回來
* 2. Master 為發行版本,穩定版分支。Commit 只存在 develop 和 Release 的 merge commit (永遠處在 production-ready 狀態,除非重大 bug,則會分出 hotfix 分支)
Ref:
[Git flow Script](https://max_chen.gitlab.io/knowledgebase/Git/data/Base/GitWorkflow-gitflow/)
### 操作參考:
#### 更新 Source Code.
==使用 git pull --rebase==
* 1. 若你目前修改還未commit 建議您先將worktree 有異動的檔案先執行Stash
>[Stash 操作參考](https://max_chen.gitlab.io/knowledgebase/Git/data/Base/GITStash-Stash/)
* 2. 若當下你已有數個commit節點,你需要同步code 這時你執行git pull --rebase 若有衝突需確認哪個版本才是正確的(git 不會知道你要用哪個版本)
>[merge時衝突(Conflict)操作參考](https://max_chen.gitlab.io/knowledgebase/Git/data/Base/GITMerge-Conflict//)
#### Develop 開發流程操作
* 方法1. 直接在 Develop 建立修改的 commit
* 方法2(建議使用). 使用 git flow script (切出另一個分支 進行修改)
* git flow init (只需設定一次)
* git flow feature start 新功能分支
* git flow feature finish 新功能分支
>[git flow操作參考](https://max_chen.gitlab.io/knowledgebase/Git/data/Base/GitWorkflow-gitflow/#cofigure-gitflow)
::: info
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.
::: info
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/