aragost Trifork: Mercurial Kick Start Exercises


Subrepositórios

Contents

Introdução

O Mercurial tem uma funcionalidade chamada de subrepositórios. Ela permite tratar uma coleção de repositórios como um grupo.

Reuso de código é um conceito importante em arquitetura de software. Subrepositórios suportam um modo de reuso, através do compartilhamento de bibliotecas e módulos na forma de subrepositórios para que seu projeto possa usá-los.

Subrepositórios podem ser aninhados.

Os comandos básicos quando se usa subrepositórios são os mesmos que os de repositórios comuns. Contudo, um grupo significativo de comandos aceita a opção --subrepo para trabalhar recursivamente nos subrepositórios.

Ao executar um comando a partir de um diretório de trabalho de um subrepositório, o comportalmento será o mesmo de um repositório normal. O subrepositório não “sabe” que está dentro de outro repositório.

Subrepositórios

Subrepositórios são como repositórios normais, portanto precisamos primeiro criar um repositório e outro dentro dele, usando o comando hg init:

alice$ hg init mainrepo
alice$ cd mainrepo
alice$ hg init subrepo
alice$ ls
subrepo

Criamos dois repositórios, um aninhado no outro. Isto não faz o repositório interno ser um subrepositório. Para isto, é necessário que seja marcado como tal no repositório que o contém.

A marcação de um subrepositório é feita pela criação de uma declaração no arquivo especial .hgsub. Isto é feito adicionando a seguinte linha:

alice$ echo subrepo = subrepo > .hgsub
alice$ hg add .hgsub

Note

A maneira de interpretar o arquivo .hgsub é como um conjunto de linhas na forma:

onde/colocar/o/subrepo = onde/pegar/o/subrepo

O lado esquerdo é o caminho relativo à raiz do seu clone e diz ao Mercurial onde colocar o subrepositório no seu clone.

O lado direito é ou um caminho relativo ao lugar de onde clonar, ou um caminho absoluto. Se for um caminho relativo, então se você executar hg clone mainrepo my-main o Mercurial criará um novo repositório em my-main/onde/colocar/o/subrepo e o preencherá com os changesets obtidos de onde/pegar/o/subrepo.

No caso simples em que você tem ‘subrepo = subrepo’, o resultado é o Mercurial fazendo os seguintes comandos:

$ hg clone http://server/repos/repo my-repo
$ cd repo
$ hg clone http://server/repos/repo/subrepo
$ cd ..

e se você tivesse usado subrepo = ../subrepo, então os comandos seriam:

$ hg clone http://server/repos/repo my-repo
$ cd repo
$ hg clone http://server/repos/subrepo
$ cd ..

em que a última URL é a versão normalizada de http://server/repos/repo/../subrepo.

Subrepositórios Remotos

Podemos também querer linkar um repositório remoto ao nosso projeto. Este procedimento é parecido com o anterior:

alice$ hg clone http://www.selenic.com/repo/hello remoterepo
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ echo remoterepo = http://www.selenic.com/repo/hello >> .hgsub
alice$ ls
remoterepo
subrepo

Note que clonamos o repositório no lugar em que ele deveria estar, pois só adicionar a declaração no arquivo .hgsub não fará nada a não ser que haja um repositório e sua respectiva localização.

Subrepositórios do Subversion

Uma empresa pode ter algumas bibliotecas em repositórios do Subversion e, por diferentes razões, não convertê-las para o Mercurial. Sem problema, o Mercurial pode ter repositórios do Subversion como subrepositórios. O procedimento é como o acima, com exceção de que [svn] deve ser prefixado na URL da localização no arquivo .hgsub.

Portanto, a adição de um repositório do SVN acontece da seguinte forma:

alice$ svn co http://mercurial.aragost.com/svn/hello/trunk svnrepo
A    svnrepo/hello.c
A    svnrepo/Makefile
A    svnrepo/README
 U   svnrepo
Checked out revision 10.
alice$ ls
remoterepo
subrepo
svnrepo
alice$ echo svnrepo = [svn]http://mercurial.aragost.com/svn/hello1/trunk >> .hgsub

Os comandos do Subversion devem ser usados dentro do subrepositório e os comandos do Mercurial no super repositório.

Trabalhando com Subrepositórios

Mostraremos como subrepositórios funcionam no dia a dia.

Consolidação

O estado pode, como sempre, ser visto pelo hg status mas você precisa adicionar a opção --subrepo para que seja recursivo pelos subrepositórios. Vemos que os arquivos dos subrepositórios não agem assim antes de consolidar o arquivo .hgsub.

alice$ hg status
A .hgsub

Nossas mudanças não estão consolidadas ainda, então faremos isto agora.

alice$ hg commit -m "Subrepositories added"
alice$ hg status
alice$ hg status --subrepos

O comportamento padrão da consolidação é primeiro ir pelos subrepositórios e consolidar cada um. Depois consolidar o repositório externo. A razão para isso é que os subrepositórios também são parte do projeto sob o controle de versão e, portanto, o registro da configuração de todo o projeto faz mais sentido. O estado resultante dos subrepositórios serão salvos em um arquivo especial chamado .hgsubstate. Este arquivo não deve ser editado pelo usuário, mas registra qual a versão do repositório que está linkada com o repositório.

Empurrando e Puxando

O Mercurial sempre tentará primeiro empurrar todos os subrepositórios antes de empurrar o repositório ao qual pertencem, garantindo assim que o repositório sendo empurrado não acabe referenciando uma versão inexistente do subrepositório.

Puxar, por outro lado, não é uma operação recursiva porque o Mercurial não sabe quais subrepositórios deve puxar antes que a atualização para um determinado changeset seja feita.

Uma Outra Versão

Como mensionado anteriormente, não se deve editar .hgsubstate. Mas se você quiser mudar o repositório para uma versão diferente, então vá para o subrepositório e atualize-o para a versão desejada. Você também tem de consolidar isso, da forma que atualizará o estado do subrepositório no arquivo .hgsubstate.

Primeiro, criamos algumas mudanças e as consolidamos no subrepositório, e a consolidação final é para atualizar e consolidar o arquivo .hgsubstate.

alice$ cd subrepo
alice$ echo a > a.txt
alice$ hg add a.txt
alice$ hg commit -m "A" 
alice$ echo b > b.txt
alice$ hg add b.txt
alice$ hg commit -m "B" 
alice$ echo c > c.txt
alice$ hg add c.txt
alice$ hg commit -m "C" 
alice$ cd ..
alice$ hg commit -m "Main repo"

Agora o repositório principal está linkado com a revisão mais nova do subrepositório. Só agora percebemos que queríamos é linkar o repositório com uma revisão mais nova do subrepositório. Isto é feito pela atualização do subrepositório para a revisão desejada e depois executando uma consolidação no repositório principal novamente para atualizar e consolidar o arquivo .hgsubstate.

alice$ cd subrepo
alice$ hg log
changeset:   2:50b194df1a80
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:12:05 2010 +0000
summary:     C

changeset:   1:00bd589746e0
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:11:05 2010 +0000
summary:     B

changeset:   0:3cf2ce324347
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:10:05 2010 +0000
summary:     A
alice$ hg update 1
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
alice$ cd ..
alice$ hg commit -m "Changed subrepo version"

Atualização sob Demanda

A atualização também executa ações sobre os subrepositórios, já que atualizar para outra revisão de um repositório implica ter de atualizar o subrepositório para uma outra revisão também.

Como mensionado, o arquivo .hgsubstate registra a revisão sendo usada do subrepositório. Portanto, não sabemos para qual versão o subrepositório deve ser atualizado antes que o arquivo .hgsubstate seja obtido. Sendo assim, o repositório é primeiro atualizado e os subrepositórios são atualizados em seguida de acordo com a versão definida no arquivo .hgsubstate. Isto também envolve puxar a partir do caminho do subrepositório, se a revisão declarada em .hgsubstate não tiver sido puxada antes.

Comandos que Atravessam os Limites do Repositório

Os comandos hg commit e hg update agem recursivamente pelos subrepositórios. Se não houver mudanças no subrepositório durante o comando, então não haverá consolidações neste subrepositório.

Outros comandos que podem operar sobre subrepositórios precisam da opção --subrepos para agir recursivamente.

Comandos que suportam esta opção são, por exemplo, hg add, hg archive, hg diff, hg incoming, hg outgoing, e hg status.

hg add com a opção --subrepos permite adicionar um arquivo ao subrepositório como se fosse um diretório comum do projeto.

Os outros comandos fazem o seu processamento normal, mas também agem sobre sobre os subrepositórios quando a opção --subrepos é fornecida.

Dicas e Truques

Finalizaremos este guia explicando como executar algumas operações incomuns, mas bastante úteis.

Convertendo uma Pasta em um Subrepositório

Um projeto pode conter uma pasta com alguns arquivos, que depois se tornam interessantes para serem usados por diferentes projetos. É claro que estes arquivos poderiam ser apenas copiados para um repositório, que seria usado como subrepositório por outros projetos. Contudo, isto significa perder o histórico precioso desses arquivos.

A melhor maneira de fazer isso é convertendo o diretório em um repositório, através da extensão convert e depois incluir este repositório como um subrepositório de diferentes projetos.

A primeira parte abaixo, cria nosso diretório, executa algumas mudanças e consolida os conteúdos.

alice$ mkdir folder
alice$ echo 'infolder' > folder/1.txt
alice$ hg add folder/1.txt
alice$ ls
folder
remoterepo
subrepo
svnrepo
alice$ hg commit -m "In folder" 
alice$ echo 'notinfolder' > 2.txt
alice$ hg add 2.txt
alice$ hg commit -m "Not in folder"

Agora, queremos fazer do diretório um subrepositório. Primeiro, torne o diretório em um subrepositório. Isto é feito convertendo o repositório original de Mercurial para Mercurial e depois usando um mapeamento de arquivo para especificar que queremos apenas um certo diretório.

O mapa de arquivo especifica a inclusão do diretório, além do mais, há uma renomeação que garante que o conteúdo do diretório folder será a raiz do novo repositório sendo criado. Em seguida, executamos a conversão e vemos que agora temos um repositório contendo apenas o changeset editado. Este repositório pode ser incluído como um subrepositório como descrito anteriormente.

alice$ echo 'include folder' > map.txt
alice$ echo 'rename folder .' >> map.txt
alice$ hg --config extensions.hgext.convert= convert --filemap map.txt . mynewsubrepo
initializing destination mynewsubrepo repository
scanning source...
sorting...
converting...
4 Subrepositories added
3 Main repo
2 Changed subrepo version
1 In folder
0 Not in folder
alice$ cd mynewsubrepo
alice$ hg update
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ hg log
changeset:   0:c505b3dd95a2
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:13:05 2010 +0000
summary:     In folder

Desanexando um Subrepositório

Se um subrepositório não é mais necessário para um projeto, então pode ser desanexado simplesmente removendo a linha de sua declaração no arquivo .hgsub. O comando hg status mostrará que os arquivos do subrepositório são desconhecidos e você pode simplesmente remover os arquivos.

Exercícios

  1. Use hg init e crie, edita e adicione o arquivo .hgsub para criar um repositório chamado kick-start e um subrepositório chamado mysubrepo.

  2. Use hg add e hg commit para fazer algumas consolidações. Tente ambos a partir do subrepositório e também com a opção --subrepos para que funcione através dos limites do repositório.

  3. Use hg update no subrepositório para mudar para uma revisão mais antiga.

  4. Vá para o repositório principal. Use hg status para ver o estado do subrepositório.

  5. Use hg commit para atualizar a revisão dos subrepositórios registrados. Como o arquivo .hgsubstate foi modificado, a consolidação não está vazia.

  6. Adicione um repositório do Subversion como subrepositório.