Contents
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 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.
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.
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.
Mostraremos como subrepositórios funcionam no dia a dia.
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.
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.
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"
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.
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.
Finalizaremos este guia explicando como executar algumas operações incomuns, mas bastante úteis.
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
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.
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.
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.
Use hg update no subrepositório para mudar para uma revisão mais antiga.
Vá para o repositório principal. Use hg status para ver o estado do subrepositório.
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.
Adicione um repositório do Subversion como subrepositório.