Repo tool do Google: gerenciando múltiplos repositórios como um

1. O que é a Repo tool e por que ela existe

A Repo tool nasceu de uma necessidade concreta no desenvolvimento do Android. Quando o Google começou a gerenciar o código-fonte do Android, enfrentou um desafio monumental: centenas de repositórios Git independentes que precisavam ser coordenados como um único projeto. O Git, por si só, não foi projetado para gerenciar múltiplos repositórios de forma coesa.

A diferença fundamental entre Repo e Git é clara: Git é uma ferramenta de versionamento de código, enquanto Repo é uma ferramenta de orquestração. Repo não substitui Git; ele o coordena. Imagine um maestro (Repo) regendo uma orquestra (Git) onde cada músico toca um instrumento diferente (repositório). O maestro não toca os instrumentos, mas garante que todos estejam na mesma página.

Casos de uso típicos incluem projetos com múltiplos componentes independentes que precisam ser versionados juntos: sistemas embarcados, kernels Linux customizados, frameworks com plugins e, claro, o próprio Android Open Source Project (AOSP).

2. Arquitetura do Repo: manifesto, referências e workspace

O coração do Repo é o arquivo de manifesto, geralmente chamado default.xml. Este arquivo XML define quais repositórios Git fazem parte do projeto, em quais branches ou commits específicos eles devem estar e como devem ser organizados no sistema de arquivos local.

Exemplo de estrutura de manifesto:

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote name="origin"
          fetch="https://github.com/minha-org" />

  <default revision="main"
           remote="origin"
           sync-j="4" />

  <project path="core/lib"
           name="biblioteca-core" />

  <project path="services/auth"
           name="servico-autenticacao"
           revision="v2.1.0" />

  <project path="frontend/webapp"
           name="aplicacao-web" />
</manifest>

Quando você executa repo init -u <url-do-manifesto>, o Repo baixa o manifesto para o diretório .repo/ e prepara o workspace. O comando repo sync então percorre cada projeto listado no manifesto, clona ou atualiza os repositórios nos diretórios especificados pelo atributo path.

A estrutura do workspace fica assim:

meu-projeto/
├── .repo/
│   ├── manifests/
│   ├── manifest.xml
│   ├── projects/
│   └── repo/
├── core/
│   └── lib/          # repositório "biblioteca-core"
├── services/
│   └── auth/         # repositório "servico-autenticacao"
└── frontend/
    └── webapp/       # repositório "aplicacao-web"

O diretório .repo/ contém todo o estado interno do Repo, enquanto os diretórios de trabalho são os repositórios Git normais que você edita diretamente.

3. Comandos fundamentais do dia a dia

Os comandos mais utilizados no dia a dia com Repo são:

repo init e repo sync — O ponto de partida:

repo init -u https://github.com/minha-org/manifestos.git -b stable-v1
repo sync

repo init configura o workspace apontando para um manifesto específico. repo sync baixa ou atualiza todos os repositórios para as revisões especificadas no manifesto.

repo start e repo upload — Trabalhando com branches temáticos:

repo start minha-feature --all
cd core/lib
echo "nova funcionalidade" >> README.md
git commit -a -m "Adiciona descrição da feature"
repo upload

repo start cria um branch com o mesmo nome em todos os projetos. repo upload empacota os commits locais e os envia para revisão (tipicamente integrado com Gerrit).

repo status, repo diff e repo info — Visão consolidada:

repo status
repo diff
repo info --all

repo status mostra o estado de todos os repositórios de uma vez. repo diff exibe mudanças não commitadas em todo o workspace. repo info fornece detalhes sobre cada repositório, como URL remota e branch atual.

4. Gerenciamento de branches e revisões com manifesto

O manifesto permite fixar versões específicas de duas formas: usando branches mutáveis ou commits imutáveis.

Fixando commits específicos:

<project path="core/lib"
         name="biblioteca-core"
         revision="a1b2c3d4e5f6..." />

Isso garante que todos os desenvolvedores estejam exatamente no mesmo commit, independentemente de novos commits no branch.

Usando branches mutáveis:

<project path="services/auth"
         name="servico-autenticacao"
         revision="develop" />

Aqui, repo sync sempre trará o HEAD mais recente do branch develop.

Para atualização seletiva, o repo sync respeita as revisões do manifesto. Se você quer atualizar apenas um projeto específico:

repo sync services/auth

Trabalhando com múltiplos manifestos:

repo init -u <url> --manifest-name=release-v2.xml
repo sync

Isso permite ter diferentes manifestos para diferentes cenários: desenvolvimento, staging, produção.

5. Trabalho colaborativo e fluxo de patches

O fluxo típico de contribuição com Repo envolve o repo upload integrado com Gerrit, um sistema de code review. O processo funciona assim:

  1. Desenvolvedor cria um topic branch com repo start
  2. Faz alterações em um ou mais repositórios
  3. Commita localmente com git commit
  4. Envia para revisão com repo upload

O repo upload analisa todos os projetos que têm commits não enviados e os empacota como changesets no Gerrit. Cada change pode abranger múltiplos repositórios, mantendo a atomicidade da mudança.

Após a aprovação e merge no Gerrit, o manifesto precisa ser atualizado para refletir as novas revisões. Isso geralmente é feito manualmente ou por scripts de CI/CD.

Conflitos entre repositórios são comuns quando há dependências cruzadas. Por exemplo, se a biblioteca-core muda uma API, o servico-autenticacao que a utiliza pode quebrar. A solução é coordenar as mudanças: atualizar primeiro a biblioteca, depois os dependentes, e só então atualizar o manifesto.

6. Boas práticas e armadilhas comuns

Quando NÃO usar Repo: Para projetos pequenos (menos de 5 repositórios) ou com baixo acoplamento, o Repo adiciona complexidade desnecessária. Git Submodules ou até mesmo scripts simples podem ser mais adequados.

Cuidados com repo sync --force-sync: Este comando força a sincronização mesmo que haja conflitos locais, descartando mudanças não commitadas. Use com extrema cautela:

# Perigoso! Pode perder trabalho não commitado
repo sync --force-sync

Versionamento do manifesto: O manifesto em si deve ser versionado em um repositório Git. Isso permite rastreabilidade e rollback:

# No repositório de manifestos
git tag release-v2.1.0
repo init -u <url> --repo-rev=release-v2.1.0

7. Comparação com alternativas do ecossistema Git

Repo vs. Git Submodules: Submodules são mais leves e nativos do Git, mas têm workflow complexo para contribuição. Repo oferece repo upload e gerenciamento centralizado de revisões, ideal para equipes grandes.

Repo vs. Git Subtree: Subtree incorpora o histórico de um repositório dentro de outro, facilitando o desenvolvimento local, mas poluindo o histórico do repositório principal. Repo mantém os repositórios separados, com histórico independente.

Repo vs. Monorepo: Monorepo (um único repositório gigante) simplifica dependências e refatorações, mas escala mal em termos de ferramentas e permissões. Repo permite escalar horizontalmente com repositórios independentes, mantendo a coordenação.

8. Cenário prático: configurando um projeto multi-repositório do zero

Vamos criar um projeto com três repositórios interdependentes: uma biblioteca core, um serviço de autenticação e uma aplicação web.

Passo 1: Crie o manifesto

Crie um repositório Git chamado manifestos e adicione default.xml:

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote name="github"
          fetch="https://github.com/minha-org/" />

  <default revision="main"
           remote="github"
           sync-j="4" />

  <project path="core/lib"
           name="biblioteca-core" />

  <project path="services/auth"
           name="servico-autenticacao" />

  <project path="frontend/webapp"
           name="aplicacao-web" />
</manifest>

Commite e faça push para o GitHub.

Passo 2: Inicialize o workspace

mkdir meu-projeto && cd meu-projeto
repo init -u https://github.com/minha-org/manifestos.git
repo sync

Agora você tem os três repositórios clonados nos diretórios especificados.

Passo 3: Fluxo completo de desenvolvimento

# Cria branch temático em todos os projetos
repo start adiciona-login --all

# Modifica a biblioteca core
cd core/lib
echo "função de hash de senha" >> lib.py
git commit -a -m "Adiciona hash de senha seguro"

# Modifica o serviço de autenticação
cd ../../services/auth
echo "usa hash da biblioteca core" >> auth.py
git commit -a -m "Integra hash de senha no serviço"

# Envia ambos para revisão
repo upload

Passo 4: Atualize o manifesto após aprovação

Após os merges, descubra os novos hashes dos commits:

cd core/lib
git rev-parse HEAD
# Saída: f1e2d3c4b5a6...

cd ../../services/auth
git rev-parse HEAD
# Saída: a6b5c4d3e2f1...

Atualize o default.xml com essas revisões:

<project path="core/lib"
         name="biblioteca-core"
         revision="f1e2d3c4b5a6..." />

<project path="services/auth"
         name="servico-autenticacao"
         revision="a6b5c4d3e2f1..." />

Commite e faça push do manifesto atualizado. Agora todos os desenvolvedores que rodarem repo sync terão exatamente as mesmas versões.

Referências