###### 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:== ::: 圖示說明: ![](https://i.imgur.com/wLKxGNS.png) #### none fast-forward merge(-no-ff) : ::: info 執行 Merge 時會自動產生一個 Merge 的 commit 並產生分支支線圖(小耳朵). ::: 圖示說明: ![](https://i.imgur.com/VCBS6Rv.png) #### 建議事項: ==對於需要經常性同步的 Repository 建議使用 fast-forward merge 的方式== ::: info 需要經常性同步的 Repository並共同維護同一個分支,通常都是 Commit 會有先後順序的問題,看誰先 Commit (先佔先行的概念),後面同步code 時,需要做merge 來更新,這個case 個人覺得 none fast-forward merge 就不是很恰當,同一個檔案一直被修改及合併,查看log 將會變得複雜,之後要用 git blame 查詢某個檔案的修改歷程,將會看到一的大堆merge 的 commit,於 trace Bug 的時候可能會造成一些困擾. ::: Example: ![](https://i.imgur.com/R0W0aqm.png) #### 建議操作方式: 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 按鈕 ![](https://i.imgur.com/r707ySs.png) * 預設為rebase mode ![](https://i.imgur.com/UHtNFve.png) ### 什麼時候要用 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/