68

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

 5 years ago
source link: https://rocko.xyz/2018/11/22/google-git-repo-多仓库项目管理/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

如果不是 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>
  <remotename="remote1"
           alias="origin"
           fetch=".."
           review="https://android-review.googlesource.com/" />

  <remotename="remote2"
           alias="origin"
           fetch="[email protected]:group2/"
           review="https://android-review2.googlesource.com/" />         

  <defaultrevision="master"
           remote="remote1"
           sync-j="4" />

  <projectpath="build/make"name="platform/build"groups="pdk">
    <copyfilesrc="core/root.mk"dest="Makefile"/>
    <linkfilesrc="CleanSpec.mk"dest="build/CleanSpec.mk"/>
  </project>
  <projectpath="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 遇到个坑, [email protected]: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-projectname="project_name"/>
<!-- 再重写 project 配置 -->
<projectpath="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 -c : 在(所有)子仓库下执行命令,比如 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 给的开发流程可以先看下: 开发

6zqAjeB.jpg!web

Git 和 Repo 快速参考表

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

yIvUzaf.jpg!web

Git-repo workflow

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

Jenkins

使用 Jenkins 的需要使用 repo 插件,也还算挺方便的,稍微由原来的 git 迁移到 repo 的配置就行。

配合 Jenkins 的自动化打包,加一些自动化的打包配置,如远程参数化构建、自定义的 Gradle 打包插件等,如果需要在 Jenkins 上打 Feature 包的,那么在 Manifest 项目里可以不需要再新建 Feature 分支了。所以综合来看,合版时的工作量减轻了不少~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK