前端架構師的 git 功力,你有幾成火候?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文從前端工程,團隊協作,生產部署的角度,介紹架構人員需要掌握的 git 實踐能力。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"大綱預覽","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文介紹的內容包括以下方面:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分支管理策略","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"commit 規範與提交驗證","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"誤操作的撤回方案","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Tag 與生產環境","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"永久杜絕 443 Timeout","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"hook 實現部署?","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"終極應用: CI/CD","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"分支管理策略","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"git 分支強大的同時也非常靈活,如果沒有一個好的分支管理策略,團隊人員隨意合併推送,就會造成分支混亂,各種覆蓋,衝突,丟失等問題。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前最流行的分支管理策略,也稱工作流(Workflow),主要包含三種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Git Flow","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GitHub Flow","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GitLab Flow","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我司前端團隊結合實際情況,制定出自己的一套分支管理策略。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們將分支分爲 4 個大類:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dev-*","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"develop","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"staging","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"release","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"dev-*","attrs":{}}],"attrs":{}},{"type":"text","text":" 是一組開發分支的統稱,包括個人分支,模塊分支,修復分支等,團隊開發人員在這組分支上進行開發。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開發前,先通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"merge","attrs":{}}],"attrs":{}},{"type":"text","text":" 合併 develop 分支的最新代碼;開發完成後,必須通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"cherry-pick","attrs":{}}],"attrs":{}},{"type":"text","text":" 合併回 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"develop","attrs":{}}],"attrs":{}},{"type":"text","text":" 分支。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"develop","attrs":{}}],"attrs":{}},{"type":"text","text":" 是一個單獨分支,對應開發環境,保留最新的完整的開發代碼。它只接受 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"cherry-pick","attrs":{}}],"attrs":{}},{"type":"text","text":" 的合併,不允許使用 merge。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"staging","attrs":{}}],"attrs":{}},{"type":"text","text":" 分支對應測試環境。當 develop 分支有更新並且準備發佈測試時,staging 要通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"rebase","attrs":{}}],"attrs":{}},{"type":"text","text":" 合併 develop 分支,然後將最新代碼發佈到測試服務器,供測試人員測試。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試發現問題後,再走 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dev-* -> develop -> staging","attrs":{}},{"type":"text","text":" 的流程,直到測試通過。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"release","attrs":{}}],"attrs":{}},{"type":"text","text":" 則表示生產環境。release 分支的最新提交永遠與線上生產環境代碼保持同步,也就是說,release 分支是隨時可發佈的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當 staging 測試通過後,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"release","attrs":{}}],"attrs":{}},{"type":"text","text":" 分支通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"rebase","attrs":{}}],"attrs":{}},{"type":"text","text":" 合併 staging 分支,然後將最新代碼發佈到生產服務器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總結下合併規則:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"develop -> (merge) -> dev-*","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dev-* -> (cherry-pick) -> develop","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"develop -> (rebase) -> staging","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"staging -> (rebase) -> release","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"爲什麼合併到 develop 必須用 cherry-pick?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 merge 合併,如果有衝突,會產生分叉;","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dev-*","attrs":{}}],"attrs":{}},{"type":"text","text":" 分支多而雜,直接 merge 到 develop 會產生錯綜複雜的分叉,難以理清提交進度。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而 cherry-pick 只將需要的 commit 合併到 develop 分支上,且不會產生分叉,使 git 提交圖譜(git graph)永遠保持一條直線。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再有,模塊開發分支完成後,需要將多個 commit 合爲一個 commit,再合併到 develop 分支,避免了多餘的 commit,這也是不用 merge 的原因之一。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"爲什麼合併到 staging/release 必須用 rebase?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rebase 譯爲變基,合併同樣不會產生分叉。當 develop 更新了許多功能,要合併到 staging 測試,不可能用 cherry-pick 一個一個把 commit 合併過去。因此要通過 rebase 一次性合併過去,並且保證了 staging 與 develop 完全同步。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"release 也一樣,測試通過後,用 rebase 一次性將 staging 合併過去,同樣保證了 staging 與 release 完全同步。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"commit 規範與提交驗證","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"commit 規範是指 git commit 時填寫的描述信息,要符合統一規範。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"試想,如果團隊成員的 commit 是隨意填寫的,在協作開發和 review 代碼時,其他人根本不知道這個 commit 是完成了什麼功能,或是修復了什麼 Bug,很難把控進度。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了直觀的看出 commit 的更新內容,開發者社區誕生了一種規範,將 commit 按照功能劃分,加一些固定前綴,比如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"fix:","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"feat:","attrs":{}}],"attrs":{}},{"type":"text","text":",用來標記這個 commit 主要做了什麼事情。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前主流的前綴包括以下部分:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"build","attrs":{}}],"attrs":{}},{"type":"text","text":":表示構建,發佈版本可用這個","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ci","attrs":{}}],"attrs":{}},{"type":"text","text":":更新 CI/CD 等自動化配置","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"chore","attrs":{}}],"attrs":{}},{"type":"text","text":":雜項,其他更改","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"docs","attrs":{}}],"attrs":{}},{"type":"text","text":":更新文檔","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"feat","attrs":{}}],"attrs":{}},{"type":"text","text":":常用,表示新增功能","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"fix","attrs":{}}],"attrs":{}},{"type":"text","text":":常用:表示修復 bug","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"perf","attrs":{}}],"attrs":{}},{"type":"text","text":":性能優化","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"refactor","attrs":{}}],"attrs":{}},{"type":"text","text":":重構","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"revert","attrs":{}}],"attrs":{}},{"type":"text","text":":代碼回滾","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"style","attrs":{}}],"attrs":{}},{"type":"text","text":":樣式更改","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"test","attrs":{}}],"attrs":{}},{"type":"text","text":":單元測試更改","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些前綴每次提交都要寫,剛開始很多人還是記不住的。這裏推薦一個非常好用的工具,可以自動生成前綴。地址在","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/commitizen/cz-conventional-changelog","title":"","type":null},"content":[{"type":"text","text":"這裏","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先全局安裝:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"sh"},"content":[{"type":"text","text":"npm install -g commitizen cz-conventional-changelog\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"~/.czrc","attrs":{}}],"attrs":{}},{"type":"text","text":" 文件,寫入如下內容:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"{ \"path\": \"cz-conventional-changelog\" }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在可以用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git cz","attrs":{}}],"attrs":{}},{"type":"text","text":" 命令來代替 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git commit","attrs":{}}],"attrs":{}},{"type":"text","text":" 命令,效果如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/04/046a49d4e04f2e454350dfddc8c97cfa.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後上下箭選擇前綴,根據提示即可方便的創建符合規範的提交。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了規範之後,光靠人的自覺遵守是不行的,還要在流程上對提交信息進行校驗。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候,我們要用到一個新東西 —— ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git hook","attrs":{}}],"attrs":{}},{"type":"text","text":",也就是 git 鉤子。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"git hook 的作用是在 git 動作發生前後觸發自定義腳本。這些動作包括提交,合併,推送等,我們可以利用這些鉤子在 git 流程的各個環節實現自己的業務邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"git hook 分爲客戶端 hook 和服務端 hook。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端 hook 主要有四個:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"pre-commit","attrs":{}}],"attrs":{}},{"type":"text","text":":提交信息前運行,可檢查暫存區的代碼","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"prepare-commit-msg","attrs":{}}],"attrs":{}},{"type":"text","text":":不常用","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"commit-msg","attrs":{}}],"attrs":{}},{"type":"text","text":":非常重要,檢查提交信息就用這個鉤子","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"post-commit","attrs":{}}],"attrs":{}},{"type":"text","text":":提交完成後運行","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務端 hook 包括:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"pre-receive","attrs":{}}],"attrs":{}},{"type":"text","text":":非常重要,推送前的各種檢查都在這","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"post-receive","attrs":{}}],"attrs":{}},{"type":"text","text":":不常用","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"update","attrs":{}}],"attrs":{}},{"type":"text","text":":不常用","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數團隊是在客戶端做校驗,所以我們用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"commit-msg","attrs":{}}],"attrs":{}},{"type":"text","text":" 鉤子在客戶端對 commit 信息做校驗。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幸運的是,不需要我們手動去寫校驗邏輯,社區有成熟的方案:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"husky + commitlint","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"husky 是創建 git 客戶端鉤子的神器,commitlint 是校驗 commit 信息是否符合上述規範。兩者配合,可以阻止創建不符合 commit 規範的提交,從源頭保證提交的規範。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"husky + commitlint 的具體使用方法請看","attrs":{}},{"type":"link","attrs":{"href":"https://book.ruims.top/git/practice/husky.html","title":"","type":null},"content":[{"type":"text","text":"這裏","attrs":{}}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"誤操作的撤回方案","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開發中頻繁使用 git 拉取推送代碼,難免會有誤操作。這個時候不要慌,git 支持絕大多數場景的撤回方案,我們來總結一下。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"撤回主要是兩個命令:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"reset","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"revert","attrs":{}}],"attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"git reset","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"reset 命令的原理是根據 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"commitId","attrs":{}}],"attrs":{}},{"type":"text","text":" 來恢復版本。因爲每次提交都會生成一個 commitId,所以說 reset 可以幫你恢復到歷史的任何一個版本。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的版本和提交是一個意思,一個 commitId 就是一個版本","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"reset 命令格式如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git reset [option] [commitId]\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如,要撤回到某一次提交,命令是這樣:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git reset --hard cc7b5be\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的命令,commitId 是如何獲取的?很簡單,用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git log","attrs":{}}],"attrs":{}},{"type":"text","text":" 命令查看提交記錄,可以看到 commitId 值,這個值很長,我們取前 7 位即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的 option 用的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"--hard","attrs":{}}],"attrs":{}},{"type":"text","text":",其實共有 3 個值,具體含義如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"--hard","attrs":{}}],"attrs":{}},{"type":"text","text":":撤銷 commit,撤銷 add,刪除工作區改動代碼","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"--mixed","attrs":{}}],"attrs":{}},{"type":"text","text":":默認參數。撤銷 commit,撤銷 add,還原工作區改動代碼","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"--soft","attrs":{}}],"attrs":{}},{"type":"text","text":":撤銷 commit,不撤銷 add,還原工作區改動代碼","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏要格外注意 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"--hard","attrs":{}}],"attrs":{}},{"type":"text","text":",使用這個參數恢復會刪除工作區代碼。也就是說,如果你的項目中有未提交的代碼,使用該參數會直接刪除掉,不可恢復,慎重啊!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了使用 commitId 恢復,git reset 還提供了恢復到上一次提交的快捷方式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git reset --soft HEAD^\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"HEAD^","attrs":{}}],"attrs":{}},{"type":"text","text":" 表示上一個提交,可多次使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實平日開發中最多的誤操作是這樣:剛剛提交完,突然發現了問題,比如提交信息沒寫好,或者代碼更改有遺漏,這時需要撤回到上次提交,修改代碼,然後重新提交。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個流程大致是這樣的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"# 1. 回退到上次提交\n$ git reset HEAD^\n# 2. 修改代碼...\n...\n# 3. 加入暫存\n$ git add .\n# 4. 重新提交\n$ git commit -m 'fix: ***'\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對這個流程,git 還提供了一個更便捷的方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git commit --amend\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個命令會直接修改當前的提交信息。如果代碼有更改,先執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git add","attrs":{}}],"attrs":{}},{"type":"text","text":",然後再執行這個命令,比上述的流程更快捷更方便。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"reset 還有一個非常重要的特性,就是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"真正的後退一個版本","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"什麼意思呢?比如說當前提交,你已經推送到了遠程倉庫;現在你用 reset 撤回了一次提交,此時本地 git 倉庫要落後於遠程倉庫一個版本。此時你再 push,遠程倉庫會拒絕,要求你先 pull。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你需要遠程倉庫也後退版本,就需要 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-f","attrs":{}}],"attrs":{}},{"type":"text","text":" 參數,強制推送,這時本地代碼會覆蓋遠程代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-f","attrs":{}}],"attrs":{}},{"type":"text","text":" 參數非常危險!如果你對 git 原理和命令行不是非常熟悉,切記不要用這個參數。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那撤回上一個版本的代碼,怎麼同步到遠程更安全呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方案就是下面要說的第二個命令:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git revert","attrs":{}}],"attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"git revert","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"revert 與 reset 的作用一樣,都是恢復版本,但是它們兩的實現方式不同。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單來說,reset 直接恢復到上一個提交,工作區代碼自然也是上一個提交的代碼;而 revert 是新增一個提交,但是這個提交是使用上一個提交的代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,它們兩恢復後的代碼是一致的,區別是一個新增提交(revert),一個回退提交(reset)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正因爲 revert 永遠是在新增提交,因此本地倉庫版本永遠不可能落後於遠程倉庫,可以直接推送到遠程倉庫,故而解決了 reset 後推送需要加 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-f","attrs":{}}],"attrs":{}},{"type":"text","text":" 參數的問題,提高了安全性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說完了原理,我們再看一下使用方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git revert -n [commitId]\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"掌握了原理使用就很簡單,只要一個 commitId 就可以了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Tag 與生產環境","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"git 支持對於歷史的某個提交,打一個 tag 標籤,常用於標識重要的版本更新。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前普遍的做法是,用 tag 來表示生產環境的版本。當最新的提交通過測試,準備發佈之時,我們就可以創建一個 tag,表示要發佈的生產環境版本。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如我要發一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"v1.2.4","attrs":{}}],"attrs":{}},{"type":"text","text":" 的版本:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git tag -a v1.2.4 -m \"my version 1.2.4\"\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後可以查看:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git show v1.2.4\n\n> tag v1.2.4\nTagger: ruims <[email protected]>\nDate: Sun Sep 26 10:24:30 2021 +0800\n\nmy version 1.2.4\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後用 git push 將 tag 推到遠程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git push origin v1.2.4\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"這裏注意","attrs":{}},{"type":"text","text":":tag 和在哪個分支創建是沒有關係的,tag 只是提交的別名。因此 commit 的能力 tag 均可使用,比如上面說的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git reset","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"git revert","attrs":{}}],"attrs":{}},{"type":"text","text":" 命令。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當生產環境出問題,需要版本回退時,可以這樣:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git revert [pre-tag]\n# 若上一個版本是 v1.2.3,則:\n$ git revert v1.2.3\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在頻繁更新,commit 數量龐大的倉庫裏,用 tag 標識版本顯然更清爽,可讀性更佳。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再換一個角度思考 tag 的用處。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面分支管理策略的部分說過,release 分支與生產環境代碼同步。在 CI/CD(下面會講到)持續部署的流程中,我們是監聽 release 分支的推送然後觸發自動構建。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那是不是也可以監聽 tag 推送再觸發自動構建,這樣版本更新的直觀性是不是更好?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"諸多用處,還待大家思考。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"永久杜絕 443 Timeout","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們團隊內部的代碼倉庫是 GitHub,衆所周知的原因,GitHub 拉取和推送的速度非常慢,甚至直接報錯:443 Timeout。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們開始的方案是,全員開啓 VPN。雖然大多時候速度不錯,但是確實有偶爾的一個小時,甚至一天,代碼死活推不上去,嚴重影響開發進度。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後來突然想到,速度慢超時是因爲被牆,比如 GitHub 首頁打不開。再究其根源,被牆的是訪問網站時的 http 或 https 協議,那麼其他協議是不是就不會有牆的情況?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"想到就做。我們發現 GitHub 除了默認的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"https","attrs":{}}],"attrs":{}},{"type":"text","text":" 協議,還支持 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ssh","attrs":{}}],"attrs":{}},{"type":"text","text":" 協議。於是準備嘗試一下使用 ssh 協議克隆代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用 ssh 協議比較麻煩的一點,是要配置免密登錄,否則每次 pull/push 時都要輸入賬號密碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GitHub 配置 SSH 的官方文檔在","attrs":{}},{"type":"link","attrs":{"href":"https://docs.github.com/en/authentication/connecting-to-github-with-ssh","title":"","type":null},"content":[{"type":"text","text":"這裏","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"英文喫力的同學,可以看","attrs":{}},{"type":"link","attrs":{"href":"https://book.ruims.top/server/shell/ssh-nopass.html","title":"","type":null},"content":[{"type":"text","text":"這裏","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總之,生成公鑰後,打開 GitHub 首頁,點 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Account -> Settings -> SSH and GPG keys -> Add SSH key","attrs":{}},{"type":"text","text":",然後將公鑰粘貼進去即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們用 ssh 協議克隆代碼,例子如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ git clone [email protected]:[organi-name]/[project-name]\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發現瞬間克隆下來了!再測幾次 pull/push,速度飛起!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不管你用哪個代碼管理平臺,如果遇到 443 Timeout 問題,請試試 ssh 協議!","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"hook 實現部署?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"利用 git hook 實現部署,應該是 hook 的高級應用了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在有很多工具,比如 GitHub,GitLab,都提供了持續集成功能,也就是監聽某一分支推送,然後觸發自動構建,並自動部署。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實,不管這些工具有多少花樣,核心的功能(監聽和構建)還是由 git 提供。只不過在覈心功能上做了與自家平臺更好的融合。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們今天就拋開這些工具,追本溯源,使用純 git 實現一個 react 項目的自動部署。掌握了這套核心邏輯,其他任何平臺的持續部署也就沒那麼神祕了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於這一部分內容較多,所以單獨拆出去一篇文章,地址如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/1d83e84ade4ec25a75a2b4213","title":"","type":null},"content":[{"type":"text","text":"純 Git 實現前端 CI/CD","attrs":{}}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"終極應用: CI/CD","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的一些地方也提到了持續集成,持續部署這些字眼,現在,千呼萬喚始出來,主角正式登場了!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以這麼說,上面寫到的所有規範規則,都是爲了更好的設計和實現這個主角 ——— CI/CD。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先了解一下,什麼是 CI/CD ?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"核心概念,CI(Continuous Integration)譯爲持續集成,CD 包括兩部分,持續交付(Continuous Delivery)和持續部署(Continuous Deployment)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從全局看,CI/CD 是一種通過","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"自動化流程","attrs":{}},{"type":"text","text":"來頻繁向客戶交付應用的方法。這個流程貫穿了應用的集成,測試,交付和部署的整個生命週期,統稱爲 “CI/CD 管道”。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然都是像流水線一樣自動化的管道,但是 CI 和 CD 各有分工。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"持續集成","attrs":{}},{"type":"text","text":"是頻繁地將代碼集成到主幹分支。當新代碼提交,會自動執行構建、測試,測試通過則自動合併到主幹分支,實現了產品快速迭代的同時保持高質量。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"持續交付","attrs":{}},{"type":"text","text":"是頻繁地將軟件的新版本,交付給質量團隊或者用戶,以供評審。評審通過則可以發佈生產環境。持續交付要求代碼(某個分支的最新提交)是隨時可發佈的狀態。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"持續部署","attrs":{}},{"type":"text","text":"是代碼通過評審後,自動部署到生產環境。持續部署要求代碼(某個分支的最新提交)是隨時可部署的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"持續部署與持續交付的唯一區別,就是部署到生產環境這一步,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"是否是自動化","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"部署自動化,看似是小小的一步,但是在實踐過程中你會發現,這反而是 CI/CD 流水線中最難落實的一環。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼?首先,從持續集成到持續交付,這些個環節都是由開發團隊實施的。我們通過團隊內部協作,產出了新版本的待發布的應用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而將應用部署到服務器,這是運維團隊的工作。我們要實現部署,就要與運維團隊溝通,然而開發同學不瞭解服務器,運維同學不瞭解代碼,溝通起來困難重重。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再有,運維是手動部署,我們要實現自動部署,就要有服務器權限,與服務器交互。這也是個大問題,因爲運維團隊一定會顧慮安全問題,因而推動起來節節受阻。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前社區成熟的 CI/CD 方案有很多,比如老牌的 ","attrs":{}},{"type":"link","attrs":{"href":"https://www.jenkins.io/","title":"","type":null},"content":[{"type":"text","text":"jenkins","attrs":{}}]},{"type":"text","text":",react 使用的 ","attrs":{}},{"type":"link","attrs":{"href":"https://circleci.com/","title":"","type":null},"content":[{"type":"text","text":"circleci","attrs":{}}]},{"type":"text","text":",還有我認爲最好用的","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/features/actions","title":"","type":null},"content":[{"type":"text","text":"GitHub Action","attrs":{}}]},{"type":"text","text":"等,我們可以將這些方案接入到自己的系統當中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果喜歡我的文章,請點贊支持我吧!感謝🙏🙏","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章