aragost Trifork: Mercurial Kick Start Exercises


Mercurial Básico

Vamos começar com os conceitos básicos do Mercurial. Acreditamos que o melhor modo de aprender é fazendo as coisas por si mesmo, por isso, sugerimos que você siga e execute os mesmos comandos que “Alice” e “Bob” executarem. Você saberá quem está executando o comando pela cor correspondente no terminal: O terminal de Alice é alaranjado e o do Bob é azul. Mais à frente, aparecerá uma personagem chamada Carla, cuja cor será verde.

Haverá alguns exercícios depois que cobrirão as mesmas situações dos exemplos feitos por Alice e Bob.

Contents

Instalação & Configuração

O Mercurial está disponível para a maior parte das plataformas, incluindo Windows, Mac OS X e GNU/Linux:

Windows:

Instale apenas o TortoiseHg, que cuidará da instalação completa do Mercurial e também de um outras ferramentas gráficas adicionais para examinar e manipular o repositório.

After installation, you will have a right-click menu in Windows Explorer that gives you access to the graphical tools. After logging out and in again, you will also have a hg and a thg program available in a Command Prompt. You can use the thg program to start the graphical TortoiseHg tools.

Tip

Embora os exemplos usem comandos Unix, eles podem facilmente ser traduzidos para outros comandos equivalentes aos linha de comando do Windows:

ls          -> dir
cat         -> type
echo "abc"  -> echo abc
Linux:

Por favor, instale o Mercurial usando seu gerenciador de pacotes. Se não encontrá-lo, então é possível instalar a partir do código-fonte: obtenha a versão estável mais recente, descompacte em algum lugar e execute make local a partir do diretório usado. Agora, crie um link do script hg para um diretório em seu PATH e tudo certo.

Para instalar o TortoiseHg, será necessária a biblioteca PyQt. Ela está disponível na maioria das distribuições. Siga estas instruções. Isto resultará no programa thg.

Mac OS X:
Recomendamos instalar o MacHg para um bom cliente nativo do Mercurial. MacHg vem empacotado com seu próprio Mercurial, de modo que não será necessário se preocupar com isso. Se você já usa o TortoiseHg no Windows, você ficará feliz em saber que também é possível instalá-lo no Mac OS X também.

Nós usaremos a linha de comando nos exemplos, mas em alguns momentos mostraremos o repositório pelo Workbench do TortoiseHg.

Após instalar o Mercurial, tente executar o comando hg version:

alice$ hg version
Mercurial Distributed SCM (version 2.2)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2012 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Está tudo bem se você vir um número de versão maior que 2.2. A versão 1.4 do Mercurial também é suficiente para nossos objetivos, mas sempre recomendamos atualizar para a versão mais nova disponível em sua plataforma Você pode verificar sua instalação com:

alice$ hg debuginstall 
Checking encoding (UTF-8)...
Checking installed modules (/usr/share/pyshared/mercurial)...
Checking templates (/usr/lib/python2.7/dist-packages/mercurial)...
Checking commit editor...
Checking username...
 no username supplied (see "hg help config")
 (specify a username in your configuration file)
1 problems detected, please check your install!

O nome do usuário normalmente está ausente logo após a instalação. Você pode defini-lo no arquivo de configuração. Em sistemas tipo Unix este arquivo é $HOME/.hgrc e no Windows, o arquivo é chamado de %HOME%\Mercurial.ini (veja hg help config para todas as possibilidades ou use thg userconfig se preferir). Crie o arquivo com este conteúdo:

[ui]
username = Firstname Lastname <example@example.net>

mas com seu próprio nome. Nós usaremos Alice <alice@example.net> primeiro e seu terminal parece como os dois que você viu acima. A execução de hg debuginstall não relatará nenhum problema a partir de agora.

Comandos Básicos

We will now make a swift tour through the basic commands. These are all similar to older systems such as Subversion or even CVS. If you are using TortoiseHg under Windows, then you might want to also refer to their quick start guide.

Criando um Repositório

Crie um repositório com o comando hg init:

alice$ ls
alice$ hg init example
alice$ ls
example

O comando hg init criou um novo diretório que é o novo repositório. Poderíamos ter criados o diretório example e depois executado o comando hg init sem parâmetros dentro desse diretório para torná-lo um repositório.

Entrando no diretório example pode-se observar:

alice$ cd example
alice$ ls -a
.
..
.hg

Como você pode ver, o novo diretório está vazio exceto por um diretório .hg na raiz do diretório. O Mercurial armazena o histórico e outros arquivos administrativos neste diretório.

Criando um Changeset

Vamos criar um arquivo e perguntar ao Mercurial sobre o estado do repositório:

alice$ echo "Hello World" > hello.txt
alice$ hg status
? hello.txt

A resposta mostra que hello.txt é desconhecido pelo Mercurial, isto é, ainda não é rastreado. Nós podemos adicionar o arquivo:

alice$ hg add hello.txt
alice$ hg status
A hello.txt

isto muda o estado para “A” de “Adicionado”. O arquivo ainda não está armazenado no histórico do repositório — Faremos isso através da consolidação (commit):

alice$ hg commit -m "First version of hello"

Nenhuma mensagem indica sucesso — O Mercurial não interrompe o fluxo de trabalho a não ser que seja necessário.

Inspecionando o Histórico

Pode-se ver o changeset recem criado com o comando hg log:

alice$ hg log
changeset:   0:d312da7770f4
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:10:05 2010 +0000
summary:     First version of hello

Sucesso, você fez uma consolidação com o Mercurial! Vamos modificar o arquivo e pedir ao Mercurial para mostrar como os arquivos na cópia de trabalho diferem da última revisão:

alice$ echo "Goodbye!" > hello.txt
alice$ hg diff
diff -r d312da7770f4 hello.txt
--- a/hello.txt Wed Mar 10 20:10:05 2010 +0000
+++ b/hello.txt Mon Mar 10 20:10:10 2010 +0000
@@ -1,1 +1,1 @@
-Hello World
+Goodbye!

Podemos mudar de ideia e reverter o arquivo de volta ao estado em que ele se encontrava antes:

alice$ hg revert hello.txt
alice$ cat hello.txt
Hello World

Vamos fazer outra mudança no nosso arquivo:

alice$ echo >> hello.txt
alice$ echo "by Alice." >> hello.txt
alice$ hg commit -m "Added author." 

Podemos pedir para o Mercurial para mostrar as anotações sobre o arquivo, isto é, mostrar quando cada linha foi modificada pela última vez:

alice$ hg annotate hello.txt
0: Hello World
1:
1: by Alice.

Muito bem, o Mercurial nos diz que a primeira linha é da revisão 0 e que as duas últimas linhas são da revisão 1. O TortoiseHg oferece uma interface muito boa dessa ferramenta de notas. Veja thg annotate.

As anotações dos arquivos são uma ajuda inestimável para consertar defeitos: após achar o defeito, você pode usar hg annotate para descobrir quando a linha problemática foi introduzida no arquivo. Em seguida, usar hg log para recuperar a mensagem de consolidação associada, que deve explicar o que a pessoa responsável estava tentando fazer quando mudou a linha:

alice$ hg log -r 1
changeset:   1:2e982bdc137f
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:15:00 2010 +0000
summary:     Added author.

Os dois changesets no nosso repositório estão em sequência. Isto é melhor ilustrado com a ajuda da extensão graphlog. Para manter o núcleo do Mercurial enxuto, muitas funcionalidades são delegadas a extensões. Muitas das melhores extensões são entregues com o Mercurial e são fáceis de serem habilitadas. Para habilitar a extensão graphlog, Alice simplesmente adiciona:

[extensions]
graphlog =

ao arquivo de configuração dela. Ela pode, então, executar hg help graphlog para aprender um pouco mais sobre a extensão. Esta extensão é particularmente simples, ela apenas fornece a Alice um novo comando:

alice$ hg glog
@  changeset:   1:2e982bdc137f
|  tag:         tip
|  user:        Alice <alice@example.net>
|  date:        Wed Mar 10 20:15:00 2010 +0000
|  summary:     Added author.
|
o  changeset:   0:d312da7770f4
   user:        Alice <alice@example.net>
   date:        Wed Mar 10 20:10:05 2010 +0000
   summary:     First version of hello

Isto mostra como o changeset 2e982bdc137f` é um filho do changeset ``d312da7770f4. O mesmo pode ser visto pelo thg log:

alice-log.png

Os arquivos na cópia de trabalho são sempre sincronizados com um changeset particular. No thg log, isto é mostrado pelo círculo no grafo de changesets. No resultado apresentado pelo hg glog, a revisão-pai do diretório de trabalho é destacado pelo símbolo @.

Viagem no Tempo

Você pode mudar a revisão-pai da cópia de trabalho através do comando hg update. Isto é útil para voltar e examinar versões antigas do seu código. Por exemplo, se você precisa testar uma versão antiga do seu software com um novo sistema operacional. Aqui, Alice apenas voltará à revisão 0 para olhar para seu arquivo antigo. Ela primeiro usa o comando hg parents para confirmar onde que ela está no grafo:

alice$ hg parents
changeset:   1:2e982bdc137f
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:15:00 2010 +0000
summary:     Added author.

e aí ela dá o pulo:

alice$ hg update 0
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ hg parents
changeset:   0:d312da7770f4
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:10:05 2010 +0000
summary:     First version of hello

Seu arquivo está agora de volta à versão antiga:

alice$ cat hello.txt
Hello World

mas a versão mais nova não foi perdida. O Mercurial pode facilmente recuperá-la para Alice:

alice$ hg cat -r 1 hello.txt
Hello World

by Alice.

O comando hg cat é útil para rapidamente ver o conteúdo de versões antigos dos arquivos ao invés de usar hg update para mudar o diretório de trabalho inteiro para a revisão desejada. Voltar para a revisão da ponta (tip) é fácil. basta executar hg update sem nenhum parâmetro:

alice$ hg update
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ cat hello.txt
Hello World

by Alice.

Exercícios

  1. Use hg init para criar um repositório chamado kick-start.

  2. Entre no diretório kick-start e crie alguns arquivos. Marque-os para adição com o comando hg add. Você pode usar hg revert para cancelar uma adição pendente se mudar de ideia.

  3. Use hg commit para consolidar os arquivos. Um editor abrirá se você não especificar uma mensagem de consolidação na linha de comando.

  4. Produza mais algumas consolidações (commits).

  5. Use hg update para atualizar o diretório de trabalho para uma revisão antiga qualquer. Note como a saída de hg parents muda e compare-a com hg tip, que permanece fixo quando se usa hg update.

  6. Tente fazer uma consolidação enquanto o diretório de trabalho está em uma revisão antiga. Você será avisado de que acabou de criar uma nova “head” (changeset localizado na extremidade do grafo) — uma head é um changeset que não tem descendentes. Use hg heads para listar as heads do repositório e veja o grafo pelo thg log.

    Note como o changeset anterior aparece como changeset-pai do changeset recem criado pelo comando hg parents. Isto é um invariante do Mercurial: a revisão-pai da cópia de trabalho se torna a revisão-pai do próximo changeset.

  7. Múltiplas heads representam múltiplas linhas divergentes de desenvolvimento. Você pode mesclar manualmente essas linhas usando hg merge. Se suas mudanças não se sobreporem, então a mesclagem será fácil. Caso contrário, você terá de resolver os conflitos — mais sobre isso depois.

    Note como hg parents mostra os dois changesets depois de um hg merge Isto significa que o próximo changeset terá duas revisões-pai. Você pode vê-los pelo hg glog ou thg log depois da consolidação.

Trabalhando com Outras Pessoas

Até agora, o repositório de Alice está no seu diretório particular. Seu colega, Bob, também precisa trabalhar no projeto. Nos mostraremos os comandos dele em uma cor de fundo azul claro.

Fazendo um Clone

Nesses exemplos, nós faremos Alice e Bob compartilharem um mesmo sistema de arquivos. Mas poderia muito bem ser pela a rede da empresa. Mudanças também podem ser compartilhadas por SSH e HTTP — algumas pessoas usam e-mails e pen drives para levar os changesets de um lugar para outro em um formato binário produzido pelo comando hg bundle. Ao invés disso, Bob usa o comando hg clone com o caminho relativo do repositório da Alice:

bob$ hg clone ../alice/example
destination directory: example
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Bob agora tem uma cópia completamente independente do repositório da Alice. Ele consegue fazer esse clone desde que tenha permissão de leitura aos arquivos de Alice. Bob pode fazer seus próprios changesets no seu clone, sem afetar Alice. Ele faz algumas mudanças:

bob$ cd example
bob$ echo "Goodbye!" > goodbye.txt
bob$ echo >> goodbye.txt
bob$ echo "by Bob." >> goodbye.txt
bob$ hg add
adding goodbye.txt
bob$ hg commit -m "My goodbye file." 
bob$ echo >> hello.txt
bob$ echo "not by Bob." >> hello.txt
bob$ hg commit -m "Not my file." 

O repositório agora tem quatro changesets:

bob-log.png

Comparando Clones

O clone sabe de onde se originou e Bob pode perguntar se há algum changeset no clone de Alice que ele ainda não tem:

bob$ hg incoming
comparing with /home/alice/example
searching for changes
no changes found

Ele também pode perguntar ao Mercurial qual changeset que Alice ainda não tem:

bob$ hg outgoing
comparing with /home/alice/example
searching for changes
changeset:   2:659410363fe0
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:00:00 2010 +0000
summary:     My goodbye file.

changeset:   3:51b2976b01e8
tag:         tip
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:03:00 2010 +0000
summary:     Not my file.

Isto faz sentido: Bob criou dois novos changesets no seu próprio clone, logo Alice ainda não os tem.

Movendo Changesets

Bob não tem acesso à escrita para os arquivos no diretório home de Alice e, portanto, não pode escrever seus changesets no clone dela. Mas ele pode permitir que ela saiba que ele fez um clone e pedir para ela obter seus changesets. Ela faz isso usando o comando hg pull. Anted de usar o comando, ela registra o caminho do clone de Bob no arquivo .hg/hgrc. Este arquivo é uma configuração local do repositório. Ela digita:

[paths]
default = /home/bob/example

para fazer com que o clone de Bob seja o alvo padrão para o hg pull e outros comandos que envolvem um repositório remoto. Ela pode ver o que será puxado com hg incoming:

alice$ hg incoming
comparing with /home/bob/example
searching for changes
changeset:   2:659410363fe0
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:00:00 2010 +0000
summary:     My goodbye file.

changeset:   3:51b2976b01e8
tag:         tip
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:03:00 2010 +0000
summary:     Not my file.

Note como isto é simétrico a quando Bob executou hg outgoing no seu clone. Ela decide ir adiante e puxar os changesets de Bob:

alice$ hg pull
pulling from /home/bob/example
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files
(run 'hg update' to get a working copy)

A mensagem final é uma indicação para que Alice saiba que a revisão-pai de sua cópia de trabalho não mudou:

alice-pull.png

A revisão-pai permaneceu inalterada já que Alice pode ter trabalhado em uma mudança própria, e pode não estar interessada em atualizar imediatamente. Caso tiver, ela atualiza:

alice$ hg update
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Você quase sempre irá querer executar hg update depois de hg pull e por essa razão, há um atalho: hg pull -u automaticamente irá atualizar depois depuxar novas mudanças.

Linhas Divergentes de Desenvolvimento

Alice mexe no arquivo hello.txt mais um pouco:

alice$ hg diff
diff -r 51b2976b01e8 hello.txt
--- a/hello.txt Thu Mar 11 10:03:00 2010 +0000
+++ b/hello.txt Thu Mar 11 11:00:00 2010 +0000
@@ -1,4 +1,4 @@
-Hello World
+Hello, Wonderful World!

 by Alice.
alice$ hg commit -m "Happy, happy!" 

Ao mesmo tempo, Bob também está trabalhando e cria um novo arquivo:

bob$ echo "Welcome!" > welcome.txt
bob$ hg add
adding welcome.txt
bob$ hg commit -m "Welcome file." 

Tanto Alice quanto Bob fizeram um changeset que é filho de 51b2976b01e8, mas nenhum deles sabe a respeito disso uma vez que ainda não puxaram ou empurraram as mudanças um do outro. Alice decide puxar de Bob:

alice$ hg pull
pulling from /home/bob/example
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 que aconteceu? Como mencionado anteriormente, uma head é um changeset que não tem filhos. Como Alice e Bob fizeram mudanças diferentes baseadas em 51b2976b01e8, agora há dois changesets no repositório sem filhos. Essas pontas são linhas divergentes de desenvolvimento, como pode ser observado pelo thg log:

alice-heads.png

As duas linhas de desenvolvimento devem ser reconciliadas, e o comando hg merge é o modo de se fazer isso:

alice$ hg merge
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

Não houve conflitos pois Alice e Bob editaram arquivos diferentes. Após hg merge, a cópia de trabalho tem duas revisões-pai — é assim que se sabe que uma mesclagem está em progresso se você deixar seu computador esquecer o que estava fazendo antes. A mesclagem ainda não está consolidada já que você pode precisar editar os arquivos e compilar o programa. Mesmo que não tenha havido sobreposição de edição (conflitos), ainda pode acontecer conflitos semânticos que o Mercurial não pode detectar. É uma boa ideia, portanto, executar um conjunto de testes depois de uma mesclagem. No nosso caso, Alice vai em frente com a consolidação:

alice$ hg commit -m "Merge with Bob." 

As duas pontas agora estão unidas no grafo de changesets:

alice-merged.png

O changeset da mesclagem contém mudanças de ambas as revisões-pai. Você deve pensar em um changeset de mesclagem como uma maneira de dizer ao mundo: isto é como você deve combinar o changeset X com o changeset Y. Quando outros puxarem o changeset da mesclagem do seu repositório, ele irá para o lugar correto no grafo de changesets. Portanto, outros não terão mais de fazer a mesma mesclagem. Podemos ver isso quando Bob puxa de Alice:

bob$ hg pull
pulling from /home/alice/example
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
bob-pull.png

Note como os changesets aparecem em ordens diferentes no repositório de Bob e no repositório de Alice — o changeset “Happy, happy!” está em ordem trocada com o changeset do “Welcome file.” Changesets sempre mantêm seus ID, o número hexadecimal longo, mas o número de revisão pode mudar. O número de revisão depende simplesmente na ordem na qual os changesets são adicionados a um repositório, e quando duas pessoas puxam changesets uma da outra, elas certamente terminarão com números de revisão diferentes para os mesmos changesets. Portanto, você deve sempre se referir a um changeset pelo seu ID quando falar com outros. Mas você ainda pode usar os números de revisão com comandos como hg update que funcionam internamente no repositório local.

Exercícios

  1. Criar um repositório e fazer algumas consolidações nele.

  2. Faça dois clones do seu repositório. Note como os arquivos .hg/hgrc registram a localização do repositório original. O comando hg paths mostrará os caminhos definidos.

  3. Adicione outro caminho (path) em .hg/hgrc tal como:

    bob = /home/bob/test
    

    Você conseguirá executar hg pull bob, hg outgoing bob, etc. É uma boa ideia adicionar atalhos para repositórios de pessoas com quem você colabora frequentemente.

  4. Experimente puxar (pull) e empurrar (push) mudanças. Note como o diretório de trabalho não muda mesmo quando novo histórico é adicionado ao repositório. A revisão-pai da cópia de trbalho só muda com hg update.

O Mercurial suporta colaboração desconectada. Isto funciona através da troca de changesets através dos chamados bundles. Um bundle (feixe) é uma representação binária comprimida de um número de changesets.

  1. Crie um novo changeset em um de seus repositórios. Use hg bundle --base X out.bundle para criar um bundle no arquivo out.bundle que conterá todas as mudanças seguintes à X. A ideia é que você use o último changeset compartilhado entre você e o alvo como base.

  2. Vá para o repositório alvo e use hg unbundle para importar os changesets do bundle. Neste exemplo, você provavelmente terá ambos os repositórios na sua máquina, mas na realidade os dois repositórios poderiam estar em máquinas que não estão online ao mesmo tempo. Neste caso, os bundles oferecem um mode para mover os changesets por e-mail, por exemplo.

Você pode também publicar seu repositório via HTTP:

  1. Execute hg serve em um repositório. Você agora pode acessar o repositório no browser com http://localhost:8000. Tente iniciar um servidor para os repositórios de Alice e Bob.

  2. Tente puxar de um repositório publicado pelo hg serve.

Note que hg serve tem como objetivo uma publicação ad hoc. Para uma publicação permanente, você pode por exemplo usar o Apache com o script hgweb.cgi que é fornecido junto com o Mercurial.

Resumo

Você viu os comandos básicos do Mercurial e viu como o grafo do histórico funciona. Os comandos importantes são:

E não menos importante: