背景
公司要做一個服務平臺,有12個領域,每個領域爲一個微服務,現在前端項目暫時拆分爲三個,分別爲SEO,content和forward,三個項目(以後會拆分更多的項目)的組件、插件、工具、狀態管理爲同一套。現狀複製三份一個項目放一份,這樣的弊端是一個公共組件修改要同時維護三份,另外不同項目新增不同的組件,隨着項目的增多組件的同步、維護成本會越來越大
需求分析
- (組件、插件、工具)、狀態管理等公用代碼遷移出去分別建立front-common和front-store(nuxt對vuex進行了封裝,爲了儘量不改動原目錄結構)兩個git 倉庫,供其他項目共享代碼
- 公用代碼原本是什麼樣,抽取後也是什麼樣
- 公用代碼庫是可以在不同項目間雙向同步的而不是單向同步
- 保留公用代碼庫的歷史提交記錄與雙向同步記錄
雙向同步例子:A項目中依賴了子項目B,在A項目裏改B子項目對應的目錄裏的代碼,然後測試通過後, 直接提交代碼,這個更改也提交到B子項目的 Git倉庫裏。 同時子項目B也可以單獨提交到 Git 倉庫,在A項目裏把子項目B的代碼直接更新。
現有方案
- Git Submodule:這是Git官方以前的推薦方案
- Git Subtree:從 Git 1.5.2 開始,Git 新增並推薦使用這個功能來管理子項目
- npm:node package manager,實際上不僅僅是 node 的包管理工具
- composer:暫且認爲他是php版npm
雖然 npm,composer,maven 等更側重於包的依賴管理,以上幾個方案都是能夠做到在不同項目中同步同一塊代碼的,但沒法雙向同步,更適用於子項目代碼比較穩定的情形。Git Submodule 和 Git Subtree 都是官方支持的功能,不具有依賴管理的功能,但能滿足我們的要求。Git Subtree相對來說會更好一些 。
submodule 與 subtree對比
- git submodule
- 允許其他的倉庫指定以一個commit嵌入倉庫的子目錄
- 倉庫
clone
下來需要init
和update
- 會產
.gitmodule
文件記錄 submodule 版本信息 - git submodule 刪除起來比較費勁
- git subtree
- 避免以上問題
- 管理和更新流程比較方便
- git subtree合併子倉庫到項目中的子目錄。不用像submodule那樣每次子項目修改了後要
init
和update
。萬一哪次沒update就直接add .
將.gitmodule
也commit
上去就悲劇了 - git v1.5.2以後建議使用git subtree
拆分已有項目
需要從現有項目中抽取公共模塊單獨進行git管理首先進行技術預演(展示網址爲私有項目,自行替換自己的git倉庫地址)
假設原項目test-sub-tree下web目錄抽取項目store
- **進入項目目錄:**cd test-sub-tree
*ps:直接使用git subtree add 的命令會出現 prefix ‘’ already exists. 這樣的錯誤提示,這是因爲對應的目錄已經存在, 不能直接添加,需要把對應的目錄剝離開然後再加入subtree
2.剝離出store
git subtree split -P <store項目的相對路徑> -b <臨時branch>
Git 會遍歷所有的commit,分離出store相關的commit,並存入front-store分支中
3. 創建子repo
在git遠程創建front-store項目
退出項目目錄,在原項目front同級目錄創建文件夾,進入front-store目錄,把這個目錄變成Git可以管理的倉庫:
cd ../
mkdir front-store
cd front-store
git init
拉取test-sub-tree項目front-store分支
git pull <test-sub-tree項目的路徑> <臨時branch>
如果直接用git clone <git地址>來拉取子項目並生成文件夾(我的common項目使用這種方法),執行上述命令
可能會報fatal: refusing to merge unrelated histories錯誤。
解決辦法使用命令: git pull <test-sub-tree項目的路徑> <臨時branch> --allow-unrelated-histories
關聯front-store遠程倉庫,推送代碼到遠程
git remote add origin <front-store項目的git倉庫>
git push origin -u master
4. 清理數據
cd test-sub-tree項目的路徑
git rm -rf <test-sub-tree項目的store相對路徑>
git commit -m '移除store模塊' # 提交刪除申請
git branch -D <臨時branch> # 刪除臨時分支front-store
5. 添加subtree
git subtree add -P <store項目的相對路徑> <store項目git地址> <分支> --squash
git push
解釋:--squash意思是把subtree的改動合併成一次commit,這樣就不用拉取子項目完整的歷史記錄。 -P等於--prefix,之後的=等號可以用空格。 執行完第3步時,對應的目錄已經剝離出來形成獨立的項目了。 第3,4步主要是把當前項目的對應的文件給刪除,重新在test-sub-tree項目建立Subtree
項目間雙向同步
test-sub-tree項目和front-store正常我們可以進行正常的日常git提交和修改維護
git add .
git commit -m "xxx文件修改"
git pull
git push
兩個項目的代碼要進行雙向同步要執行下面的操作:
1. test-sub-tree項目修改store文件夾下的文件提交更改到front-store子項目
場景test-sub-tree項目刪除store文件夾下supply_item文件
現在通過命令:
git subtree push -P <front-store項目的相對路徑> <front-store項目git地址> <分支>
將“刪除supply_item文件”這個操作同步到front-store子項目的倉庫
進入front-store子項目文件夾,拉取遠程倉庫代碼,會看到front-store文件夾下supply_item文件被刪除
Git 會遍歷步驟2中所有的commit,從中找出針對S目錄的更改,然後把這些更改記錄提交到S項目的Git服務器上, 並保留步驟2中的相關S的提交記錄到S倉庫中
4. front-store子項目修改提交同步到test-sub-tree項目store文件夾下
場景front-store項目新增supply_item文件
現在通過命令:
git subtree pull --prefix=<front-store項目的相對路徑> <front-store項目git地址> <分支> --squash
將front-store子項目的“新增supply_item文件”這個操作同步(拉取)到test-sub-tree項目
用git push將front-store子項目修改推送到test-sub-tree項目遠程倉庫
Tips:相對路徑區別大小寫