1. repo工具链组成

项目工程庞大,多人协作开发,如果能够按照边界进行拆分,不仅从逻辑层面进行了解耦,也有利于模块进行独自维护,最小限度地解决问题而降低对他人的影响。

现在在公司采用已搭建好的 repo+gerrit 环境进行代码下载、检视、修改及提交。

想了解一下repo工具统一对多代码仓的管理的方式,以便日后用于自己的项目管理。
大概折腾了一下,把基本的工作流程及方式记录如下。

2. repo工作流

2.1 repo 工具套装

无Gerrit的repo本地开发配置 - 图1

由图所示,repo套装工具主要由三部分构成:

  • repo script: 用以支撑repo执行各种命令(例如git)的底层python脚本仓。

  • manifests: 复杂的多项目管理配置XML文件仓,其中的XML文件将指示各个项目将以何种方式进行组织。例如某个项目的git clone URL,分支,目录组织形式。

  • 将上述两个库从server下载到client端的引导脚本 repo boot script — repo。

repo boot script 会通过repo init命令,将 repo script 及 manifests 仓 从server端按照命令参数拉取到client端,并组织在 .repo 目录下。

2.2 项目的Git仓库目录和工作目录

一般来说,一个项目的Git仓库目录(默认为.git目录)是位于工作目录下面的,但是Git支持将一个项目的Git仓库目录和工作目录分开来存放。

.repo 目录组织如下(已省略部分次要项):

  1. .repo
  2. ├── manifests
  3. └── default.xml
  4. ├── manifests.git
  5. ├── manifest.xml -> manifests/default.xml
  6. ├── project.list
  7. ├── project-objects
  8. ├── pro_1.git
  9. ├── pro_2.git
  10. ├── pro_3.git
  11. └── test
  12. └── pro_3.git
  13. ├── projects
  14. ├── pro_1.git
  15. ├── pro_2.git
  16. └── test
  17. └── pro_3.git
  18. └── repo
  19. ├── .git
  20. ├── main.py
  21. ...
  22. ├── subcmds
  23. ├── abandon.py
  24. ...

其中,repo script 仓的git仓目录(.git)位于工作目录 .repo/repo 下,而 manifests 仓的git仓目录与工作目录平行位于 .repo 仓下(二者目录独立)。

2.3 项目的git仓

在拉取项目代码后,我们很清晰的可以看到每个项目的工作目录下都有一个git仓目录:

  1. repo_client/
  2. ├── pro_1
  3. ├── .git
  4. ├── f1
  5. └── f2
  6. ├── pro_2
  7. ├── .git
  8. ├── f3
  9. └── f4
  10. └── test
  11. └── pro_3
  12. ├── .git
  13. └── f5

而该git仓目录,并不是真正的项目管理git仓,而仅仅是一些软链接,这些项目真正的git仓,位于 .repo 下project相关的git仓中。

  1. xxxxxxxxx@xxxxxxxxx:~/repo_test/repo_client/pro_1/.git$ ll
  2. total 16
  3. drwxrwxr-x 2 xx 4096 Sep 12 14:13 ./
  4. drwxrwxr-x 3 xx 4096 Sep 12 14:13 ../
  5. lrwxrwxrwx 1 xx 37 Sep 12 14:13 config -> ../../.repo/projects/pro_1.git/config
  6. lrwxrwxrwx 1 xx 49 Sep 12 14:13 description -> ../../.repo/project-objects/pro_1.git/description
  7. -rw-rw-r-- 1 xx 41 Sep 12 14:13 HEAD
  8. lrwxrwxrwx 1 xx 43 Sep 12 14:13 hooks -> ../../.repo/project-objects/pro_1.git/hooks/
  9. -rw-rw-r-- 1 xx 209 Sep 12 14:13 index
  10. lrwxrwxrwx 1 xx 42 Sep 12 14:13 info -> ../../.repo/project-objects/pro_1.git/info/
  11. lrwxrwxrwx 1 xx 35 Sep 12 14:13 logs -> ../../.repo/projects/pro_1.git/logs/
  12. lrwxrwxrwx 1 xx 45 Sep 12 14:13 objects -> ../../.repo/project-objects/pro_1.git/objects/
  13. lrwxrwxrwx 1 xx 42 Sep 12 14:13 packed-refs -> ../../.repo/projects/pro_1.git/packed-refs
  14. lrwxrwxrwx 1 xx 35 Sep 12 14:13 refs -> ../../.repo/projects/pro_1.git/refs/
  15. lrwxrwxrwx 1 xx 46 Sep 12 14:13 rr-cache -> ../../.repo/project-objects/pro_1.git/rr-cache/
  16. lrwxrwxrwx 1 xx 38 Sep 12 14:13 shallow -> ../../.repo/projects/pro_1.git/shallow
  17. lrwxrwxrwx 1 xx 41 Sep 12 14:13 svn -> ../../.repo/project-objects/pro_1.git/svn/

3. manifest.xml组织格式

repo script 仓是repo真正执行指令的python脚本,此处暂不做代码级分析。
manifests 仓中存放的是 复杂的多项目管理配置XML文件,repo仓脚本,根据该仓下的XML配置文件,对不同的项目执行不同的操作,故该仓中的文件,直接决定了众多项目的工作方式。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <manifest>
  3. <!-- remote element. -->
  4. <!-- several remote element, used to specify different project's default download server -->
  5. <!-- Main Attribute:
  6. ┊ ┊fetch:all projects' git URL prefix
  7. -->
  8. <remote name="origin" fetch="/home/m00354437/repo_test/repo_server" />
  9. <!-- default element. all projects' default attribute if project element not specified -->
  10. <!-- Main Attribute:
  11. ┊ ┊revision:git branch's name. master or refs/heads/master ...
  12. ┊ ┊path: local sub directory used in git clone
  13. ┊ ┊sync_j: repo sync threads used
  14. ┊ ┊sync_c: True/False. sync specific revision(branch)
  15. ┊ ┊sync_s: True/False. sync git's children project
  16. -->
  17. <default remote="origin" revision="master" sync_j="4" />
  18. <!-- project element, name:unique signature pointed to project. -->
  19. <!-- project git URL: ${remote fetch}/${project name} -->
  20. <project name="pro_1" path="pro_1" remote="origin" revision="master" />
  21. <project name="pro_2" path="pro_2" remote="origin" revision="master" />
  22. <project name="test/pro_3" path="test/pro_3" remote="origin" revision="master" />
  23. </manifest>

该XML的主要标签为3个 — remote default project

  • remote

设置远程git服务器的属性,主要属性为:

  1. name:远程git服务器的名字;

  2. fetch:所有projects的git URL 前缀;

当然,可以设置多个remote标签,以指示多个remote服务端

  • default

设定所有projects的默认属性值,如果在project元素里没有指定一个属性,则使用default元素的属性值。

  1. revision:分支名。master, refs/heads/master or other

  2. path:git clone 的时候,指定本地子目录

  3. sync_j:执行 repo sync 时的线程数

  4. sync_c:是否指定只同步指定branch

  5. sync_s:是否同步git仓下的子项目

  • project:
  1. name:唯一的名字标识project,同时也用于生成git仓库的URL。

  2. path: 可选的路径。指定git clone出来的代码存放在本地的子目录。如果没有指定,则以name作为子目录名。

  3. remote: 指定之前在某个remote元素中的name,以确定从哪个server进行clone等操作。

  4. revision: 指定需要获取的git提交点,可以是master, refs/heads/master, tag或者SHA-1值。如果不设置的话,默认下载当前project,当前分支上的最新代码。

【重要】project git URL 组成:${remote fetch}/${project name}.git
这部分非常重要,remote fetch决定了server端地址, project name 包含了项目代码组织结构,故一定确认。

4. 常规实战

  1. 在repo_test目录下创建两个子目录,repo_server,repo_client

repo_server模拟远端 repo_client模拟本地

  1. 在repo_server端 git init —bare 4个空git仓:
  • manifest.git

  • pro_1.git

  • pro_2.git

  • test/pro_3.git

  1. 分别clone这4个仓,将 第3节的 xml 修改名为 default.xml 并push到manifest.git;其余3个仓分别touch几个文件并push。

  2. 在repo_client下,执行 repo init,进行.repo目录生成,manifests仓及repo script仓的拉取

  1. repo init -u <manifest_repo_url> -b <manifest_repo_branch> -m <manifest_repo_xmlFile> --repo-url=<Repo_repo_url>
  1. 执行sync 进行代码拉取
  1. repo sync -c --no-tags
  1. 检查代码目录组织结构,及log信息

参考:
[1] 深层次的讲解repo原理
[2] 搭建Repo服务器
[3] Manifest和Repo使用详解