Depois da série de Git básico (post um, dois e três) vamos começar a nos aprofundar nos comandos e poderemos entender o poder que está na nossa mão.

No post 3, o Brian usou o comando merge para integrar o código dobranch de desenvolvimento com o master. Depois a Julia atualizou abranch dela de desenvolvimento e depois integrou o código de desenvolvimento no branch Master. O nosso objetivo era conhecer maneiras de fazer o merge e lidar com conflito no Git, por isso os dois fizeram as alterações no mesmo local. Mas e se o Brian tivesse feito uma alteração em outro local… o merge é a melhor maneira de atualizar o seu Branch? É o que vamos ver nesse post.

No Git commits são ordenados, mas não sequênciais

Isso quer dizer que no Git, diferente dos controles de versão centralizados, não existe uma ordem, ou seja, no CVCS (Centralized Version Control System), o client se conecta com o server, faz o commit/checkin, pega o número, cada um na sua vez, numa fila.

O primeiro problema é que nos DVCS (Distributed Version Control System) não se tem um server! Cada repositório tem que poder fazer seucommit sem perguntar para um servidor qual o número do commit, por isso o uso do hashes SHA-1 na identificação.

O segundo problema de não ter um server é que os commits precisam ser locais, o que vale frisar, no Git todos os commits são locais!… E por isso não podem ser sequencial, pois não existe um lugar para saber quem é o próximo a fazer um commit, mas podem ser ordenados. Ou seja eu sei quem é o antecessor, ou o pai, e daí eu consigo montar a árvore de commits. O HEAD é o produto de todo esse encadeamento.

Portando, se o Git é descentralizado e não sequencial é possível reconstruir o meu histórico se puder trocar o ancestral/pai de um commit!

É exatamente isso o que o comando rebase faz, refaz o seu histórico, mais especificamente a sua branch. Por isso que ele não é a mesma coisa que o merge, que integra o seu código em outro branch. Vamos ver como ele funciona na prática.

Voltando ao repositório que criamos para o Brian, vamos ver qual a situação:

git status

e o histórico

git log –oneline

image

Não está atualizado com as alterações da Julia, atualize-o:

git pull origin master

image

Veja o histórico:

git log –online

image

Agora estamos atualizado… abra a solution no Visual Studio, vamos fazer uma alteração, acrescente a linha abaixo na View Contact.cshtml:

image

E faça o commit:

git commit –am “Adicionado e-mail de TI”

Lembre-se, a opção ‘a’ só vale se o commit for para uma edição de um arquivo, ou seja ele já existe no repositório. Repare também que eu fiz ocommit na Master, o que não é muito legal, mas tudo bem, é só para demonstração… É só mandar o commit para o servidor agora:

git push origin master

image

A Julia vai seguir as boas práticas, criar um branch para editar o código… Vá para o repositório dela e verifique como ele está primeiramente:

git status

E o seu histórico

git log –online

image

Está onde deixamos… Crie um novo branch de desenvolvimento:

git checkout –b desenv

image

Abra a Solution e vamos alterar a View Contact.cshtml, mas em um lugar um pouco diferente:

image

Acrescentamos o endereço da Microsoft Brasil… Faça o commit:

git commit –am “Adicionado o endereço da Microsoft Brasil”

image

A boa prática manda que antes de integrarmos o código no Branch Master verifiquemos se teve alteração, nesse exemplo além da alteração ser pequena foi em um campo texto, mas se fosse em algum lógica o objetivo de baixar e integrar antes de enviarmos para o servidor é saber se vai compilar com código novo e isso deve ser feito antes na máquina do desenvolvedor:

git checkout master

git pull origin master

image

Baixou alguma coisa:

git log –oneline

image

Se executar o comando diff entre os dois últimos commits podemos ver a diferença:

git diff 867d9b 6bcccc

image

Tá lá a alteração do Brian, e não é no mesmo local… Mas dá para ter mais certeza que isso? Que tal uma diferença entre branchs?

git diff master desenv

image

Não é mesmo no mesmo lugar! Nesse momento faríamos um merge dobranch Desenv para o Master e commit, mas já que podemos reescrever o histórico e não temos conflito, por que não refazer o branch Desenv como se fosse a partir do commit do Brian? Já que o commit dele já foi compartilhado e a Julia é a última a enviar alterações, não vamos fazer um merge disso e sim reescrever o branch. Em outro controle de versão teríamos que colocar a alteração em uma área reservada, destruir obranch, recriar a partir daquele commit ou checkin, fazer o merge da nossa alteração… Deixa pra lá, você entendeu que iria ser complicado. No Git só precisamos dizer para ele que queremos isso.

Vamos reescrever o branch Desenv:

git checkout desenv

git rebase master

image

  1. O Git voltou o Head
  2. Aplicou os commits que não estavam lá, a alteração do Brian
  3. Depois reconstruiu a árvore

git log –oneline

image

Agora o branch Desenv tem o commit com o e-mail adicionado pelo Brian

Vamos fazer o merge para integrar o código da Júlia?

git checkout master

git merge desenv

image

Depois do rebase o código ficou compatível com o Master, daí foi possível fazer o merge fast-forward.

Usando o rebase deixamos o histórico do código mais limpo e fácil de entender. Se você quer saber como ficaria o histórico usando merge use o comando reset que já foi explicando no post sobre merge, volte o código e refaça, compare os dois…

Dica: Não faça rebase em código já integrado e compartilhado, ou seja, diretamente na master, por exemplo. Altere o histórico do seu branch, no seu repositório, não no de terceiros, as consequências não são boas.

Com grandes poderes vem grande responsabilidades.

O comando não é só isso! É possível fazer um rebase interactive… Quer saber o que é? Continue acompanhando o blog.

Emmanuel Brandão