Como o Git rastreia o histórico durante uma refatoração?

Eu entendo bem como o Git pode suportar movimentos de arquivos: como ele usa hash de arquivo, um arquivo “adicionado” é facilmente detectado como sendo o mesmo que o “removido”.

Minha pergunta é sobre a refatoração: considerando o Java, a declaração do pacote muda para que o conteúdo do arquivo NÃO seja o mesmo. Nesse caso, como o Git determina que o arquivo “adicionado” compartilha o histórico com o “removido”? Ele verifica se há “conteúdo mais semelhante”, presumindo que eu fiz apenas pequenas alterações ou uma solução não determinística semelhante?

Como mencionado no Git FAQ , ele detectará conteúdo semelhante com base em uma heurística.

O Git precisa interoperar com vários streams de trabalho diferentes, por exemplo, algumas alterações podem vir de patches, onde as informações de renomeação podem não estar disponíveis. Baseando-se no rastreamento explícito de renomeação, é impossível mesclar duas trees que fizeram exatamente a mesma coisa, exceto que uma fez como uma correção (criar / excluir) e outra usando outra heurística.

Em uma segunda nota, as renomeações de rastreamento são realmente apenas um caso especial de rastreamento de como o conteúdo é movido na tree. Em alguns casos, você pode estar interessado em consultar quando uma function foi adicionada ou movida para um arquivo diferente. Contando apenas com a capacidade de recriar essas informações quando necessário, o Git tem como objective fornecer uma maneira mais flexível de rastrear como a sua tree está mudando.

No entanto, isso não significa que o Git não tenha suporte para renomear.
O maquinário de diferenças no Git tem suporte para detectar automaticamente renomeações, isto é ativado pela opção ‘ -M ‘ para a família de comandos git-diff-* .
O mecanismo de detecção de renomeação é usado por git-log (1) e git-whatchanged (1) , portanto, por exemplo, ‘ git log -M ‘ fornecerá ao histórico de confirmação informações de renomeação.
O Git também suporta uma forma limitada de fusão entre renomeações.
As duas ferramentas para atribuição de culpa, git-blame(1) e git-annotate(1) usam o código de detecção automática de renomeação para rastrear renomeações.


git log fornece alguns detalhes sobre essa heurística:

 -B[][/] 

Pausa completa rewrite as alterações em pares de excluir e criar. Isso serve a dois propósitos:

  • Ela afeta a forma como uma mudança que equivale a uma reescrita total de um arquivo não como uma série de exclusão e inserção misturadas com poucas linhas que correspondem textualmente ao contexto, mas como uma única exclusão de tudo o que é antigo seguido por uma inserção única de tudo novo, e o número m controla este aspecto da opção -B (o padrão é 60%).
    -B / 70% especifica que menos de 30% do original deve permanecer no resultado para o git considerá-lo uma reescrita total (ou seja, o patch resultante será uma série de deleção e inserção misturadas com linhas de contexto).

  • Quando usado com -M, um arquivo totalmente reescrito também é considerado como a origem de uma renomeação (geralmente -M considera apenas um arquivo que desapareceu como a origem de uma renomeação), e o número n controla esse aspecto da opção -B (o padrão é 50%) .
    -B20% especifica que uma alteração com adição e exclusão em comparação com 20% ou mais do tamanho do arquivo é elegível para ser selecionada como uma possível fonte de renomeação para outro arquivo.

 -M[] 

Se estiver gerando diffs, detecte e relate renomeações para cada commit. Para seguir os arquivos nas renomeações ao percorrer o histórico, consulte --follow .
Se n for especificado, é um limite no índice de similaridade (ou seja, quantidade de adições / exclusões em comparação com o tamanho do arquivo).
Por exemplo, -M90% significa que o git deve considerar um par de exclusão / adição como renomeado se mais de 90% do arquivo não tiver sido alterado .


Referências adicionais:

  • O último post da ferramenta de rastreamento de conteúdo da Linus , por Junio ​​C Hamano, mantenedor do Git.
  • Obtendo o Git para reconhecer arquivos previamente movidos
  • Como fazer o git marcar um arquivo deletado e um novo como um arquivo?
  • Como o Git resolve o problema de mesclagem?

Nota: Com o Git 2.18 (Q2 2018), o git status deve agora mostrar-lhe renomeações (ao invés de deletar / adicionar arquivos) quando você move / renomeia arquivos.
Veja ” Como dizer ao Git que é o mesmo diretório, apenas um nome diferente “.