Dev (Back & Front)ARTIGO

Dica Git da semana: rebasing revisitado

A dica Git desta semana é sobre como usar rebase para se mover entre os branches.

Rebasing interativo

No meu artigo passado sobre rebasing, discuti o conceito de rebasing, a capacidade de criar novo histórico repetindo commits passados em uma ordem diferente. Na verdade, a maior parte desse artigo acabou discutindo rebasing interativo, que permite que você altere a ordem de confirmações, juntar dois (ou mais) commits em um único e ainda remover commits do histórico.

No entanto, o rebasing também desempenha outro papel fundamental na maneira como os desenvolvedores frequentemente interagem com git, movendo um branch inteiramente para um outro ponto a sua frente.

Branches rebasing

Vamos dizer que você está trabalhando em um recurso que se ramifique de um ponto conhecido, e agora você quer commitá-lo para o repositório. Vamos supor que o histórico se parece com:

A ? B ? C ? 1 ? 2 ? 3

Em que C era o ponto de master no início do branch do recurso. Se o branch remoto mudou desde então (por exemplo, a “F”), temos a opção de criar um merge node “G”, ou mover os nossos recursos para a frente do branch, com base em onde remoto está agora:

A ? B ? C ? D ? E ? F ? G // Merge node
\? 1 ? 2 ? 3 ?/

A ? B ? C &rarr D ? E ? F ? 1 ? 2 ? 3

Alguns desenvolvedores ou grandes equipes de desenvolvimento possuem uma preferência para criar merge nodes, não só como uma forma de evitar problemas (o merge node pode ser testado), mas também como uma forma de documentar de onde veio, em primeiro lugar. Algumas equipes inclusive criam merge nodes quando não são necessários (como a opção git pull – no-ff).

Outros desenvolvedores gostam de tentar manter um número mínimo de merge nodes para tentar (na medida do possível) ter um histórico linear no repositório.

De qualquer maneira, embora não haja uma resposta certa, o Git lhe permite fazer as duas coisas – dependendo do que seja a resposta certa está para você.

Exemplo de rebase

Se eu quisesse alcançar o histórico secundário, poderia escrever um script rebase interativo, que selecionou as primeiras mudanças D, E e F, seguidos por 1, 2 e 3. No entanto, fazer isso manualmente estaria propenso a erros, especialmente se o branch mudou mais do que alguns ccommits.

É aí que git rebase onto entra em jogo.

Se os branches acima foram nomeados master (para A..G) e feature (para 1..3), podemos transplantar feature para a frente com:

$ git rebase master feature
First, rewinding head to replay your work on top of it...
Applying: 1
Applying: 2
Applying: 3

Isso leva o conjunto de alterações no feature que não estão no master, aplica-os para onde o master está agora, e depois o nomeia como o novo branch feature.

O segundo argumento pode ser otimizado, se já estamos no branch feature:

$ git branch
* feature
master
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: 1
Applying: 2
Applying: 3
72366d5 3
8da923e 2
a4fe060 1
65521f9 F
62f8b88 E
e210d31 D
94c037d C
668b955 B
a34bd14 A

Uma boa propriedade do git rebase é que ele não irá duplicar deltas que são iguais. Então, se precisamos de uma mudança particular para uma correção de bug, ele não vai se recaplicar isso:

$ git checkout master
Switched to branch 'master'
$ git cherry-pick 8da923e
[master cf6d845] 2
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 2
$ git log --oneline
cf6d845 2
65521f9 F
62f8b88 E
...
$ git rebase master feature
First, rewinding head to replay your work on top of it...
Applying: 1
Applying: 3
$ git log --oneline
ba0ce72 3
6979b71 1
cf6d845 2
65521f9 F
62f8b88 E
...

Neste caso, nós escolhemos o 2 a partir do branch e, em seguida, aplicamos as alterações dos recursos. Esta é uma maneira rápida de fazer um rebase interativo, se você souber que quer realizar uma única mudança.

Rebasing onto

Então, onde é que o –onto entra em cena? Ele é usado para uma cirurgia grave de tree, que basicamente permite que você tome um conjunto de commits, e transplante-os para um node diferente.

No nosso exemplo, vamos dizer que tinha sido criado dois branches de recursos – feature1 e feature2 -, que eram logicamente independentes, mas nós acabamos desenvolvendo de um modo que o feature2 ramificou o feature1. Nós teríamos algo parecido com:

Ma ? Mb ? Mc ? Master
\ ? F1a ? F1b ? Feature1
\ ? F2a ? F2b ? Feature2

Agora vamos dizer que queremos empurrar o feature2 para o master, mas não queremos empurrar o feature1 para o master ainda (talvez porque não esteja pronto). Podemos realizar o transplante com um rebase git rebase –onto:

$ git log --oneline feature2
986d8ac F2b
625bcde F2a
fb24802 F1b
d8f7e48 F1a
3cd0af7 Mc
addd99a Mb
7ad7ead Ma
$ git rebase --onto master feature1 feature2
First, rewinding head to replay your work on top of it...
Applying: F2a
Applying: F2b
$ git log --oneline feature2
b3e017a F2b
ac65d2e F2a
3cd0af7 Mc
addd99a Mb
7ad7ead Ma
$ git log --oneline feature1
fb24802 F1b
d8f7e48 F1a
3cd0af7 Mc
addd99a Mb
7ad7ead Ma
$ git log --oneline master
3cd0af7 Mc
addd99a Mb
7ad7ead Ma

Nós já transplantamos um pouco entre o feature1 e o feature2 para o branch master atual. Na verdade, estamos fazendo um git cherry-pick de todas as mudanças entre o feature1 e o feature2 para o ponto atual de master; em seguida, reiniciaremos o branch feature2 (que está atualmente ativado) para apontar para o novo local. Nosso repositório git agora está assim:

Ma ? Mb ? Mc ? Master
\ ? F1a ? F1b ? Feature1
\ ? F2a ? F2b ? Feature

Resumo

Rebasing é uma maneira extremamente conveniente de atualizar seu repositório local para a versão atual do branch em um servidor remoto, e é usado com frequência. Também pode ser usado para executar múltiplas operações cherry-pick e reordenar (de forma local) o histórico, a fim de criar novas versões daqueles dele.

Como acontece com qualquer ferramenta, o cuidado deve ser tomado para não reordenar as alterações que foram previamente disponibilizadas (por exemplo, através do GitHub) já que a criação de um novo histórico provoca uma divergência na linha do tempo, o que provoca redemoinhos no espaço-tempo contínuo. Ou, pelo menos, incomoda as pessoas.

***

Texto original disponível em: http://alblue.bandlem.com/2011/06/git-tip-of-week-rebasing-revisited.html

é guru em Eclipse e fanático pelo Mac.

Ver perfil