Tutorial - Mesclando modificações
(This page in English: TutorialMerge). Esta página é a parte 7 de 9 da série BrazilianPortugueseTutorial. A parte anterior é BrazilianPortugueseTutorialExport, a próxima parte é BrazilianPortugueseTutorialConflict)
Em ../BrazilianPortugueseTutorialExport, nós aprendemos como compartilhar uma mudança com outra pessoa. Agora iremos demonstrar como fazer a mesclagem dessas mudanças com mudanças divergentes obtidas de outro repositório com um comando pull.
Primeiramente, nós devemos criar algo para mesclar. Vamos novamente criar um clone do repositório my-hello:
$ cd .. $ hg clone my-hello my-hello-desc updating working directory 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
Nós daremos a hello.c uma descrição em sua seção de comentários.
$ cd my-hello-desc $ vi hello.c
Vamos trocar a segunda linha, que é:
* hello.c
por esta linha:
* hello.c - hello, world
Salvemos, fechemos o editor, e consolidemos nossa alteração. Desta vez, nós economizamos um pouco de tempo através da opção -m para o comando commit, com a qual fornecemos um comentário diretamente, evitando que o editor de texto seja chamado:
$ hg commit -m "Add description of hello.c"
Neste ponto, nós fizemos uma mudança em hello.c no repositório my-hello-new-output , e outra mudança em hello.c no repositório my-hello-desc. Como nós fazemos para juntar essas duas linhas divergentes de desenvolvimento? Haverá algum problema quando tentarmos fazer um pull de um para o outro?
Isso funcionará sem problemas. Enquanto ainda estamos em my-hello-desc, vamos fazer o pull das mudanças de my-hello-new-output e ver o que acontece:
$ hg pull ../my-hello-new-output pulling from ../my-hello-new-output searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge)
O Mercurial nos diz que uma nova cabeça foi adicionada ao nosso repositório. Para ver essas cabeças podemos usar o comando heads:
$ hg heads changeset: 3:86794f718fb1 tag: tip parent: 1:82e55d328c8c user: mpm@selenic.com date: Mon May 05 01:20:46 2008 +0200 summary: Express great joy at existence of Mercurial changeset: 2:c3844fde99f0 user: mpm@selenic.com date: Tue May 06 20:10:35 2008 +0200 summary: Add description of hello.c
O Mercurial é distribuído com uma extensão capaz de mostrar um grafo em ASCII do histórico do repositório: a GraphlogExtension. Tudo o que você precisa fazer para que ela funcione é habilitá-la em seu arquivo de configuração ~/.hgrc (ou Mercurial.ini no Windows) através da adição da linha hgext.graphlog = na seção [extensions] dessa maneira:
[extensions] hgext.graphlog =
Ao fazermos isso, torna-se disponível um comando adicional chamado glog (veja tambem hg help glog):
$ hg glog o changeset: 3:86794f718fb1 | tag: tip | parent: 1:82e55d328c8c | user: mpm@selenic.com | date: Mon May 05 01:20:46 2008 +0200 | summary: Express great joy at existence of Mercurial | | @ changeset: 2:c3844fde99f0 |/ user: mpm@selenic.com | date: Tue May 06 20:10:35 2008 +0200 | summary: Add description of hello.c | o changeset: 1:82e55d328c8c | user: mpm@selenic.com | date: Fri Aug 26 01:21:28 2005 -0700 | summary: Create a makefile | o changeset: 0:0a04b987be5a user: mpm@selenic.com date: Fri Aug 26 01:20:50 2005 -0700 summary: Create a standard "hello, world" program
Neste grafo, podemos notar que acabamos de trazer a revisão 86794f718fb1 para o nosso repositório (por sera última adicionada, essa revisão se torna a nova tip). Como tanto a revisão 86794f718fb1 trazida como nossa própria c3844fde99f0 foram geradas a partir da mesma revisão base 82e55d328c8c (a revisão pai), uma linha divergente de desenvolvimento foi criada - o que chamamos de ramo. Note que nesse caso esse ramo tem vida curta, e essa separação pode ser gerenciada acilmente com o Mercurial.
Ao examinar a saída do comando pull, você pode pensar que ela parece bem similar àquela de BrazilianPortugueseTutorialShareChange. Então você poderia perguntar: por que não fazemos um simples update, como fizemos antes? Vamos tentar isso para ver o que acontece:
$ hg update abort: crosses branches (use 'hg merge' or 'hg update -C')
Algo está claramente diferente aqui. Mas como você pode ver, o Mercurial toma o cuidado de não fazer nada estúpido, de modo que se acontecer de ignorarmos por engano aquele aviso de "(+1 heads)" (uma cabeça a mais) após um pull, podemos estar seguros de que o Mercurial nos lembrará que provavelmente vamos precisar fazer alguma coisa aqui: devemos mesclar.
Mas antes de fazermos nossa primeira mesclagem vamos ver a partir de qual revisão estamos trabalhando. Para isso podemos usar o comando parents (veja Parent):
$ hg parents changeset: 2:c3844fde99f0 user: mpm@selenic.com date: Tue May 06 20:10:35 2008 +0200 summary: Add description of hello.c
Isso nos diz que nosso diretório de trabalho ainda está se referindo à revisão c3844fde99f0 - afinal, foi o que acabamos de consolidar. O que é importante de se entender aqui é que o comando pull que acabamos de fazer apenas trouxe aquela outra revisão para a área de armazenamento de nosso repositório. O pull não mudou nada no nosso diretório de trabalho. E dê mais uma olhada na saída de glog: uma revisão está marcada com um "@". Esse é o pai atual do diretório de trabalho: nós estamos "em" c3844fde99f0.
Então vamos finalmente fazer nossa mesclagem. Mesclar soa difícil, não é mesmo? Mas é na verdade bastante simples. Vamos seguir as instruções daquela última linha de saída do pull que fizemos anteriormente:
$ hg merge merging hello.c 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
E é apenas isso! O Mercurial foi capaz de gerenciar o merge automaticamente para você, porque os trechos modificados não conflitaram (uma mesclagem com conflitos será vista em BrazilianPortugueseTutorialConflict). Se olharmos agora para hello.c no diretório de trabalho, veremos que ele contém ambas as mudanças, de my-hello-new-output e de my-hello-desc.
Ao trabalhar com mudanças feitas por outras pessoas, este é o tipo de mesclagem que você fará em boa parte das vezes. Na maior parte das vezes é ainda mais simples que isso: os arquivos modificados por linhas divergentes de desenvolvimento são tipicamente disjuntos. Assim, na maioria das vezes, ao mesclar não haverá nenhum arquivo a ser mesclado.
Nós ainda não consolidamos o resultado de nossa mesclagem. Antes de fazermos isso, vamos ver o que o comando parents tem a nos dizer (usamos a abreviação par):
$ hg par changeset: 2:c3844fde99f0 user: mpm@selenic.com date: Tue May 06 20:10:35 2008 +0200 summary: Add description of hello.c changeset: 3:86794f718fb1 tag: tip parent: 1:82e55d328c8c user: mpm@selenic.com date: Mon May 05 01:20:46 2008 +0200 summary: Express great joy at existence of Mercurial
Ele está nos dizendo que o diretório de trabalho se refere a duas revisões: ele tem dois pais. E note que esses dois pais estão ordenados: o primeiro pai é nossa revisão anterior c3844fde99f0, na qual adicionamos aquela descrição em hello.c. Ela era o pai do diretório de trabalho no momento em que fizemos o comando merge. O segundo pai é a revisão 86794f718fb1, que nós trouxemos do outro repositório e que é também a revisão que agora está mesclada.
Outro glog agora mostra o símbolo @ nos dois pais:
$ hg glog @ changeset: 3:86794f718fb1 | tag: tip | parent: 1:82e55d328c8c | user: mpm@selenic.com | date: Mon May 05 01:20:46 2008 +0200 | summary: Express great joy at existence of Mercurial | | @ changeset: 2:c3844fde99f0 |/ user: mpm@selenic.com | date: Tue May 06 20:10:35 2008 +0200 | summary: Add description of hello.c | o changeset: 1:82e55d328c8c | user: mpm@selenic.com | date: Fri Aug 26 01:21:28 2005 -0700 | summary: Create a makefile | o changeset: 0:0a04b987be5a user: mpm@selenic.com date: Fri Aug 26 01:20:50 2005 -0700 summary: Create a standard "hello, world" program
Note que se você mudar de idéia e quiser desfazer essa mesclagem antes de consolidar qualquer coisa, fazer apenas um hg revert -r2 --all reverterá apenas o estado dos arquivos no diretório de trabalho para a revisão 2, e não mudará os pais do diretório de trabalho de volta para um único pai (veja Revert). Neste caso, para desfazer a mesclagem você deve usar "hg update ---clean -r.".
Para completar todo o procedimento de mesclagem, não devemos esquecer de consolidar estas mudanças locais, como foi sugerido na última linha da saída do comando merge que fizemos:
$ hg commit -m "Merged changes from my-hello-new-output"
Não deve haver nenhuma saída para esse comando. Nesse momento, a mesclagem está gravada na área de armazenamento do nosso repositório como uma nova revisão de mesclagem, e nós podemos explorá-la com glog:
$ hg glog @ changeset: 4:d2ecac0134d8 |\ tag: tip | | parent: 2:c3844fde99f0 | | parent: 3:86794f718fb1 | | user: mpm@selenic.com | | date: Tue May 06 23:44:19 2008 +0200 | | summary: Merged changes from my-hello-new-output | | | o changeset: 3:86794f718fb1 | | parent: 1:82e55d328c8c | | user: mpm@selenic.com | | date: Mon May 05 01:20:46 2008 +0200 | | summary: Express great joy at existence of Mercurial | | o | changeset: 2:c3844fde99f0 |/ user: mpm@selenic.com | date: Tue May 06 20:10:35 2008 +0200 | summary: Add description of hello.c | o changeset: 1:82e55d328c8c | user: mpm@selenic.com | date: Fri Aug 26 01:21:28 2005 -0700 | summary: Create a makefile | o changeset: 0:0a04b987be5a user: mpm@selenic.com date: Fri Aug 26 01:20:50 2005 -0700 summary: Create a standard "hello, world" program
Para mostrar as mudanças de nossa mesclagem, podemos ainda usar usar o comando annotate para mostrar informações de revisão por linha do arquivo. Note que a revisão 2 é a modificação que fizemos no repositório my-hello-desc e a revisão 3 foram as mudanças trazidas do repositório my-hello-new-output e mescladas com o repositório my-hello-desc.
$ hg annotate hello.c 0: /* 2: * hello.c - hello, world 0: * 0: * Placed in the public domain by Bryan O'Sullivan 0: * 0: * This program is not covered by patents in the United States or other 0: * countries. 0: */ 0: 0: #include <stdio.h> 0: 0: int main(int argc, char **argv) 0: { 0: printf("hello, world!\n"); 3: printf("sure am glad I'm using Mercurial!\n"); 0: return 0; 0: }
Vamos agora ver em BrazilianPortugueseTutorialConflict como lidar com situações onde foram feitas mudanças conflitantes.