Reescrevendo a história com Git Rebase

No fluxo de trabalho fundamental do Git, você desenvolve um novo recurso em um branch de tópico dedicado e o mescla novamente em um branch de produção assim que estiver concluído. Isto faz git merge uma ferramenta integral para combinar ramos. No entanto, não é o único que o Git oferece.

Combinando ramificações mesclando-asCombinando ramificações mesclando-asCombinando ramificações mesclando-as
Combinando ramificações mesclando-as

Como alternativa ao cenário acima, você pode combinar os ramos com o git rebase comando. Em vez de amarrar as ramificações com uma confirmação de mesclagem, o rebase move toda a ramificação do recurso para a ponta de master como mostrado abaixo.

Combinando branches com git rebaseCombinando branches com git rebaseCombinando branches com git rebase
Combinando branches com git rebase

Isso serve para o mesmo propósito que git merge, integrando commits de diferentes branches. Mas há duas razões pelas quais podemos optar por um rebase em vez de uma mesclagem:

  • Isso resulta em um histórico de projeto linear.
  • Isso nos dá a oportunidade de limpar os commits locais.

Neste tutorial, exploraremos esses dois casos de uso comuns de git rebase. Infelizmente, os benefícios de git rebase venha em uma troca. Quando usado incorretamente, pode ser uma das operações mais perigosas que você pode realizar em um repositório Git. Portanto, também examinaremos cuidadosamente os perigos do rebase.

Pré-requisitos

Este tutorial pressupõe que você esteja familiarizado com os comandos básicos do Git e os fluxos de trabalho de colaboração. Você deve se sentir confortável em testar e enviar snapshots, desenvolver recursos em branches isolados, mesclar branches e enviar/puxar branches de/para repositórios remotos.

1. Rebase para uma história linear

O primeiro caso de uso que exploraremos envolve um histórico de projeto divergente. Considere um repositório onde sua ramificação de produção avançou enquanto você estava desenvolvendo um recurso:

Desenvolvendo um recurso em uma ramificação de recursosDesenvolvendo um recurso em uma ramificação de recursosDesenvolvendo um recurso em uma ramificação de recursos

Para rebasear o feature ramo para o master branch, você executaria os seguintes comandos:

1
git checkout feature
2
git rebase master

Isso transplanta o feature ramo de sua localização atual até a ponta do master filial:

Transplantando o branch feature para a ponta do branch masterTransplantando o branch feature para a ponta do branch masterTransplantando o branch feature para a ponta do branch master

Há dois cenários em que você gostaria de fazer isso. Primeiro, se o recurso dependia dos novos commits em master, agora teria acesso a eles. Em segundo lugar, se o recurso estivesse completo, agora seria configurado para uma mesclagem de avanço rápido em master. Em ambos os casos, o rebase resulta em um histórico linear, enquanto git merge resultaria em confirmações de mesclagem desnecessárias.

Por exemplo, considere o que aconteceria se você integrasse os commits upstream com uma mesclagem em vez de um rebase:

1
git checkout feature
2
git merge master

Isso nos daria uma confirmação de mesclagem extra no feature filial. Além do mais, isso aconteceria toda vez que você quisesse incorporar upstream commits em seu recurso. Eventualmente, o histórico do seu projeto estaria repleto de confirmações de mesclagem sem sentido.

Integrando upstream commits com uma mesclagemIntegrando upstream commits com uma mesclagemIntegrando upstream commits com uma mesclagem
Integrando upstream commits com uma mesclagem

Esse mesmo benefício pode ser visto ao se fundir na outra direção. Sem rebase, integrando o acabado feature ramo em master requer uma confirmação de mesclagem. Embora este seja realmente um commit de mesclagem significativo (no sentido de que representa um recurso concluído), o histórico resultante está cheio de bifurcações:

Integrando um recurso concluído com uma mesclagemIntegrando um recurso concluído com uma mesclagemIntegrando um recurso concluído com uma mesclagem
Integrando um recurso concluído com uma mesclagem

Quando você rebase antes da fusão, o Git pode avançar rapidamente master para a ponta de feature. Você encontrará uma história linear de como seu projeto progrediu no git log output—os commits em feature são cuidadosamente agrupados em cima dos commits em master. Este não é necessariamente o caso quando as ramificações são vinculadas com uma confirmação de mesclagem.

Fazendo rebase antes de mesclarFazendo rebase antes de mesclarFazendo rebase antes de mesclar
Fazendo rebase antes de mesclar

Resolução de Conflitos

Quando você corre git rebase, Git pega cada commit no ramo e os move, um por um, para a nova base. Se algum desses commits alterar a(s) mesma(s) linha(s) de código que os commits upstream, isso resultará em um conflito.

O git merge O comando permite resolver todos os conflitos da ramificação no final da mesclagem, que é um dos propósitos principais de uma confirmação de mesclagem. No entanto, funciona de maneira um pouco diferente quando você está rebaseando. Os conflitos são resolvidos por confirmação. Então se git rebase encontrar um conflito, ele interromperá o procedimento de rebase e exibirá uma mensagem de aviso:

1
Auto-merging readme.txt
2
CONFLICT (content): Merge conflict in readme.txt
3
Failed to merge in the changes.
4
....
5
When you have resolved this problem, run "git rebase --continue".
6
If you prefer to skip this patch, run "git rebase --skip" instead.
7
To check out the original branch and stop rebasing, run "git rebase --abort".

Visualmente, é assim que o histórico do seu projeto se parece quando git rebase encontra um conflito:

Os conflitos podem ser inspecionados executando git status. A saída é muito semelhante a um conflito de mesclagem:

1
Unmerged paths:
2
  (use "git reset HEAD ..." to unstage)
3
  (use "git add ..." to mark resolution)
4

5
    both modified:   readme.txt
6

7
no changes added to commit (use "git add" and/or "git commit -a")

Para resolver o conflito, abra o arquivo em conflito (readme.txt no exemplo acima), localize as linhas afetadas e edite-as manualmente para obter o resultado desejado. Em seguida, diga ao Git que o conflito foi resolvido ao preparar o arquivo:

Observe que esta é exatamente a mesma maneira que você marca um git merge conflito como resolvido. Mas lembre-se de que você está no meio de um rebase — você não quer esquecer o resto dos commits que precisam ser movidos. A última etapa é dizer ao Git para terminar o rebase com o --continue opção:

Isso moverá o restante dos commits, um por um, e se algum outro conflito surgir, você terá que repetir esse processo novamente.

Se você não deseja resolver o conflito, pode optar pelo --skip ou --abort bandeiras. O último é particularmente útil se você não tem ideia do que está acontecendo e só quer voltar para a segurança.

1
# Ignore the commit that caused the conflict

2
git rebase --skip
3

4
# Abort the entire rebase and go back to the drawing board

5
git rebase --abort

2. Rebase para limpar commits locais

Até agora, só usamos git rebase para mover galhos, mas é muito mais poderoso do que isso. Ao passar o -i sinalizador, você pode iniciar uma sessão de rebase interativa. O rebase interativo permite definir com precisão como cada confirmação será movida para a nova base. Isso lhe dá a oportunidade de limpar o histórico de um recurso antes de compartilhá-lo com outros desenvolvedores.

Por exemplo, digamos que você terminou de trabalhar em seu feature ramo e você está pronto para integrá-lo em master. Para iniciar uma sessão de rebase interativa, execute o seguinte comando:

1
git checkout feature
2
git rebase -i master

Isso abrirá um editor contendo todos os commits em feature que estão prestes a ser movidos:

1
pick 5c43c2b [Description for oldest commit]
2
pick b8f3240 [Description for 2nd oldest commit]
3
pick c069f4a [Description for most recent commit]

Esta listagem define o que feature branch vai ficar depois do rebase. Cada linha representa um commit e o pick O comando antes de cada hash de confirmação define o que acontecerá com ele durante o rebase. Observe que os commits são listados do mais antigo para o mais recente. Ao alterar esta listagem, você obtém controle total sobre o histórico do seu projeto.

Se você quiser alterar a ordem dos commits, basta reordenar as linhas. Se você quiser alterar a mensagem de um commit, use o reword comando. Se você deseja combinar dois commits, altere o pick comando para squash. Isso rolará todas as alterações nesse commit para o que está acima dele. Por exemplo, se você esmagou o segundo commit na listagem acima, o feature branch ficaria assim depois de salvar e fechar o editor:

Esmagando o segundo commit com um rebase interativoEsmagando o segundo commit com um rebase interativoEsmagando o segundo commit com um rebase interativo
Esmagando o segundo commit com um rebase interativo

O edit comando é particularmente poderoso. Quando atingir o commit especificado, o Git pausará o procedimento de rebase, como quando encontra um conflito. Isso lhe dá a oportunidade de alterar o conteúdo do commit com git commit --amend ou até adicionar mais commits com o padrão git add/git commit comandos. Quaisquer novos commits que você adicionar farão parte do novo branch.

O rebase interativo pode ter um impacto profundo em seu fluxo de trabalho de desenvolvimento. Em vez de se preocupar em dividir suas alterações em confirmações encapsuladas, você pode se concentrar em escrever seu código. Se você acabou confirmando o que deveria ser uma única alteração em quatro instantâneos separados, isso não é um problema – reescreva o histórico com git rebase -i e esmagá-los todos em um commit significativo.

3. Perigos do Rebase

Agora que você tem uma compreensão git rebase, podemos falar sobre quando não usá-lo. Internamente, o rebase não move os commits para um novo branch. Em vez disso, ele cria novos commits que contêm as alterações desejadas. Com isso em mente, o rebasing é melhor visualizado da seguinte forma:

Após o rebase, os commits em feature terá diferentes hashes de confirmação. Isso significa que não apenas reposicionamos uma ramificação – literalmente reescrevemos a história do nosso projeto. Este é um efeito colateral muito importante da git rebase.

Quando você está trabalhando sozinho em um projeto, reescrever a história não é grande coisa. No entanto, assim que você começar a trabalhar em um ambiente colaborativo, isso pode se tornar muito perigoso. Se você reescrever commits que outros desenvolvedores estão usando (por exemplo, commits no master branch), parecerá que esses commits desapareceram na próxima vez que tentarem puxar seu trabalho. Isso resulta em um cenário confuso do qual é difícil se recuperar.

Com isso em mente, você nunca deve rebasear commits que foram enviados para um repositório público, a menos que você tenha certeza de que ninguém baseou seu trabalho neles.

Conclusão

Este tutorial apresentou os dois casos de uso mais comuns de git rebase. Conversamos muito sobre como mover ramificações, mas lembre-se de que o rebase é realmente sobre o controle do histórico do projeto. O poder de reescrever confirmações após o fato libera você para se concentrar em suas tarefas de desenvolvimento, em vez de dividir seu trabalho em instantâneos isolados.

Observe que o rebase é uma adição totalmente opcional à sua caixa de ferramentas do Git. Você ainda pode fazer tudo o que precisa com o velho git merge comandos. Na verdade, isso é mais seguro, pois evita a possibilidade de reescrever a história pública. No entanto, se você entender os riscos, git rebase pode ser uma maneira muito mais limpa de integrar branches em comparação com merge commits.

[ad_2]

Deixe uma resposta