Google Git-Repo (Android)多仓库项目管理

原文:Google Git-Repo (Android)多仓库项目管理

前言

如果不是 Android dev 可以忽略标题的 Android。

项目模块化/组件化之后各模块也作为独立的 Git 仓库从主项目里剥离了出去,各模块各自管理自己的版本。正常 Android 项目,各剥离出去的子模块仓库则通过 Maven 仓库 来管理,然后和引入第三方库一样依赖到主项目里。这种状态下的项目迭代带来的问题会是:需要频繁发布子模块的 版本并需要把修改的版本提交 Merge Request 到主模块里,尤其像我们一周一版的快速迭代的情况下,问题尤为凸显。此外还有个问题就是 Debug/断点 会变得不太方便,因为可能子模块的源码并未发到 Maven 上。

在上面所说的状态下工作了一段时间后,需要找到个方案来解决这个问题。遂尝试在多仓库管理的方向上尝试。支持 Git 多仓库的管理工具也有好几个:git-repogit-submodulegitslavegit-subtree,看了一番,除了 git-repo 外的几个要么 子模块需要手动指定本地仓库路径、要么 主仓库与子仓库会产生污染 且操作不够简便化。所以最终就采用了 Google 的 Git-repo(Repo)

Git-repo

Repo 是对 Git 构成补充的多(可以巨多的那种)代码库管理工具,简单说就是使用 Python 在 Git 基础上开发的一系列脚本命令。当前整个 Android 项目(AOSP)就是通过 repo 来管理,最新版本的仓库大约 七百多个,可见 Repo 在多仓库的代码管理和版本管理上是可以支撑现有我们的项目的。接下来描述分析下迁移使用 Repo 的过程和一些解释。

环境准备

准备好梯子,把 repo 装到系统里:

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 下环境变量

然后就可以操作项目了,如初始化 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 定义的结构,一般有两个主要的配置:子仓库用到的仓库地址(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>

解释下常用的各节点:

remote

远程仓库地址配置,可以多个。

  • name: 名字,也用于子仓库的 git remote 名称(.git/config 里的 remote)
  • alias: 别名,可省略,建议设为 origin, 设置了那么子仓库的 git remote 即为此名,方便不同的 name 下可以最终设置生成相同的 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)。最终发现是 git-repo 里的代码 manifest_xml.py 有问题,需要 fix 下,简单的把 78 行注释掉,然后推到自己的仓库,指定下系统环境变量:

REPO_URL=your_fix_git-repo_git_url
# repo 脚本会使用此变量的地址

project

子项目仓库配置,可以多个。

  • path: repo sync 同步时,相对于根目录的子仓库文件夹路径
  • name: 子仓库的 git 仓库名称
  • group: 分组
  • revision: 使用的分支名
  • clone-depth: 仓库同步 Git 的 depth
copyfile

project 的子节点属性.

  • src: project 下的相对路径
  • dest: 整个仓库根路径下的相对路径
linkfile

project 的子节点属性,类似 copyfile,只是把复制文件变为创建链接文件。

default

project 没有设置属性时会使用的默认配置,常用的是 remote 和 revision。

其他

其他现在暂时用的较少,详细完整的 manifest 格式说明请看官方文档
了解了 Repo 下的 Manifest 配置仓库的概念后就可以根据自己的项目来创建了,Just do it!

local_manifest

local_manifest 简单说就是一个比 repo init 时设置的 manifest 有更高优先级的本地配置,一般用在不改动远程 manifest 配置又想设置到本地的专属配置时用得到。版本高一点的 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 工作区后,repo sync,你就可以进入正常的特性开发状态了。那么我们以开发一个 feature 为例,顺序说明下需要使用到的常用命令,也是我现在尝试使用的 repo workflow 了。

repo init

初始化。

repo init -u your_project_git_url

其他常用可选参数:

  • -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 里配置的各仓库和其对应目录。其他常用可选参数:

  • -f 不阻断同步,即一个 project 失败会继续下一个 project 同步
  • --force-sync 如果需要,强制覆盖现有的 git 目录指向不同的对象目录。此操作可能会导致数据丢失
  • -d 把项目回退到 manifest 里配置的分支
  • -m 手动指定当前操作使用哪个 manifest 文件
  • -t 使用对应 tag 里的 manifest 文件

同步完后你就可以用你的 IDE 打开项目了。此时,你需要根据当前的子仓库目录配置,在项目里处理下子仓库的依赖问题,比如 Android 项目,在 settting.gradle 里就需要根据此 repo 仓库的项目目录把子仓库 include 进来。

repo start

从 manifest 配置文件中指定的分支里,创建一个新的分支进行开发,如:

repo start your_feature_branch_name --all | [project1 project2]

后续如果发现还改动到了其他模块仓库那么再 start 一个对应 project 的分支

Work in progress

开发过程中需要用到的常用命令:

  • repo status: 跟 git status 类似,会把当前 repo 工作区的状态信息列出来
  • repo diff: 同理 git diff
  • repo forall <PROJECT_LIST> -c <COMMAND>: 在(所有)子仓库下执行命令,比如 repo 没有类似 git stash 的命令,利用 forall 就可以实现:repo forall -c git stash
  • repo prune: 删除已经合并分支
  • repo stage: 把文件添加到 index 表(暂存区)中
  • repo manifest: 显示当前使用的 manifest 信息内容

repo upload

提交代码到 Gerrit code review 中,如果没有使用 Gerrit,则可以跟之前那样手动操作单个仓库 push 或者使用 repo forall -c git push xxx

Other

其他更详细的参考可以查看 Repo 命令参考资料

Workflow

上面一节介绍的 repo 命令的顺序过程可以说就是一个常用的 repo 工作流程,AOSP 给的开发流程可以先看下:
开发

Git 和 Repo 快速参考表

那么根据上一节的介绍,给出下当前尝试使用的 repo-workflow 的图示:

Git-repo workflow

具体到每个仓库来说,主仓库位置原有的 git workflow 不用变,各子仓库新增 devbuild 分支,那么对应的合版打包与发版打包对应 Manifest 固定的分支配置就行,如果没有项目删减,那么合版与发版时的子模块仓库版本也是不用动的。避免了频繁的合版时,各模块版本频繁/繁琐的修改合并。

Jenkins

使用 Jenkins 的需要使用 repo 插件,也还算挺方便的,稍微由原来的 git 迁移到 repo 的配置就行。
配合 Jenkins 的自动化打包,加一些自动化的打包配置,如远程参数化构建、自定义的 Gradle 打包插件等,如果需要在 Jenkins 上打 Feature 包的,那么在 Manifest 项目里可以不需要再新建 Feature 分支了。所以综合来看,合版时的工作量减轻了不少~

End

参考:

AOSP 文档
Git多项目管理
git-repo README

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 开发中我们经常会遇到这样的情况:项目越来越大,一些通用的模块我们希望将他抽离出来作为单独的项目,以便其他项目也可以...
    feil0n9wan9阅读 19,919评论 0 24
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 美国感恩节即将来临,洛杉矶中餐馆的感恩节火鸡预订生意越发火爆,不少华人新移民表示,感恩节吃火鸡已经是过节必做的事...
    余温残阅读 155评论 0 0
  • 第一回请圣旨双仙结发 巍巍天门锁风尘,秋水延绵入晓春。山间仙果七八树,村头犬吠一两声。千年清修金刚体,万载缘得结发...
    猴子猪阅读 722评论 10 5
  • 大易无边阅读 192评论 1 2