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
Não está atualizado com as alterações da Julia, atualize-o:
git pull origin master
Veja o histórico:
git log –online
Agora estamos atualizado… abra a solution no Visual Studio, vamos fazer uma alteração, acrescente a linha abaixo na View Contact.cshtml:
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
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
Está onde deixamos… Crie um novo branch de desenvolvimento:
git checkout –b desenv
Abra a Solution e vamos alterar a View Contact.cshtml, mas em um lugar um pouco diferente:
Acrescentamos o endereço da Microsoft Brasil… Faça o commit:
git commit –am “Adicionado o endereço da Microsoft Brasil”
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
Baixou alguma coisa:
git log –oneline
Se executar o comando diff entre os dois últimos commits podemos ver a diferença:
git diff 867d9b 6bcccc
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
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
- O Git voltou o Head
- Aplicou os commits que não estavam lá, a alteração do Brian
- Depois reconstruiu a árvore
git log –oneline
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
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