前言故事
小唯在一間資訊公司擔任 Android 軟體工程師,負責公司內的主力產品開發,這是一項功能複雜的產品,它累計了數年的程式碼、商務邏輯、出包經驗等的功能與修正。數年後,團隊中正在推動全新 2.0 的產品,以及公司其他服務也即將推出 App。不可避免的,有許多相似功能相繼橫跨各個產品 App,這讓小唯自覺必須有一套更好的方式管理自己團隊的程式碼,改善之後擴充性、維護性,和保護團隊工程師們的肝~
一個從頭打造的產品,功能是不斷的疊加上去,鮮少有團隊能真的從開頭就搞好架構設計,小唯團隊的產品也不例外。除了自身的核心功能外,還使用了圖隊之前開發的功能,以及許多現成 github 的模組,有的已提供 gradle 的安裝與使用,有的卻只是單純的 Java/Kotlin 程式碼。在初期,最直接暴力的方式就是直接抓下來,塞進當前的 App 中使用,累積到今時今日,已有數個相同模組,分別的複製到數個專案上 😱
小唯團隊有幾個選擇:
- 維持現狀,每個 App 複製一份程式碼
- 優點:現有架構、不需更改、各個 App 工程彈性自由,想怎麼改就怎麼改
- 缺點:維上非常不便,一個修正需要同步到所有 App,之後工程師要記得去手動 merge 更新的程式碼
- 將可模組化的,改寫成 gradle
- 優點:不必要再包一份程式碼到各個 App,之後其他專案要使用會更便利。模組更新,只需要修改一份程式碼,gradle 版號升級便可更新程式
- 缺點:之後各個 App 要做客製化,會相對不容易
- 將可模組化的,改成 submodule
- 優點:每個模組只需要維護一份程式碼,需要更新時,使用 git 內建方式,即可同步最新的程式。App 也有一定自由度去做產品客製化
- 缺點:還是仰賴各 App 去 merge 更新,但不一定需要手動(透過 git 同步 submodule 即可)
最後,小唯團隊選擇 submodule 模式去進行,以下是小唯的手稿紀錄:
將專案切分成 submodule
目前小唯團隊負責的產品 git 架構如下,一個巨型 git repo,內容包山包海,含之前團隊開發的功能模組,以及第三方 github 的功能模組等:
重新打造後,新的 git 架構如下,可被多個產品共用的模組,都會拉成獨立的 git repo,由一個主要 git repo 來把其他功能,以 submodule 方式添加進來:
加入 submodule 指令
以下用其中一個功能為範例。將 function1 以 submodule 方式加入主 App:
❯ git submodule add https://github.com/wm4n/function1.gitfunction1 |
以上指令會將 function1 repo 內容抓到 function1 目錄下,如果印出 .gitmodules 檔案(使用 git submodule add
後自動建立)內容,會發現:
❯ cat .gitmodules |
上傳添加的 submodule
照平常 git 使用的方式,將添加好的 submodule 設定,上傳至 github
❯ git add .gitmodules |
更新 repo 內容
當要同步線上程式碼時,一般會用 git pull
來抓取最新內容,但改用 submodule 方式後,會發現 function1 不會同步。如果要更新,則必須進入到 function1 目錄下再次執行 git pull
❯ cd function1 |
當 submodule 數量成長後
小唯團隊負責的產品,如今被拆解成 5 個 submodule 後,發現今後如果要更新,就要進入每個 submodule 的目錄,分別執行 git pull
指令,這表示一個專案如果有 X 個 submodule,每次更新最多就會有 X + 1 個 git pull
。這不僅讓小唯受不了,團隊也常為了忘記要確實執行,反而造成許多問題。
為了改善這問題,小唯在模組設定檔中,指定讓模組 submodule 追蹤某個特定的遠端分支
❯ cat .gitmodules |
branch 代表這個 submodule 將追蹤指定的遠端分支。之後,團隊只要使用 git submodule update --remote
指令一次,所有的 submodule 都會依照指定的分支去抓最新的內容,如下:
❯ git submodule update --remote |
令人分心的 git status
一切設定好,分支追蹤功能滿足了團隊的需求,從此之後,可重複使用的功能順利地抽出成獨立 git repo,不同 App 依照各自需求,添加自己需要的 submodule,然後一鍵更新所有模組也不再是夢想 🥳。一切是如此的美好,直到團隊發現… 經常 git status
,就會出現:
❯ git status |
在使用分支追蹤之前,小唯的團隊總是會在跟新模組後,緊接著跟新主 App 的模組 commit ID。使用分支追蹤後,雖然不必要再跟新模組 commit ID(因為 git submodule update --remote
指令會自動同步分支最新的 commit ID),但 git status
指令還是會提醒有更新尚未提交。久而久之,團隊都覺得這訊息實在惱人啊~ 😖
小唯試了數個方式後,包括把相關檔案都加到 .gitignore
(此方法無效),最後是在模組設定中,加上 ignore = all
,就搞定了:
❯ cat .gitmodules |
對 github 上的第三方專案做客製化
沒多久後,小唯團隊發現,他們想對 github 上的第三方專案做客製化,以符合規格需求,但要如何追蹤 github 專案分支的同時,又做客製化呢?畢竟無法對第三方的專案做修改(除非經由 PR 流程,但一般來說不會接受客製化…)
小唯團隊利用 github fork 專案的方式,讓自己的專案轉追蹤 fork 的拷貝,利用 fork 拷貝來做客製化,同時又能不斷從 upstream 專案中更新
從此之後,git status
也不再困擾小唯團隊了 🥳
相關連結: Demo 專案
------------- 本文结束 git 就是這麼的難搞!-------------