Guia Git Número da peça 2: a regra de ouro e outros princípios básicos de rebase

Vamos ver o que acontece quando você executa o git rebase e por que você precisa ter cuidado. 

Esta é a segunda e terceira parte do guia Git do blog Pierre de Wulf traduzido pela equipe do Mail.ru Cloud Solutions . A primeira parte pode ser lida aqui .

A essência da rebase


Como exatamente a rebase acontece:


Você pode dizer que rebase é desafixar o ramo que você deseja mover e conectá-lo a outro ramo. Essa definição é verdadeira, mas tente parecer um pouco mais profunda. Se você olhar para a documentação , veja o que diz sobre rebase: "Aplicar confirmações para outro ramo (Reaplicar confirmadas sobre outra dica básica)".

A palavra principal aqui é aplicar, porque rebase não é apenas copiar e colar ramificações para outra ramificação. A rebase sequencialmente leva todas as confirmações da ramificação selecionada e as aplica novamente na nova ramificação.

Esse comportamento leva a dois pontos:

  1. Re-aplicando confirmações, o Git cria novas confirmações. Mesmo que contenham as mesmas alterações, o Git é considerado como confirmação nova e independente.
  2. O Git rebase substitui as confirmações e não exclui as antigas. Isso significa que, após a conclusão da restauração, as confirmações antigas continuarão sendo armazenadas na subpasta / gjects da pasta .git. Se você não entender completamente como o Git armazena e considera confirmações, leia a primeira parte deste artigo.

Aqui está uma interpretação mais correta do que acontece durante o rebase:


Como você pode ver, o ramo do recurso contém confirmações completamente novas. Como mencionado anteriormente, o mesmo conjunto de alterações, mas objetos completamente novos do ponto de vista do Git. 

Isso também significa que confirmações antigas não são destruídas. Eles apenas se tornam inacessíveis diretamente. Se você se lembra, um ramo é apenas um link para um commit. Portanto, se nem uma ramificação nem uma tag se referem a uma confirmação, ela não pode ser acessada usando o Git, embora continue presente no disco.

Agora vamos discutir a Regra de Ouro.

Regra de rebase dourada


A regra de rebase de ouro é: NUNCA rebase um ramo compartilhado! " Uma ramificação compartilhada refere-se a uma ramificação que existe no repositório de rede e com a qual outras pessoas, exceto você, podem trabalhar.

Frequentemente, essa regra é aplicada sem o devido entendimento; portanto, analisaremos por que ela apareceu, principalmente porque ajudará a entender melhor o trabalho do Git.

Vejamos uma situação em que um desenvolvedor quebra a regra de ouro e o que acontece neste caso.

Suponha que Bob e Anna estejam trabalhando juntos em um projeto. Abaixo está a aparência dos repositórios Bob e Anna e o repositório original no GitHub:


Todos os usuários têm repositórios sincronizados com o GitHub.

Agora, Bob, quebrando a regra de ouro, realiza uma nova rebase e, ao mesmo tempo, Anna, trabalhando no ramo de recursos, cria um novo commit:


Você vê o que vai acontecer?

Bob está tentando executar um envio por push; ele se recusa a algo como isto:


O Git não teve êxito porque o Git não sabia como combinar a ramificação de recursos do Bob com a ramificação de recursos do GitHub.

A única solução que permite que Bob pressione é usar a tecla force, que instrui o repositório do GitHub a remover a ramificação de recursos e aceitar a que Bob está pressionando para essa ramificação. Depois disso, temos a seguinte situação:


Agora Anna quer lançar suas alterações, e aqui está o que acontecerá:


Isso é normal, Git disse a Anna que ela não possui uma versão sincronizada do ramo de recursos, ou seja, sua versão do ramo e a versão do ramo no GitHub são diferentes. Anna deve completar a atração. Da mesma maneira que o Git mescla uma ramificação local com uma ramificação no repositório quando você pressiona, o Git tenta mesclar a ramificação no repositório com uma ramificação local quando você puxa.

Antes de fazer pull commit nas ramificações local e GitHub, é assim:

A--B--C--D'   origin/feature // GitHub
A--B--D--E    feature        // Anna

Quando você puxa, o Git é mesclado para eliminar a diferença nos repositórios. E assim, o que isso leva a:


A confirmação M é uma confirmação de mesclagem. Por fim, os ramos de recursos do Anna e do GitHub são totalmente mesclados. Anna deu um suspiro de alívio, todos os conflitos foram resolvidos, ela pode executar um empurrão. 

Bob está puxando, agora tudo está sincronizado:


Olhando para a bagunça resultante, você tinha que ter certeza da importância da regra de ouro. Lembre-se também de que essa confusão foi criada por apenas um desenvolvedor e em um ramo compartilhado entre apenas duas pessoas. Imagine estar em uma equipe de dez pessoas. 

Um dos muitos benefícios do Git é que você pode reverter sem problemas a qualquer momento. Porém, quanto mais erros são cometidos, como o descrito, mais difícil é fazê-lo.

Observe também que confirmações duplicadas aparecem no repositório de rede. No nosso caso, D e D ', contendo os mesmos dados. De fato, o número de confirmações duplicadas pode ser tão grande quanto o número de confirmações em sua ramificação rebaseada.

Se você ainda não está convencido, vamos apresentar Emma, ​​a terceira desenvolvedora. Ela trabalha no ramo de recursos antes de Bob cometer seu erro e, atualmente, quer pressionar. Suponha que, no momento em que ela forneça, nosso pequeno script anterior já esteja concluído. Aqui está o que sai:


Oh, esse Bob !!!!

Esse texto pode fazer você pensar que a rebase é usada apenas para mover um ramo para o topo de outro ramo. Isso é opcional - você pode refazer a nova ramificação.

Rebase da tração da beleza


Como você viu acima, os problemas de Anna poderiam ter sido evitados se ela usasse rebase de tração. Vamos considerar esta questão em mais detalhes.

Digamos que Bob trabalha em um ramo que se afasta do mestre, então sua história pode ficar assim:




Bob decide que é hora de puxar, o que, como você já entendeu, levará a alguma confusão. Como o repositório de Bob estava se afastando do GitHub, o Git perguntará se a fusão foi feita e o resultado será o seguinte:


Essa solução funciona e funciona bem; no entanto, pode ser útil saber que existem outras soluções para o problema. Um deles é o pull-rebase.

Quando você faz o pull-rebase, o Git tenta descobrir quais confirmações estão apenas em sua filial e quais estão no repositório de rede. O Git combina as confirmações do repositório de rede com a confirmação mais recente presente nos repositórios local e de rede. Em seguida, reveja as confirmações locais até o final da ramificação. 

Parece complicado, então ilustramos:

  1. O Git presta atenção apenas aos commits que estão no seu e no repositório de rede:

    Parece um clone local do repositório GitHub.
  2. Confirmações locais do Git rebase:


Como você se lembra, quando rebase o Git se aplica, confirma um por um, ou seja, nesse caso, aplica-se o commit mestre E, depois o F. ao final do branch.O resultado é o rebase para si mesmo. Parece bom, mas surge a pergunta - por que fazer isso?

Na minha opinião, o maior problema com a fusão de ramificações é que o histórico de confirmações é poluído. Portanto, o pull-rebase é uma solução mais elegante. Eu diria ainda mais que quando precisar fazer o download das alterações mais recentes em sua filial, você sempre deve usar pull-rebase. Mas você deve se lembrar: como rebase aplica todas as confirmações sucessivamente, quando você refaz 20 confirmações, talvez seja necessário resolver 20 conflitos um após o outro. 

Normalmente, você pode usar a seguinte abordagem: uma grande alteração feita há muito tempo é a mesclagem, duas pequenas alterações feitas recentemente em um pull-rebase.

Força rebase em


Suponha que seu histórico de consolidação seja assim:




Então, você deseja refazer a ramificação do recurso 2 para a ramificação mestre. Se você fizer uma nova atualização regular na ramificação principal, obtenha o seguinte:


Não é lógico que a confirmação D exista nos dois ramos: no recurso 1 e no recurso 2. Se você mover o ramo do recurso 1 para o final do ramo mestre, verifica-se que o commit D será aplicado duas vezes.

Suponha que você precise obter um resultado diferente:


Para implementar esse cenário, o git rebase é exatamente o que se pretende.

Primeiro, leia a documentação:

SYNOPSIS
       git rebase [-i | --interactive] [<options>] [--exec <cmd>]
               [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
       git rebase [-i | --interactive] [<options>] [--exec <cmd>] 
[--onto <newbase>]
               --root [<branch>]
       git rebase (--continue | --skip | --abort | --quit | --edit-todo 
| --show-current-patch)


Estamos interessados ​​nisso:

OPTIONS
       --onto <newbase>
          Starting point at which to create the new commits. If the 
--onto option is not specified, the starting point is <upstream>. May be 
any valid commit, and not just an existing branch name.


Use esta opção para indicar em que ponto criar novas confirmações.

Se essa opção não for especificada, o upstream se tornará o ponto de partida.

Para entender, darei mais uma imagem:

A--B--C        master
    \
     D--E      feature1
         \
          F--G feature2

Here we want to rebase feature2 to master beginning from feature1
                           |                                |
                        newbase                         upstream


Ou seja, a ramificação principal é nova base e a ramificação do recurso 1 é upstream.

Portanto, se você deseja obter o resultado como na última figura, execute git rebase --onto master feature1 na ramificação feature2.

Boa sorte

Traduzido com suporte do Mail.ru Cloud Solutions .

O que mais se pode ler sobre o tópico :

  1. A primeira parte do guia Git.
  2. Meu segundo ano como desenvolvedor independente .
  3. Nosso canal de telegrama sobre transformação digital


All Articles