原文:Google Git-Repo (Android)多倉庫項目管理
前言
如果不是 Android dev 可以忽略標題的 Android。
項目模塊化/組件化
之后各模塊也作為獨立的 Git
倉庫從主項目里剝離了出去,各模塊各自管理自己的版本。正常 Android 項目,各剝離出去的子模塊倉庫則通過 Maven 倉庫
來管理,然后和引入第三方庫一樣依賴到主項目里。這種狀態(tài)下的項目迭代帶來的問題會是:需要頻繁發(fā)布子模塊的 版本并需要把修改的版本提交 Merge Request 到主模塊里茴丰,尤其像我們一周一版的快速迭代的情況下,問題尤為凸顯天吓。此外還有個問題就是 Debug/斷點
會變得不太方便贿肩,因為可能子模塊的源碼并未發(fā)到 Maven 上。
在上面所說的狀態(tài)下工作了一段時間后龄寞,需要找到個方案來解決這個問題汰规。遂嘗試在多倉庫管理的方向上嘗試。支持 Git 多倉庫的管理工具也有好幾個:git-repo物邑、git-submodule溜哮、gitslave、git-subtree色解,看了一番茂嗓,除了 git-repo 外的幾個要么 子模塊需要手動指定本地倉庫路徑
、要么 主倉庫與子倉庫會產(chǎn)生污染
且操作不夠簡便化科阎。所以最終就采用了 Google 的 Git-repo(Repo)
Git-repo
Repo 是對 Git 構(gòu)成補充的多(可以巨多的那種)代碼庫管理工具在抛,簡單說就是使用 Python 在 Git 基礎(chǔ)上開發(fā)的一系列腳本命令。當前整個 Android 項目(AOSP)就是通過 repo 來管理萧恕,最新版本的倉庫大約 七百多個刚梭,可見 Repo 在多倉庫的代碼管理和版本管理上是可以支撐現(xiàn)有我們的項目的。接下來描述分析下遷移使用 Repo 的過程和一些解釋票唆。
環(huán)境準備
準備好梯子朴读,把 repo
裝到系統(tǒng)里:
mkdir ~/bin
PATH=~/bin:$PATH # 自己加到 ~/.zshrc or ~/.bashrc or /etc/profile 里
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
# 最后 source 下環(huán)境變量
然后就可以操作項目了,如初始化 AOSP 項目:
repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1
Manifest
Repo 管理的核心就在于 Manifest
走趋,每個采用 repo 管理的復雜多倉庫項目都需要一個對應的 manifest 倉庫衅金,如 AOSP 的 manifest ,此倉庫用來存儲所有子倉庫的配置信息簿煌,repo 也是讀取此倉庫的配置文件來進行管理操作氮唯。里面的配置就是 xml 定義的結(jié)構(gòu),一般有兩個主要的配置:子倉庫用到的倉庫地址(remote)姨伟、子倉庫詳細配置信息(project)惩琉。舉例如下:
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote name="remote1"
alias="origin"
fetch=".."
review="https://android-review.googlesource.com/" />
<remote name="remote2"
alias="origin"
fetch="git@github.com:group2/"
review="https://android-review2.googlesource.com/" />
<default revision="master"
remote="remote1"
sync-j="4" />
<project path="build/make" name="platform/build" groups="pdk" >
<copyfile src="core/root.mk" dest="Makefile" />
<linkfile src="CleanSpec.mk" dest="build/CleanSpec.mk" />
</project>
<project path="build/blueprint" name="platform/build/blueprint" groups="pdk,tradefed" revision="other_branch" remote="remote1"/>
<!-- ... -->
</manifest>
解釋下常用的各節(jié)點:
remote
遠程倉庫地址配置,可以多個夺荒。
- name: 名字瞒渠,也用于子倉庫的 git remote 名稱(.git/config 里的 remote)
- alias: 別名,可省略技扼,建議設(shè)為
origin
伍玖, 設(shè)置了那么子倉庫的 git remote 即為此名,方便不同的 name 下可以最終設(shè)置生成相同的 remote 名稱 - fetch: 倉庫地址
前綴
剿吻,即 project 的倉庫地址為:remote.fetch + project.name
- pushurl: 一般可省略窍箍,省略了則直接用 fetch
- review: Gerrit code review 的地址,如果沒有用 Gerrit 則不需要配置(也就不能用 repo upload 命令了)
- revision: 使用此 remote 的默認分支
這里的 fetch 遇到個坑丽旅,git@github.com:group2/
這樣 git scheme 開頭的地址會有問題(ssh/https 的正常)椰棘,最終子倉庫在本地的 remote 地址一直是 manifest 的地址拼上 fetch 再加 project 的 name (manifest.git_path + remote.fetch + project.name)。最終發(fā)現(xiàn)是 git-repo 里的代碼 manifest_xml.py 有問題魔招,需要 fix 下晰搀,簡單的把 78
行注釋掉,然后推到自己的倉庫办斑,指定下系統(tǒng)環(huán)境變量:
REPO_URL=your_fix_git-repo_git_url
# repo 腳本會使用此變量的地址
project
子項目倉庫配置外恕,可以多個。
- path: repo sync 同步時乡翅,相對于根目錄的子倉庫文件夾路徑
- name: 子倉庫的 git 倉庫名稱
- group: 分組
- revision: 使用的分支名
- clone-depth: 倉庫同步 Git 的 depth
copyfile
project 的子節(jié)點屬性.
- src: project 下的相對路徑
- dest: 整個倉庫根路徑下的相對路徑
linkfile
project 的子節(jié)點屬性鳞疲,類似 copyfile,只是把復制文件變?yōu)閯?chuàng)建鏈接文件蠕蚜。
default
project 沒有設(shè)置屬性時會使用的默認配置尚洽,常用的是 remote 和 revision。
其他
其他現(xiàn)在暫時用的較少靶累,詳細完整的 manifest 格式說明請看官方文檔腺毫。
了解了 Repo 下的 Manifest 配置倉庫的概念后就可以根據(jù)自己的項目來創(chuàng)建了癣疟,Just do it!
local_manifest
local_manifest 簡單說就是一個比 repo init 時設(shè)置的 manifest 有更高優(yōu)先級的本地配置,一般用在不改動遠程 manifest 配置又想設(shè)置到本地的專屬配置時用得到潮酒。版本高一點的 repo 下睛挚,在工作根目錄新建配置文件即可: .repo/local_manifests/local_manifest.xml
有一點需要注意的是如果需要覆蓋主 manifest 文件已有 project 的配置那么需要先 remove-project
才行,不然會報錯:
fatal: duplicate path project_name in .repo/manifest.xml
fix :
<remove-project name="project_name" />
<!-- 再重寫 project 配置 -->
<project path="xxx" name="xxx" revision="xxxx" remote="xxxx" />
Repo 命令
repo init -u yout_manifest_git_url
初始化了你的項目 repo 工作區(qū)后急黎,repo sync
扎狱,你就可以進入正常的特性開發(fā)狀態(tài)了。那么我們以開發(fā)一個 feature 為例勃教,順序說明下需要使用到的常用命令淤击,也是我現(xiàn)在嘗試使用的 repo workflow
了。
repo init
初始化故源。
repo init -u your_project_git_url
其他常用可選參數(shù):
- -b 選取的 manifest 倉庫分支污抬,默認 master
- -m 選取的默認配置文件,默認 default.xml
- --depth=1 git clone 的深度心软,一般如在 Jenkins 上打包時可用壕吹,加快代碼 clone 速度
- --repo-url=URL 使用自定義的 git-repo 代碼,如前面說到的 fix 了 bug 的 git-repo
- --no-repo-verify 不驗證 repo 的源碼删铃,如果自定義了 repo url 那么這個一般也加上
repo sync
初始化好一個 repo 工作目錄后下一步就是把代碼同步下來了:
repo sync
同步完成后就會在當前目錄下生成 manifest 里配置的各倉庫和其對應目錄耳贬。其他常用可選參數(shù):
- -f 不阻斷同步,即一個 project 失敗會繼續(xù)下一個 project 同步
- --force-sync 如果需要猎唁,強制覆蓋現(xiàn)有的 git 目錄指向不同的對象目錄咒劲。此操作可能會導致數(shù)據(jù)丟失
- -d 把項目回退到 manifest 里配置的分支
- -m 手動指定當前操作使用哪個 manifest 文件
- -t 使用對應 tag 里的 manifest 文件
同步完后你就可以用你的 IDE 打開項目了。此時诫隅,你需要根據(jù)當前的子倉庫目錄配置腐魂,在項目里處理下子倉庫的依賴問題,比如 Android 項目逐纬,在 settting.gradle 里就需要根據(jù)此 repo 倉庫的項目目錄把子倉庫 include 進來蛔屹。
repo start
從 manifest 配置文件中指定的分支里,創(chuàng)建一個新的分支進行開發(fā)豁生,如:
repo start your_feature_branch_name --all | [project1 project2]
后續(xù)如果發(fā)現(xiàn)還改動到了其他模塊倉庫那么再 start 一個對應 project 的分支
Work in progress
開發(fā)過程中需要用到的常用命令:
- repo status: 跟 git status 類似兔毒,會把當前 repo 工作區(qū)的狀態(tài)信息列出來
- repo diff: 同理 git diff
- repo forall <PROJECT_LIST> -c <COMMAND>: 在(所有)子倉庫下執(zhí)行命令,比如 repo 沒有類似
git stash
的命令甸箱,利用 forall 就可以實現(xiàn):repo forall -c git stash
- repo prune: 刪除已經(jīng)合并分支
- repo stage: 把文件添加到 index 表(暫存區(qū))中
- repo manifest: 顯示當前使用的 manifest 信息內(nèi)容
repo upload
提交代碼到 Gerrit code review 中育叁,如果沒有使用 Gerrit,則可以跟之前那樣手動操作單個倉庫 push 或者使用 repo forall -c git push xxx
Other
其他更詳細的參考可以查看 Repo 命令參考資料
Workflow
上面一節(jié)介紹的 repo 命令的順序過程可以說就是一個常用的 repo 工作流程芍殖,AOSP
給的開發(fā)流程可以先看下:
開發(fā)
那么根據(jù)上一節(jié)的介紹豪嗽,給出下當前嘗試使用的 repo-workflow
的圖示:
具體到每個倉庫來說,主倉庫位置原有的 git workflow 不用變,各子倉庫新增 dev
、build
分支,那么對應的合版打包與發(fā)版打包對應 Manifest 固定的分支配置就行柑贞,如果沒有項目刪減,那么合版與發(fā)版時的子模塊倉庫版本也是不用動的成榜。避免了頻繁的合版時,各模塊版本頻繁/繁瑣的修改合并蹦玫。
Jenkins
使用 Jenkins 的需要使用 repo 插件,也還算挺方便的刘绣,稍微由原來的 git 遷移到 repo 的配置就行樱溉。
配合 Jenkins 的自動化打包,加一些自動化的打包配置纬凤,如遠程參數(shù)化構(gòu)建福贞、自定義的 Gradle 打包插件等,如果需要在 Jenkins 上打 Feature 包的停士,那么在 Manifest 項目里可以不需要再新建 Feature 分支了挖帘。所以綜合來看,合版時的工作量減輕了不少~
End
參考: