Corrija apenas conflitos com git rerere

Então você corrigiu um conflito em algum lugar em seu repo, então mais tarde tropeçou exatamente o mesmo (talvez você tenha feito outra fusão, ou acabou rebasing em vez disso, ou cerejeira – escolheu o commit defeituoso em outro lugar …). E bang, você teve que consertar o mesmo conflito novamente.

Isso é uma merda.

Especialmente quando Git é tão legal que oferece um mecanismo para poupar-lhe essa tarefa, pelo menos a maior parte do tempo: rerere . OK, então o nome é ruim, mas isso realmente significa Re use Re corded Re solução , você sabe.

Neste artigo, tentaremos mergulhar em como ele funciona, quais são seus limites e como se beneficiar melhor dele.

O suspeito habitual: o controle se funde

Uma situação em que o rerere é realmente útil é o controle de fusões.

Imagine isso: você está trabalhando em um ramo de longa vida; talvez um ramo de recursos pesados. Vamos chamá-lo de longa vida . E, naturalmente, com o passar do tempo, você fica cada vez mais preocupado com a fusão desse ramo no principal ramo de desenvolvimento (geralmente mestre ), porque com o passar do tempo, a divergência espessa …

Então, para aliviar algumas dessas tensões e aliviar a fusão final que você está dirigindo, você decide executar uma fusão de controle agora e então: uma mescla de mestre em seu próprio ramo, de modo que, sem mestre poluente, você possa ver quais conflitos são espreitando e descobre se eles são difíceis de consertar.

Na verdade, é útil e, assim, você não terá que corrigir isso mais tarde, você ficaria tentado a deixar essa mesclagem de controle na árvore depois de terminar com ele, em vez de reviver com, digamos, uma reinicialização git – Órgão ORIG_HEAD e mantenha seu gráfico prístino.

Então, com o passar do tempo, você obtém um gráfico que se parece com isso, mas pior:

O controle mescla a poluição do gráfico da sua história

Isso é feio e polui seu gráfico histórico em filiais. Afinal, uma fusão só deve ocorrer para juntar uma ramificação finalizada .

Mas se você cancelar essa fusão de controle assim que terminar, você terá que corrigir novamente esses conflitos na próxima vez que você fizer uma mesclagem de controle, para não mencionar na mesclagem final em direção ao mestre . Então, o que um desenvolvedor deve fazer?

rerere para o resgate

Isso é exatamente para o qual é rerere . Este recurso Git tira uma impressão digital de cada conflito, e o faz com uma correspondente correspondência quando o commit problemático é finalizado.

Mais tarde, se um conflito corresponder à primeira impressão digital, o rerere usará automaticamente a correção correspondente para você.

Ativando Rerere

Rerere não é apenas um comando, mas um comportamento transversal do Git. Para que seja ativo, você precisa de pelo menos uma das duas condições a serem atendidas:

  • A configuração de configuração rerere.enabled está definida como verdadeira
  • O seu repo contém um banco de dados rerere (você possui um diretório .git / rr-cache )

Não consigo imaginar uma situação em que ter sido ativado é uma má idéia, então eu recomendo que você vá em frente e ative globalmente:

 git config --global rerere.enabled true

Surge um conflito

Digamos que você agora enfrenta uma divergência conflitante; talvez o mestre tenha mudado seu <title> em index.html de uma certa maneira, e a vida longa de outra forma.

Vamos tentar uma mesclagem de controle:

 (longa vida) $ git merge master

Seu primeiro conflito habilitado para rerere. Observe a 3ª linha.

Isso parece seu conflito regular, mas preste atenção à terceira linha:

 Pré-imagem gravada para 'index.html'

Isso nos diz que rerere levantou uma impressão digital de nosso conflito. E, de fato, se perguntarmos em que arquivos está prestando atenção nessa, isso nos dirá:

 (Long-lived * + | MERGING) $ git rerere status 
 index.html

Se olharmos para o nosso repo, encontraremos o arquivo de impressão digital:

 $ tree .git / rr-cache 
 .git / rr-cache 
 ??? f08b1f478ffc13763d006460a3cc892fa3cc9b73 
 ??? preimage

Este arquivo preimage contém a impressão digital completa do arquivo e seu conflito (o blob inteiro, se você quiser).

Gravando a correção

OK, então vamos resolver esse conflito. Por exemplo, eu irei com o seguinte título combinado:

 ... 
 <head> 
 <meta charset = "utf-8"> 
 <title> 20% cooler e título mais sólido </ title> 
 </ head> 
 ...

Posso então verificar o que o rerere lembrará quando eu concluir a mesclagem:

 $ git rerere diff 
 --- a / index.html 
 +++ b / index.html 
 @@ -2,11 +2,7 @@ 
 <html> 
 <head> 
 <meta charset = "utf-8"> 
 - <<<<<<< 
 - <title> 20% cooler title </ title> 
 - ======= 
 - <title> Título mais sólido </ title> 
 - >>>>>>> 
 + <title> 20% mais frio e título mais sólido </ title> 
 </ head> 
 <corpo> 
 <h1> Título base </ h1>

Posso então marcar isso como corrigido da maneira usual, com um git add . Então git rerere restante vai me dizer quais outros arquivos eu deveria procurar (agora, nenhum).

De qualquer forma, para rerere para lembrar efetivamente a correção, eu preciso finalizar cometer o atual. Ao ser uma fusão, cabe a mim executar manualmente o commit:

 (Long-lived + | MERGING) $ git commit --no-edit 
 Resolução gravada para 'index.html' 
 [fcd883f de longa vida] Fusão do ramo "mestre" em longa vida 
 (longa vida) $

Preste atenção na segunda linha:

 Resolução gravada para 'index.html'

E de fato, esse instantâneo de correção é agora uma postimage em nosso repo:

 $ tree .git / rr-cache 
 .git / rr-cache 
 ??? f08b1f478ffc13763d006460a3cc892fa3cc9b73 
 ??? postimage 
 ??? preimage

Então eu posso ir para a frente e reverter essa fusão de controle , porque eu não quero poluir meu gráfico de histórico com ele:

 (longa vida) $ git reset --hard HEAD ^ 
 HEAD é agora b8dd02b 20% de título mais frio 
 (longa vida) $

O conflito reaparece

Vamos agora assumir que a longa vida e o mestre continuam marchando. Talvez no primeiro, um CSS aparece. E no último, o mesmo CSS aparece (embora com conteúdos diferentes), juntamente com um arquivo JS.

Chega o momento em que uma nova fusão de controle parece estar em ordem. Aqui vamos nós:

 (longa vida) $ git merge master 
 Auto-fusioning style.css 
 CONFLICTO (adicionar / adicionar): mesclar conflito em style.css 
 Auto-fusão index.html 
 CONFLITO (conteúdo): confundir conflito em index.html 
 Pré-imagem gravada para 'style.css' 
 Resolve 'index.html' usando a resolução anterior. 
 Fusão automática falhou; corrigir conflitos e, em seguida, confirmar o resultado. 
 (Long-lived * + | MERGING) $

Temos um conflito de adicionar / adicionar para o CSS e o conhecido conflito para index.html . Mas olhe mais de perto ao final:

 Pré-imagem gravada para 'style.css' 
 Resolve 'index.html' usando a resolução anterior.

Como você pode ver, o conflito sobre index.html já é conhecido e foi auto fixado. De fato, se você perguntar a Git Rerere restando o que aconteceu, ele irá dizer-lhe que apenas style.css ainda está com problemas.

Então, comece com a marcação index.html como sendo bom, encenando-o:

 $ git add index.html

Por favor , se você preferir rerere para os arquivos de auto-estágio resolvidos (eu faço), você pode pedir para: você só precisa ajustar sua configuração assim:

 $ git config --global rerere.autoupdate true

De agora em diante, considero que você tem essa configuração. Como fizemos antes, vamos resolver o conflito restante e depois:

 (long-lived * + | MERGING) $ git commit -a - no-edit 
 Resolução gravada para 'style.css'. 
 [D6eea3e de longa duração] Fusão do ramo 'mestre' em longa vida 
 (longa vida) $

Agora temos dois pares de impressões digitais disponíveis, incluindo um em style.css :

 $ tree .git / rr-cache 
 .git / rr-cache 
 ??? d8cd8c78a005709a8aac404d46f23d6e82b12aee 
 ? ??? postimage 
 ? ??? preimage 
 ??? f08b1f478ffc13763d006460a3cc892fa3cc9b73 
 ??? postimage 
 ??? preimage

E podemos reverter esse compromisso, como antes.

Para concluir, vamos assumir que index.html seja modificado uma última vez, adicionando conteúdo perto do final do <body> . Então nós o comprometemos.

Este foi o último compromisso necessário para uma vida longa , então, em vez de fazer mais uma mesclagem de controle, decidimos fazer a mesclagem final e adequada no mestre :

 (mestre) $ git fundir longa vida 
 Auto-fusioning style.css 
 CONFLICTO (adicionar / adicionar): mesclar conflito em style.css 
 Auto-fusão index.html 
 CONFLITO (conteúdo): confundir conflito em index.html 
 Escalonado 'index.html' usando a resolução anterior. 
 Layged 'style.css' usando a resolução anterior. 
 Fusão automática falhou; corrigir conflitos e, em seguida, confirmar o resultado. 
 (mestre + | FUSÃO) $

Observe que, em vez do “Resolvido … usando a resolução anterior” que tínhamos antes, agora recebemos:

 Escalonado 'index.html' usando a resolução anterior. 
 Layged 'style.css' usando a resolução anterior.

Isso ocorre porque pedimos que rerere para auto-estágio de arquivos completamente fixos. E de fato, meu prompt apenas menciona ” +” (encenado), não “*” (modificado), o que me leva a pensar que não há conflito restante. Algo git rerere restante confirma por não mostrar nada.

Então, você não precisa parar ao ver a “fusão automática falhar” no final, isso significa apenas que as estratégias de mesclagem regulares não eram suficientes; mas porque tínhamos uma boa ajuda em cima disso, poderíamos passar. Ainda assim, porque as heurísticas de Rerere não são garantidas a 100% para serem relevantes (o contexto pode ter mudado …), a Git se recusará a finalizar automaticamente uma operação habilitada para rerere.

Para se certificar de que foi adequadamente corrigido, se você tiver alguma dúvida, um simples show de git diff -staged ou git: 0: o arquivo (que é um zero, e não uma letra O) irá acalmar seus medos.

Ainda é seu trabalho encerrar o commit:

 (mestre + | FUSÃO) $ git commit

Usando o Git com o GitHub? Quer se tornar um verdadeiro mestre GitHub? Nós lançamos a parte 1 da nossa série de treinamento de vídeos GitHub de melhores classe! 5 horas, 69 vídeos, conteúdos surpreendentes para iniciantes e especialistas! Saiba mais .

Sem contexto

Você deve lembrar que as impressões digitais são independentes do contexto:

  • Não importa qual comando resultou no levantamento do par de impressões digitais ( fusão , rebase , seleção de cereja , stash apply / pop , checkout -m , etc.).
  • Não importa qual comando reutiliza a correção.
  • Não importa em que caminhos os arquivos conflitantes estavam em (é o conteúdo do instantâneo que importa).

Por outro lado, uma impressão digital é utilizável somente se os contextos imediatos do seu diffs forem preservados, como é usual para os conflitos de mesclagem. Se você modificou uma linha muito próxima a uma diferença na préimagem , rerere se recusará a considerar esse par de imagem + fix e você terá que corrigir o novo contexto você mesmo.

Além disso, se um novo conflito aparecer em um arquivo já segmentado por pares de impressões digitais para conflitos anteriores, rerere parece bastante rigoroso sobre suas regras de aplicabilidade para correções anteriores. Eu acho difícil determinar exatamente quais são os limites de proximidade, mas pode ignorar as correções anteriores e decidir pedir uma nova correção para todo o conflito definido no arquivo. Como de costume, YMMV.

Não posso compartilhar isso com outros contribuidores?

Assim como os ganchos, o banco de dados rerere (o diretório .git / rr-cache ) permanece no seu local local: não é compartilhado com o upstream quando você pressiona (independentemente das configurações e opções de envio).

E, assim como ganchos, isso não significa que você não pode compartilhar isso com colegas de trabalho e outros colaboradores do código se você realmente quiser (na verdade, é uma boa idéia compartilhar esses). Existem várias opções, geralmente baseadas em links simbólicos ( links simbólicos ).

Opção 1: incorporado no seu diretório de trabalho

Você pode absolutamente dedicar um diretório em seu WD para compartilhar elementos de outra forma mantidos locais em seu repo, como rr-cache e hooks , por exemplo.

Você pode criar um diretório chamado. git-system na raiz do seu WD, no qual você teria subpastas. Desta forma, no seu diretório .git , rr-cache seria um link simbólico em ../.git-system/rr-cache . No OSX / Linux / Git Bash, você faria o seguinte:

 # Crie a pasta 
 mkdir .git-system
 # Se a sua pasta existir no repo, mova-o; 
 # caso contrário, crie-o em sua localização final 
 [-d .git / rr-cache] && mv .git / rr-cache .git-system / || 
 mkdir .git-system / rr-cache
 # Crie o link simbólico 
 ln -nfs ../.git-system/rr-cache

No Windows, isso seria mais parecido com isso:

 mkdir .git-system
 # O seguinte é uma única linha 
 se existir .git  rr-cache (mover .git  rr-cache .git-system) else mkdir .git-system  rr-cache
 mklink / d .git  rr-cache ... . git-system  rr-cache

(Desde o Windows Vista, o comando mklink permite criar links simbólicos, mas você precisará executá-lo em um prompt de comando de privilégios elevados, ou seja, um executado como administrador. O seu administrador não é suficiente. OU, sua Política de segurança local pode incluir sua conta de usuário específica na autorização Criar permissões simbólicas. Porque, você sabe, links simbólicos são para hackers do mal, certo ?! Mais informações sobre o mklink aqui . Se no Windows XP ou não quiser se machucar com scripts do Windows, basta executar o primeiro conjunto de comandos no Git Bash instalado pelo instalador do Git Windows.)

Compartilhando suas configurações de reposição locais, incorporadas no diretório de trabalho. Isso exige comprometer o malabarismo para manter as coisas perfeitamente separadas.

Em tal situação, a idéia não é incluir as novas impressões digitais na autenticação original, mantendo o conteúdo do sistema .git em sua própria confirmação. Isso requer alguns malabarismos com o fato de você querer redefinir mesclagens de controle, mas manter as impressões digitais – apenas mais tarde se comprometer. Um rebase de três pontos ajuda com isso, por exemplo:

 # 1. Certifique-se de que não está cometendo .git-system por engano 
 (Long-lived * + | MERGING) $ git reset - .git-system 
 (Long-lived * + | MERGING) $ git commit --no-edit
 # 2. Comprometer o sistema .git por si só 
 (Long-lived *) $ git adiciona .git-system 
 (Long-lived +) $ git commit -m "Corrigir impressões digitais para a mesclagem de controle"
 # 3. Reescreva o histórico para preservar apenas o último dos dois compromissos. 
 (longa vida) $ git rebase --onto HEAD ~ 2 HEAD ^

Opção 2: um repositório de compartilhamento local dedicado

A outra abordagem, que evita comprometer o malabarismo, mas faz o compartilhamento de um processo em duas etapas, é ter um repo (e seu upstream para compartilhar, obviamente) dedicado a compartilhar configurações de outra forma mantidas locais.

Você só mudaria o alvo do seu link simbólico para algo mais fixo e absoluto, idealmente um subdiretório do seu compartilhamento de compartilhamento central, algo como as seguintes:

  • ~ / .git-shared-locals / your-project / rr-cache em OSX / Linux, ou
  • C: Usuários you git-shared-locals your-project rr-cache no Windows.

Compartilhando suas configurações locais de repo, através de um repositório separado e dedicado. Não cometer malabarismo, mas compartilhamento em duas etapas.

Dessa forma, você não apresenta nenhum conteúdo extra em seu diretório de trabalho devido a impressões digitais. Não cometer malabarismo. É só isso, para compartilhar suas impressões digitais, você também precisa ir para o repo de compartilhamento central, comprometer-se, fazer um rápido git pull –rebase para obter o que as configurações compartilhadas são novas no servidor e reproduzir suas próprias coisas novas no topo disso, então git push para realmente compartilhar com seus amigos.

(Esta é a segunda vez que estamos falando sobre o rebaixamento neste artigo, se você está confuso sobre quando fundir vs rebase e quais coisas estranhas como rebases de três pontos são, nós temos você coberto ).

Isso é de dois passos, mas elimina o risco de compromissos quebrados misturando impressões digitais pré-imagem com correções, etc.

Quer saber mais?

Eu escrevi uma série de artigos Git , e você pode estar particularmente interessado nos seguintes:

Além disso, se você gostou desta postagem, diga assim: avanço na HN ! Muito obrigado!

Embora não divulguemos muito por enquanto, oferecemos treinamento Git em inglês em toda a Europa, com base em nosso curso de treinamento Total Git , testado em batalha. Se você gosta de um, apenas deixe-nos saber !

(Nós podemos absolutamente vir para os EUA / Canadá ou em qualquer outro lugar do mundo, mas considerando que você irá incorrer em nossos custos de viagem, apesar de termos super preços razoáveis, é provável que você encontre um negócio mais econômico usando um contato mais próximo provedor, seja o GitHub ou outra pessoa. Ainda assim, se você nos quer , siga o link acima e vamos conversar!)

 

Hacker Noon é como os hackers começam suas tardes. Somos uma parte da família @AMI . Agora estamos aceitando envios e estamos felizes em discutir oportunidades de propaganda e patrocínio .

Se você gostou desta história, recomendamos ler nossas últimas histórias de tecnologia e histórias de tecnologia de tendências . Até a próxima, não concorde com as realidades do mundo!