Skip to content

repo 多仓管理工具指南 ​

项目规模到一定程度后,单个 Git 仓库往往装不下,会拆成几十上百个仓库。repo 是 Google 为管理 AOSP(Android 开源项目)开发的多仓管理工具:用一份 manifest 清单描述所有子仓库的地址与版本,提供统一的初始化、同步、分支与上传命令。本文覆盖从安装到日常开发的完整流程,包括 detached HEAD、分支无上游跟踪等新手常踩的坑,末尾附 manifest 格式字段速查(译自官方文档)。

核心概念 ​

Repo 是一个建立在 Git 之上的仓库管理工具,用于管理由多个子 Git 仓库组成的大型项目。

  • Manifest(清单):一个 XML 文件(通常是 default.xml),定义所有组成项目的 Git 仓库、存放路径、远程服务器地址以及应该检出的修订版本(revision,可以是分支、标签或提交哈希)。这是 Repo 的"大脑"。
  • Project(项目):一个被 Repo 管理的独立 Git 仓库。
  • Working Directory(工作目录):执行 repo init 的目录,包含所有项目的源代码和一个隐藏的 .repo/ 目录。
  • .repo/ 目录:包含 Repo 自身的代码、manifest 文件、每个项目的 Git 仓库(在 .repo/projects/ 下)以及项目配置信息。不要手动修改这个目录。

安装与初始化 ​

安装 Repo ​

bash
# 在 Linux/macOS 上
mkdir ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

# 将 ~/bin 加入 PATH 环境变量
export PATH=~/bin:$PATH

# 将其添加到 shell 配置文件(~/.bashrc、~/.zshrc)中使其永久生效

初始化工作目录 ​

bash
# 创建并进入工作目录
mkdir my-project && cd my-project

# 初始化 Repo,指定 manifest 仓库的 URL 和要使用的 manifest 文件
repo init -u https://github.com/my-org/manifest-repo.git -b main -m default.xml
  • -u:manifest 仓库的 URL。
  • -b:manifest 仓库本身的分支。
  • -m:要使用的 manifest 文件(在 manifest 仓库中)。省略时默认为 default.xml。

同步所有代码 ​

bash
# 拉取所有项目代码,检出到 manifest 指定的修订版本
repo sync

# 常用选项:
# -c: 只同步 manifest 中指定的分支(节省流量和时间)
# -j4: 使用 4 个线程并行同步(根据 CPU 核心数调整)
# --no-tags: 不拉取标签(进一步节省流量)

repo sync -c -j4 --no-tags

同步完成后,各项目处于**分离头指针(detached HEAD)**状态。这是正常现象:repo 保证代码与 manifest 定义的精确版本一致,而不是跟踪某个分支的最新提交。

日常开发工作流 ​

查看工作目录状态 ​

bash
# 查看所有项目的状态(是否有修改,当前在什么分支/修订版本上)
repo status

# 查看所有项目当前所在的分支
repo branch  # 或 repo branches

# 查看 manifest 文件中定义的所有项目
repo list

创建功能分支 ​

不要在 repo sync 后的分离头指针状态下直接提交代码,必须先创建分支。

bash
# 方法一:使用 repo start(推荐)
# 为所有项目创建新分支(适用于系统级改动)
repo start my-feature --all

# 仅为特定项目创建新分支(适用于模块级改动)
repo start my-feature path/to/project1 path/to/project2

# 为当前所在目录的项目创建新分支
cd path/to/project
repo start my-feature .

# 方法二:使用原生 Git(需确认基于正确的基础版本)
# 先 repo sync 到正确的基础代码,再进入项目目录,基于当前提交创建分支
git checkout -b my-feature

repo start 的优势:它基于 manifest 中定义的修订版本创建分支,而不是本地当前的 HEAD,确保所有开发者分支起点一致。如果分支已存在,repo start 会直接切换过去而不是报错。

修改和提交 ​

bash
# 1. 修改代码
# 2. 在项目目录中用标准 Git 命令提交
cd path/to/project
git add .
git commit -m "实现新功能"

# 3. 查看哪些项目有提交
repo status

上传代码审核(Gerrit) ​

如果项目使用 Gerrit 进行代码审查:

bash
# 上传当前分支的提交到代码审查服务器
repo upload

# 仅上传特定项目
repo upload path/to/project1

# 常用选项:
# --reviewers=alice@example.com: 指定审核人
# --cc=bob@example.com: 抄送
# -t: 将本地分支名作为 topic(便于 Gerrit 上关联同一批变更)

更新代码到最新 ​

别人提交代码后,更新本地分支的步骤:

bash
# 1. 同步所有项目到 manifest 的最新定义
repo sync

# 2. 切换到功能分支(如果不在分支上)
repo start my-feature .  # 分支已存在时会切换过去

# 3. 在功能分支上变基到最新上游代码
git rebase m/master

第 3 步有一个常见坑:repo start 创建的分支没有设置 upstream,直接执行裸 git rebase 会报错:

There is no tracking information for the current branch.

正确做法是显式指定 repo 维护的上游镜像引用(m/ 前缀):git rebase m/master,其中 master 换成 manifest 定义的上游分支名,可用 repo info . 查看。变基可能有冲突,需要手动解决。

处理冲突 ​

repo sync 或 git rebase 后发生冲突时:

  1. 进入有冲突的项目目录。
  2. git status 查看冲突文件。
  3. 手动编辑标记了 <<<<<<<、=======、>>>>>>> 的文件解决冲突。
  4. git add <file> 标记冲突已解决。
  5. git rebase --continue 继续变基。

分支管理 ​

bash
# 删除所有相关项目的某个分支(放弃这条分支的工作)
repo abandon my-feature-branch

# 查看所有项目中的分支情况
repo branch

# 在所有存在该分支的项目中切换到指定分支
repo checkout my-other-branch

高级技巧 ​

加速同步 ​

bash
repo sync -c -j8 --no-tags --optimized-fetch

只同步特定项目 ​

bash
repo sync platform/build platform/bionic

在所有项目上执行命令 ​

bash
# 在所有项目中执行 git log -1
repo forall -c 'git log -1'

# 在有修改的项目中执行 git stash
repo forall -c 'git stash'

# 查看所有项目的远程 URL
repo forall -c 'git remote -v'

-p 选项在执行命令前打印项目路径,输出多项目时便于定位。

清理和重置 ​

bash
# 丢弃所有项目的本地修改(不可恢复,执行前务必确认)
repo forall -c 'git reset --hard ; git clean -fdx'

查看差异 ​

bash
# 比较各项目工作区和暂存区的差异
repo diff

最佳实践 ​

  1. 始终先 repo sync:开始新工作前,确保代码库是最新的。
  2. 用 repo start 创建分支:这是基于正确修订版本创建分支的最安全方法。
  3. 小范围提交:尽量让提交只涉及相关的项目,而不是总是 --all。
  4. 频繁变基:经常用 git rebase m/<上游分支> 将功能分支与主干保持同步,减小冲突积累。
  5. 理解 repo status 输出:- 表示项目没有修改,M 表示文件内容修改,A 表示文件添加,R 表示文件重命名,?? 表示未追踪的新文件。
  6. .repo/local_manifests/ 本地定制:可在此目录创建 local_manifest.xml 来覆盖或添加默认 manifest 中的设置,例如添加一个本地测试用的项目。

要点回顾 ​

  • repo 用一份 manifest 管理几十上百个 Git 仓库,repo init + repo sync 是入口,sync 后各项目处于 detached HEAD 是设计使然。
  • 开发前必须 repo start 建分支,提交用原生 Git 命令,上传审核用 repo upload。
  • repo start 的分支没有上游跟踪,更新代码时用 git rebase m/<上游分支> 而不是裸 git rebase。
  • 需要本地定制仓库集合时,优先改 .repo/local_manifests/,不动主 manifest。

附:manifest 格式字段说明(官方文档翻译) ​

来源:repo 官方 manifest-format 文档,翻译整理。

Repo 清单描述了一个 repo 客户端的结构:哪些目录可见、应该从何处通过 git 获取。清单的基本结构是一个裸 Git 仓库,在其顶级目录中包含一个单独的 default.xml 文件。清单本身是版本控制的,对清单的更新会在 repo sync 期间由客户端自动获取。

为了跨 repo 版本的兼容性,所有未知元素都会被静默忽略。如果想使用自定义元素,x-* 命名空间被保留用于此目的。

manifest ​

文件的根元素。

notice ​

每当 repo sync 完成时显示给用户的任意文本。内容按清单中的原样传递。

remote ​

可指定一个或多个。每个 remote 指定一个或多个项目共享的 Git URL,以及(可选地)这些项目上传更改所通过的 Gerrit 代码审核服务器。

  • name:在清单文件中唯一的短名称。用作每个项目的 .git/config 中的远程名称,自动可用于 git fetch、git remote、git pull 和 git push。
  • alias:覆盖 name,将其设置为每个项目的 .git/config 中的远程名称。它的值可以重复,而 name 在清单文件内必须唯一。
  • fetch:所有使用此远程的项目的 Git URL 前缀。每个项目的名称会附加此前缀,形成克隆项目的实际 URL。
  • pushurl:所有使用此远程的项目的 Git push URL 前缀。可选;未指定时 git push 使用与 fetch 相同的 URL。
  • review:Gerrit 服务器的主机名,repo upload 将更改上传到该服务器。可选;未指定时 repo upload 无法工作。
  • revision:Git 分支名(例如 main 或 refs/heads/main)。拥有自己 revision 的远程将覆盖默认的 revision。

default ​

最多一个。其 remote 和 revision 属性在 project 元素未指定自己的值时使用。

  • remote:先前定义的 remote 元素的名称。
  • revision:Git 分支名。未指定自身 revision 的 project 使用此修订版本。
  • dest-branch:Git 分支名。未设置自身 dest-branch 的 project 继承此值;未设置时项目默认使用 revision。
  • upstream:可在其中找到 sha1 的 Git 引用名。在以 -c 模式同步修订版本锁定的清单时使用,避免同步整个引用空间。
  • sync-j:同步时使用的并行作业数。
  • sync-c:设为 true 时只同步给定分支(revision 属性指定),而非整个引用空间。
  • sync-s:设为 true 时同时同步子项目。
  • sync-tags:设为 false 时只同步给定分支,不同步其他标签。

manifest-server ​

最多一个。url 属性指定清单服务器的 URL,该服务器是一个 XML RPC 服务。详见智能同步文档。

submanifest ​

可指定一个或多个。每个元素描述一个要作为子项检出的单个清单。

  • name:子清单在当前(子)清单内的唯一名称,同时用作 revision 的默认值。相同的名称可用于具有不同父清单的子清单。
  • remote:先前定义的 remote 元素的名称;未提供时使用 default 给出的远程。
  • project:清单项目的名称。URL 的形成方式为 ${remote_fetch}/${project_name}.git。项目名称必须与 Gerrit 所知的名称匹配(如果使用 Gerrit)。
  • manifest-name:清单项目中的清单文件名;未提供时使用 default.xml。
  • revision:Git 分支名、标签或提交哈希;未提供时使用 name。
  • path:相对于 repo 客户端顶级目录的路径,放置子清单的顶级目录;未提供时使用 revision。
  • groups:包含的子清单中所有项目所属的附加组,追加且递归。
  • default-groups:init 时未指定 --groups= 参数时要同步的清单组列表。

project ​

可指定一个或多个。每个元素描述一个要克隆到 repo 客户端工作区的单个 Git 仓库。可通过嵌套 project 指定 Git 子模块:子模块被自动识别并继承父级属性,也可被显式定义覆盖。

  • name:项目的唯一名称。追加到远程的 fetch URL 形成 ${remote_fetch}/${project_name}.git。不能为空、不能是绝对路径、不能使用 . 或 .. 路径组件(本地清单中不强制)。
  • path:相对于 repo 客户端顶级目录的本地检出目录;未提供时使用项目 name。限制同上。要将文件放入检出根目录,改用 copyfile 或 linkfile。
  • remote:先前定义的 remote 元素的名称;未提供时使用 default 给出的远程。
  • revision:清单希望为此项目跟踪的 Git 分支,可相对 refs/heads 或绝对引用;标签和显式 SHA-1 理论上有效但未经广泛测试。
  • dest-branch:repo upload 时更改提交到该分支进行代码审核;此处和 default 均未指定时使用 revision。
  • groups:项目所属的组,以空格或逗号分隔。所有项目都属于 all 组,且每个项目自动属于 name:<name> 和 path:<path> 的组。例如 <project name="monkeys" path="barrel-of"/> 隐式位于 default、name:monkeys 和 path:barrel-of 组中。放在 notdefault 组中的项目不会被 repo 自动下载。
  • sync-c / sync-s / upstream:语义同 default 元素中的同名属性,作用于单个项目。
  • clone-depth:获取此项目时使用的深度,覆盖 repo init --depth。
  • force-path:设为 true 时强制根据 path(而非 name)创建本地镜像仓库,仅适用于本地镜像同步。

extend-project ​

修改指定项目的属性,在本地清单中非常有用:不必完全替换现有项目定义即可修改其属性,对原始清单的更改更具鲁棒性。

  • path:将更改限制为在指定路径检出的项目。
  • dest-path:移动检出项目的位置(覆盖 path 设置)。
  • groups / revision / remote / dest-branch / upstream:覆盖原始项目的对应属性,语法同 project。
  • base-rev:添加针对要扩展的修订版本的检查,不匹配时清单解析失败并给出列表。

annotation ​

零个或多个,作为 project 或 remote 的子元素。描述名称-值对:对项目,将在 forall 命令期间以 REPO_ 前缀导出到每个项目的环境。可选属性 keep(true/false,默认 true)确定 manifest 子命令导出时是否保留。

copyfile ​

零个或多个,作为 project 的子元素。repo sync 期间将项目内的 src 文件复制到客户端顶级的 dest 位置。src、dest 必须是文件,不允许目录或符号链接,中间路径也不能是符号链接;dest 的父目录缺失时自动创建。

linkfile ​

与 copyfile 同时运行,但创建符号链接而非复制。符号链接在 dest(相对树顶级)创建,指向项目中由 src 指定的路径。目标可以是文件或目录,但不能指向 repo 客户端外部。

remove-project ​

从内部清单表中删除一个项目,允许同一清单文件中的后续 project 元素用不同的源替换它。在本地清单中删除并替换项目时很有用。

  • 仅指定 name 时删除所有同名项目;同时指定 name 和 path 时只删除名称与路径都匹配的项目;仅指定 path 时删除所有匹配路径的项目。
  • optional:设为 true 时忽略没有匹配项目的元素。
  • base-rev:语义同 extend-project 的同名属性。

repo-hooks ​

一次只能指定一个,重新定义将导致解析失败。

  • in-project:定义钩子的项目,值必须与先前定义的 project 元素的 name 属性(而非 path)匹配。
  • enabled-list:要使用的钩子列表,以空格或逗号分隔。

superproject ​

指定超级项目的 URL。最多一个,重新定义将导致解析失败。

  • name:语义同 project 的 name。
  • remote:先前定义的 remote 元素的名称。
  • revision:清单希望为此超级项目跟踪的 Git 分支。

contactinfo ​

让清单作者自行注册联系信息。具有一个必需属性 bugurl(提交 bug 的 URL)。可以重复,后面的条目覆盖前面的。

include ​

将另一个清单文件包含进原始清单。目标清单必须能独立使用。

  • name:要包含的清单,相对于清单仓库的根。
  • groups:包含的清单中所有项目所属的附加组,追加且递归。
  • revision:包含的清单中所有项目默认所属的分支。

本地清单 ​

可在 $TOP_DIR/.repo/local_manifests/*.xml 中放置本地清单文件,添加额外的远程和项目:

bash
$ ls .repo/local_manifests
local_manifest.xml

$ cat .repo/local_manifests/local_manifest.xml
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <project path="manifest"
           name="tools/manifest" />
  <project path="platform-manifest"
           name="platform/manifest" />
</manifest>

在 repo sync 之前添加的项目会被自动下载和管理。

最近更新

基于 VitePress 构建