Às vezes você abre os dois arquivos e não há um caractere de diferença, mas o diff diz que todas as linhas mudaram, ou que só a primeira ou a última linha é diferente. A causa costuma ser um caractere que não aparece na tela. Este guia reúne os tipos dessas "diferenças invisíveis" e como resolvê-las.
Caracteres de quebra de linha: CRLF, LF, CR
A convenção para quebrar linhas varia conforme o sistema operacional.
| Notação | Bytes | Onde é mais usada |
|---|---|---|
| LF | 0A (\n) | Linux, macOS, a maioria das ferramentas de desenvolvimento |
| CRLF | 0D 0A (\r\n) | Vários programas do Windows, como o Bloco de Notas, e protocolos da internet como e-mail e HTTP |
| CR | 0D (\r) | Mac OS muito antigo (até a versão 9) |
Se um arquivo foi salvo com CRLF e o outro com LF, na comparação por linha um \r invisível no fim de cada linha conta como diferença, e parece que o arquivo inteiro mudou. No GNU diff, --strip-trailing-cr ignora essa diferença; no git diff, --ignore-cr-at-eol.
Quebra de linha no fim do arquivo
Pela convenção POSIX, as linhas de um arquivo de texto terminam com um caractere de quebra de linha. Por isso muitas ferramentas colocam uma quebra depois da última linha, mas alguns editores não colocam. No unified diff, essa diferença aparece assim:
@@ -1,2 +1,2 @@ host=example.com-port=8080\ No newline at end of file+port=8080
\ No newline at end of file significa que a linha logo acima não termina com quebra de linha. No exemplo, o conteúdo é o mesmo, e só o arquivo depois da mudança ganhou a quebra de linha final. Muitos guias de estilo de código exigem a quebra no fim do arquivo, e dá para padronizar isso com a configuração "inserir nova linha no fim do arquivo" do editor.
BOM: quando só a primeira linha difere
Às vezes um arquivo UTF-8 começa com os três bytes EF BB BF. Isso se chama marca de ordem de bytes (BOM). O UTF-8 não precisa de ordem de bytes, mas alguns programas acrescentam a marca como sinal de "este arquivo é UTF-8". O BOM não aparece na tela, então, se o resultado diz que só a primeira linha é diferente, desconfie dele. Este site remove o BOM do início ao abrir um arquivo e, com as opções de ignorar espaços ativadas, também trata como espaço o BOM (U+FEFF) no texto colado.
Tabulações, espaços e espaços especiais
- Tabulação e espaço: uma tabulação e 4 espaços podem parecer iguais na tela, mas são caracteres diferentes.
- Espaço sem quebra (NBSP, U+00A0): aparece com frequência em texto copiado de páginas web ou de editores de texto.
- Espaço ideográfico (U+3000): pode ser inserido por métodos de entrada de coreano, japonês e chinês.
- Espaço de largura zero (U+200B): tem largura 0 e é totalmente invisível, mas conta como caractere.
A maioria desses caracteres é filtrada quando as opções de ignorar espaços estão ativadas (mas caracteres que não são classificados como espaço, como o U+200B, podem continuar mesmo com a opção ativada). Mudando a comparação dentro da linha para caractere, o destaque mostra em que posição está o caractere invisível.
Normalização Unicode: o mesmo caractere, bytes diferentes
A letra "é" pode ser escrita como um único caractere pré-composto (U+00E9) ou como "e" (U+0065) seguido do acento agudo combinante (U+0301). Da mesma forma, a sílaba coreana "가" pode ser o caractere completo U+AC00 ou a sequência das partes ㄱ (U+1100) e ㅏ (U+1161). O Unicode chama a primeira forma de NFC e a segunda de NFD (UAX #15). Os dois textos aparecem idênticos na tela, mas, na comparação, são caracteres diferentes. Um exemplo típico é o nome de arquivo criado no macOS que, em outro sistema, aparece com o acento separado da letra. A solução é converter os dois lados para NFC antes de comparar.
Padronizando quebras de linha no git
Em repositórios usados em vários sistemas operacionais, dá para padronizar as quebras de linha com configurações do git.
core.autocrlf: comtrue, converte LF em CRLF no checkout e CRLF em LF no commit (usado principalmente no Windows). Cominput, só converte CRLF em LF no commit..gitattributes: define as regras no nível do repositório, então o resultado é o mesmo mesmo que cada pessoa tenha configurações diferentes.
* text=auto
*.sh text eol=lf
*.bat text eol=crlf
text=auto normaliza para LF, dentro do repositório, as quebras de linha dos arquivos que o git considera texto, e eol=lf/eol=crlf definem a quebra de linha usada ao gravar os arquivos na pasta de trabalho.
Experimente nesta ferramenta
A opção Normalizar fim de linha do comparador de textos vem ativada por padrão: trata CRLF, CR e LF como a mesma quebra de linha e ignora a presença ou ausência da quebra no fim do arquivo. Desativando-a, o CR no fim da linha aparece como ␍ e a última linha sem quebra aparece com ∅, e você consegue ver as diferenças que estavam invisíveis. As opções de espaço estão em Opções de ignorar espaços, e o significado dos símbolos do resultado em Como ler um unified diff.