Como resolver problemas de out of sync e conflitos de merge com Git Rebase

Entenda a dinâmica e não passe mais apuros!

Muitos profissionais do mundo tecnológico têm dificuldade de usar Git por ter conhecimento limitado da ferramenta. Isso impede que elas trabalhem ou contribuam para comunidades de maneira segura. Neste post, vamos tentar abordar alguns problemas partindo de exemplos práticos para entender a dinâmica e o funcionamento do Git. Bora lá?


João é desenvolvedor num projeto e trabalha em conjunto com várias outras pessoas. Ele normalmente é muito cuidadoso ao lidar com o repositório, porque não entende muito de Git. Em uma segunda-feira, ele chega ao trabalho depois do final de semana já com a mão na massa.

// Hmm, vamos ver o status da develop
8127 ± : git status
On branch develop
Your branch is up to date with ‘origin/develop’.

// Legal, tá tudo certo. Vamos criar a nova branch e começar a trabalhar!
8128 ± : git checkout -b feature/new-feature-A
Switched to a new branch ‘feature/new-feature-A’

… Depois de algumas horas de trabalho…

// Maneiro. Agora que terminei o trabalho vou adicionar as mudanças no commit.
8129 ± : git add .
8130 ± : git commit -m “Implementar toda a nova featureA”
[feature/new-feature-A] Implementar toda a nova featureA
250 files changed, 230 insertions(+), 20 deletions(-)

// Legal demais! Agora só ‘pushar’ pra origin e mergear via P.R.
8131 ± : git push -u origin feature/new-feature-A ⏎

Total 6323(delta 1232), reused 3241(delta 3212)

remote:
remote: Create pull request for feature/new-feature-A:
remote: https://bitrepository.org/meu-produto-maneiro/new?source=feature/new-feature-A
remote:

To bitrepository.org:meu-produto-maneiro.git
* [new branch] feature/new-feature-A -> feature/new-feature-A
Branch ‘feature/new-feature-A’ set up to track remote branch ‘feature/new-feature-A’ from ‘origin’.

// Sucesso!

Quando João abre o link do pull request, percebe que a feature está dessincronizada (Out of Sync) com a branch develop do repositório. Ele se confundiu porque havia feito git status em vez do git pull e esperou que atualizasse o estado da develop. De toda forma, ele aceita o erro e parte pra resolver o problema consultando fóruns, na esperança de identificar uma solução compatível com seu problema. Até que ele encontra um artigo de comunidade open source mencionando um comando chamado git rebase.

8132 ± : git checkout develop
8133 ± : git pull
remote: Counting objects: 1, done.
remote: Total 1 (delta 0), reused 0 (delta 0)

Unpacking objects: 100% (1/1), done.
From bitrepository.org:meu-produto-maneiro
Updating 3ae323be4..695ab32a9e
Fast-forward

4 files changed, 4 insertions(+), 3 deletions(-)
Current branch develop is up to date.

3ae323be4..695ab32a9e develop -> origin/develop

8134 ± : git checkout feature/new-feature-A
8134 ± : git rebase develop
First, rewinding head to replay your work on top of it…
Applying: Mudanças de madrugada de Sábado
Using index info to reconstruct a base tree…
Falling back to patching base and 3-way merge..

E aí acontece.

error: Your local changes to the following files would be overwritten by merge:
projeto-maneiro/arquivo123123.txt
Please commit your changes or stash them before you merge.
Aborting
error: Failed to merge in the changes.
Patch failed at 0002 Mudanças de madrugada de Domingo
hint: Use ‘git am — show-current-patch’ to see the failed patch

Resolve all conflicts manually, mark them as resolved with
“git add/rm <conflicted_files>”, then run “git rebase — continue”.
You can instead skip this commit: run “git rebase — skip”.
To abort and get back to the state before “git rebase”, run “git rebase — abort”

E aí, o que João deve fazer?


Antes de resolver o problema do João, vamos entender o que aconteceu com ele pelo ponto de vista da ferramenta:

Quando João começou a trabalhar na segunda-feira, sua branch develop estava apontada para o commit s7a2b2s. No entanto, na origin/develop haviam mudanças (commits as983ns e 9s2k2nk) que não estavam lá quando o João começou a trabalhar.

Mais tarde, ele consolidou todo o trabalho em um único commit a8b32ps nessa featureA e percebeu só depois que sua base estava Out of Sync com o repositório. Aí ele realizou os comandos git checkout develop e git pull para atualizar a develop local, mas empacou ao fazer rebase por causa de um conflito.

A maioria das comunidades de Git usam o fluxo de trabalho GitFlow (recomendo a leitura), um ideal que emprega escalabilidade, rastreabilidade e centralização controlada. Esse fluxo protege a integridade do projeto, permitindo somente a entrada de incrementos completos (funcionalidades, bugfix, melhorias, etc) nos fios de produção (branches develop e master). Além disso, também exige que toda mudança respeite as entradas anteriores (ou seja, todo novo incremento deve conter os mesmos commits do destinatário).

Perceba, no exemplo acima, que a branch featureA está Out of Sync (dessincronizado) com a develop, porque tem dois commits não inclusos após o fork point. Então, devemos usar alguma estratégia que atualize a featureA para que ela passe a conter esses dois commits: o que é o caso do git rebase!

Seguindo a expectativa, se nós usarmos git rebase develop (enquanto estamos na branch featureA) ou explicitamente git rebase develop featureA, vamos mover a featureA para depois das alterações do final de semana, certo?

Então, depende. Pode dar tanto certo quanto errado, depende se é inferível pelo Git. De acordo com o log no caso do João, o Git não conseguiu inferir uma resolução para o conflito no arquivo arquivo123123.txt.

As alterações no Git são coordenadas por blocos de informação e posição de linha. Ou seja, se houver mudanças que concorrem no mesmo arquivo, mas não na mesma linha, pode ser suficiente para gerar conflito. Agora, mudanças que concorrem a mesma linha e arquivo, provavelmente vai causar conflitos.


João chamou a colega de trabalho que efetuou as mudanças durante o final de semana para ajudá-lo no conflito. Maria, essa colega, sabia um pouco mais de Git do que ele. Ela tomou o controle do computador e leu todo o registro de operações que ele fez no Git, explicando pra ele que aconteceu um conflito, algo completamente normal, e que eles poderiam resolver manualmente.

De forma proativa, Maria mostra pelo registro do git status que todas as alterações foram convergidas com sucesso, exceto no arquivo arquivo123123.txt marcado como both modified.

<<<<<< HEAD
github “ModuloAPI/API3001” == 1.4.0
======
github “ModuloComum/ComponenteVisualManeiro” == 5.0.0
>>>>>> 9s2k2nk

Quando eles analisaram o conflito, viram que nesse arquivo colocaram novas dependências, cada uma necessária para resolver os problemas desenvolvidos individualmente, tanto os que surgiram no final de semana quanto a nova featureA. Eles discutiram rapidamente ali a importância de cada dependência e decidiram que ambas eram necessárias para o funcionamento do projeto.


Para resolver conflitos com rebase basta:

  • Acessar os arquivos marcados como both modified, apagar os marcadores de <<<<<< HEAD, ====== e >>>>>> <commit>e fazer os ajustes desejados.
  • Salvar o arquivo.
  • Adicionar o arquivo que sofreu ajustes manuais com git add <nome do arquivo>
  • Repetir o processo para todo arquivo como both modified até que o git status informe que não há mais arquivos marcados.
  • Prosseguir com o processo de rebase usando o comando git rebase --continue.
  • Se surgir a frase do commit, abra edição pelo comando i e renomeie (pode manter o mesmo nome, se quiser). Quando estiver pronto, aperte ESC, digite :x! e ENTER.
  • Repetir o processo para todos os commits que tiverem conflitos.
  • Quando concluído, usar o comando git push -f(o operador -f é necessário para forçar a atualização do branch da remote se estiver Out of Sync), ou gerar uma nova branch para não mudar o commit que a branch está apontando com git push -u origin feature/rebased-branch.

Durante as etapas de conflito, caso queira desistir do rebase, basta usar o comando git rebase --abort.


Depois de tudo resolvido e pushado para o repositório remoto, Maria vai até o link do pull request e mostra a João que não há mais conflitos.

João não só percebe que está tudo certo, mas também como o commit com suas alterações está completamente modificado. A featureA (commit s93ndos) está em sincronia com a develop, mas ela não é a mesma de antes (commit a2b32ps). Efeito da operação do rebase.

Maria só insiste com João que: conflito durante merge é algo completamente natural de acontecer, mas devemos ser capazes de julgar a razão do conflito e, com isso, resolver a questão.


Considere algumas dessas situações como exemplo:

Situação 1:

Uma comunidade tem um repositório específico para guardar dados de participantes, e nesse repositório há um arquivo reservado para cadastrar informações pessoais dos desenvolvedores. Você que é contribuinte da comunidade quer colocar o seu nome junto com os outros.

Se ocorrer algum conflito neste arquivo, entre o seu registro e o registro de outra pessoa, mantenha ambos.

Situação 2:

Você está substituindo uma dependência por um módulo customizado que resolve todas as suas funções. No ato de fazer rebase em cima de modificações que usem a dependência, a resolução dos conflitos deve priorizar mudanças para o novo módulo.

Situação 3:

Você desenvolveu uma feature que durante a entrada para o fio de produção houve a substituição de um módulo que foi utilizado. A resolução dos conflitos deve priorizar o módulo em produção.

Atenção:

Quanto mais complexa é a operação de rebase, mais importante é garantir que o projeto está operando como esperado (build, casos de testes, etc), devido ao risco.


Espero que este texto te ajude a entender melhor o processo de rebase como solução de versionamento. Sintetizar algo tão complexo é difícil, e precisa de muita atenção aos detalhes para não induzirmos o leitor ao erro. Muito obrigado pela atenção. Se ficou alguma dúvida ou tem algo a acrescentar, deixe um comentário neste post. Até a próxima!

Concrete

Nós desenvolvemos produtos digitais com inovação, agilidade…

Guilherme Guimarães

Written by

Brazilian Citizen. Email: heaven_@live.jp

Concrete

Concrete

Nós desenvolvemos produtos digitais com inovação, agilidade e excelentes práticas, para que o mercado brasileiro e latino-americano acompanhe a velocidade do mercado digital mundial.

More From Medium

More on Artigos from Concrete

More on Artigos from Concrete

Era uma vez… uma Daily!

Apr 2 · 7 min read

20

More on Artigos from Concrete

More on Artigos from Concrete

Prodcast #6 — Entretenimento

Apr 1 · 1 min read

17

More on Artigos from Concrete

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade