Tentando corrigir finais de linha com git filter-branch, mas não tendo sorte

270

Fui mordido pelo problema de final de linha do Windows / Linux com o git. Parece, via GitHub, MSysGit e outras fontes, que a melhor solução é ter seus repositórios locais definidos para usar finais de linhas no estilo linux, mas definidos core.autocrlfcomo true. Infelizmente, eu não fiz isso cedo o suficiente, então agora toda vez que faço alterações, as terminações da linha são acionadas.

Eu pensei que tinha encontrado uma resposta aqui, mas não consigo fazer isso funcionar para mim. Meu conhecimento de linha de comando do Linux é limitado, na melhor das hipóteses, então nem tenho certeza do que a linha "xargs fromdos" faz em seu script. Continuo recebendo mensagens sobre esse arquivo ou diretório e, quando consigo apontá-lo para um diretório existente, ele diz que não tenho permissões.

Eu tentei isso com o MSysGit no Windows e através do terminal Mac OS X.

Brian Donahue
fonte
Não consigo aprovar esse tópico quase o suficiente. +1 ++ para fornecer a melhor resposta sobre o assunto.
SJul
Concordo com Charles. No entanto, no meu caso (usando o Mac OS X 10.8)> git config core.autocrlf false funcionou, não> git config core.autocrlf input
user1045085

Respostas:

187

A documentação do git para gitattributes agora documenta outra abordagem para "corrigir" ou normalizar todas as terminações de linha no seu projeto. Aqui está a essência:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

Se algum arquivo que não deve ser normalizado aparecer no status git, desmarque o atributo de texto antes de executar o git add -u.

manual.pdf -text

Por outro lado, os arquivos de texto que o git não detecta podem ter a normalização ativada manualmente.

weirdchars.txt text

Isso aproveita um novo --renormalizesinalizador adicionado no git v2.16.0, lançado em janeiro de 2018. Para versões mais antigas do git, existem mais algumas etapas:

$ echo "* text=auto" >>.gitattributes
$ rm .git/index     # Remove the index to force git to
$ git reset         # re-scan the working directory
$ git status        # Show files that will be normalized
$ git add -u
$ git add .gitattributes
$ git commit -m "Introduce end-of-line normalization"
Russ Egan
fonte
1
Você poderia me dizer qual é o objetivo git reset, por favor?
crdx
1
força o git a reconstruir o índice, durante o qual ele varre cada arquivo para adivinhar se é binário. A rm exclui o índice antigo, a redefinição cria o novo índice.
Russ Egan
16
Obrigado, isso funcionou para mim. Um comando útil após a execução git statusé executar git diff --ignore-space-at-eolapenas para garantir que as únicas alterações que você está enviando sejam as terminações da linha.
Zelanix
1
Nota: A única diferença "real" entre esta e a solução "antiga" está na presença de .gitattributes (com o conteúdo apropriado). Sem isso, git resetnão detectará modificações e, portanto, é inútil.
Rob
3
As instruções sobre o gitattributes página foram atualizados para aproveitar a --renormalizebandeira adicionado v2.16.0 git que foi lançado em janeiro de 2018. A --renormalizebandeira consolida o processo de finais de linha processamento de re-para cada arquivo monitorado em um único comando: git add --renormalize ..
Mike Colina
389

A maneira mais fácil de corrigir isso é fazer um commit que corrija todas as terminações de linha. Supondo que você não tenha nenhum arquivo modificado, faça o seguinte.

# From the root of your repository remove everything from the index
git rm --cached -r .

# Change the autocrlf setting of the repository (you may want 
#  to use true on windows):
git config core.autocrlf input

# Re-add all the deleted files to the index
# (You should get lots of messages like:
#   warning: CRLF will be replaced by LF in <file>.)
git diff --cached --name-only -z | xargs -0 git add

# Commit
git commit -m "Fixed crlf issue"

# If you're doing this on a Unix/Mac OSX clone then optionally remove
# the working tree and re-check everything out with the correct line endings.
git ls-files -z | xargs -0 rm
git checkout .
CB Bailey
fonte
7
PS: Eu recomendei sua correção para o pessoal do github.com e eles atualizaram o guia de ajuda para usar sua solução (anteriormente, ele recomendava apenas um novo clone e uma reinicialização total, que não pareciam obter todos os arquivos.) Help.github. com / lidar com lineendings
Brian Donahue
31
Obrigado ... esta é uma ótima solução. Encontrei no GitHub.
PHLAK 5/11/09
4
Você também pode verificar o config.safecrlf para garantir que você não esteja alterando os crlfs em arquivos que não sejam de texto (como o binário). Confira em docs kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88
4
@ vrish88: Se você estiver nessa situação, é provável que esteja sofrendo de terminações alinhadas e core.safecrlf pode realmente impedir que você faça o que precisa fazer. Provavelmente é mais fácil não usar o safecrlf. O git não costuma errar na detecção de arquivo binário e, se o fizer, você pode marcá-lo manualmente como binário com um atributo .gitat e recuperar a versão correta da confirmação anterior.
CB Bailey
26
A solução mais recente recomendada na resposta de Russ Egan abaixo é mais simples e não envolve coisas assustadoras, como excluir todo o código-fonte , então eu realmente recomendo que as pessoas o usem, mesmo que essa solução antiga tenha 10 vezes mais votos!
Porculus
11

Meu procedimento para lidar com os finais de linha é o seguinte (batalha testada em vários repositórios):

Ao criar um novo repositório:

  • coloque .gitattributeso primeiro commit junto com outros arquivos típicos como .gitignoreeREADME.md

Ao lidar com um repo existente:

  • Criar / modificar de .gitattributesacordo
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n( --no-verifyé para pular ganchos de pré-confirmação)
    • Eu tenho que fazê-lo com frequência suficiente para defini-lo como um alias alias fixCRLF="..."
  • repita o comando anterior
    • sim, é vodu, mas geralmente tenho que executar o comando duas vezes, primeira vez que normaliza alguns arquivos, segunda vez ainda mais arquivos. Geralmente é provavelmente melhor repetir até que nenhum novo commit seja criado :)
  • ir e voltar entre o antigo (pouco antes da normalização) e o novo ramo algumas vezes. Depois de mudar o ramo, às vezes o git encontrará ainda mais arquivos que precisam ser renormalizados!

Em .gitattributesEu declaro todos os arquivos de texto explicitamente como tendo LF EOL, pois geralmente as ferramentas do Windows são compatíveis com o LF, enquanto as ferramentas que não são do Windows não são compatíveis com o CRLF (mesmo muitas ferramentas de linha de comando nodejs assumem o LF e, portanto, podem alterar o EOL em seus arquivos).

Conteúdo de .gitattributes

Meu .gitattributesgeralmente se parece com:

*.html eol=lf
*.js   eol=lf
*.json eol=lf
*.less eol=lf
*.md   eol=lf
*.svg  eol=lf
*.xml  eol=lf

Para descobrir quais extensões distintas são rastreadas pelo git no repo atual, veja aqui

Problemas após normalização

Feito isso, há mais uma ressalva comum.

Diga que você masterjá está atualizado e normalizado e faça o checkout outdated-branch. Muitas vezes, logo após verificar essa ramificação, o git marca muitos arquivos como modificados.

A solução é fazer um commit falso ( git add -A . && git commit -m 'fake commit') e depois git rebase master. Após o rebase, o commit falso deve desaparecer.

jakub.g
fonte
1
Eu pensei que estava ficando louco, até ler a sua postagem, porque também tive que executar a sequência de comandos especificada várias vezes. Voodoo! ;)
Sean Fausett
Com a versão git 2.7.0.windows.1, usei o seguinte: # git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n
Sean Fausett 5/16/16
4
git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

Explicação:

  • git status --short

    Isso exibe cada linha que o git é e não tem conhecimento. Arquivos que não estão sob controle git são marcados no início da linha com um '?'. Os arquivos modificados são marcados com um M.

  • grep "^ *M"

    Isso filtra apenas os arquivos que foram modificados.

  • awk '{print $2}'

    Isso mostra apenas o nome do arquivo sem marcadores.

  • xargs fromdos

    Isso pega os nomes dos arquivos do comando anterior e os executa pelo utilitário 'fromdos' para converter as terminações de linha.

Lloyd Moore
fonte
Isso é incrível. Obrigado. Para quem procura uma solução usando o Homebrew, use em dos2unixvez de fromdos.
Almir Sarajčić
4

Veja como eu corrigi todas as terminações de linha em todo o histórico usando git filter-branch. O ^Mcaractere precisa ser inserido usando CTRL-V+ CTRL-M. Eu costumava dos2unixconverter os arquivos, pois isso pula automaticamente os arquivos binários.

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'
pfrenssen
fonte
3

O "| xargs fromdos" lê da entrada padrão (os arquivos findlocalizam) e a utiliza como argumentos para o comando fromdos, que converte as terminações de linha. (Fromdos é padrão nesses ambientes? Estou acostumado a dos2unix). Observe que você pode evitar o uso de xargs (especialmente útil se você tiver arquivos suficientes para que a lista de argumentos seja muito longa para xargs):

find <path, tests...> -exec fromdos '{}' \;

ou

find <path, tests...> | while read file; do fromdos $file; done

Não tenho muita certeza das suas mensagens de erro. Eu testei com sucesso esse método. Qual programa está produzindo cada um? Para quais arquivos / diretórios você não tem permissão? No entanto, aqui está uma tentativa de adivinhar qual pode ser:

Uma maneira fácil de obter um erro 'arquivo não encontrado' para o script é usar um caminho relativo - use um caminho absoluto. Da mesma forma, você pode obter um erro de permissão se não tiver executado seu script executável (chmod + x).

Adicione comentários e tentarei ajudá-lo a resolver isso!

Cascabel
fonte
Eu vi outro exemplo com o dos2unix e achei que, de alguma forma, estava copiando arquivos para uma pasta chamada that, mas agora entendi. Uau, parece óbvio agora. Obrigado pela ajuda!
Brian Donahue
1

ok ... no cygwin não temos fromdos facilmente disponíveis, e esse subconjunto awk aparece na sua cara se você tiver algum espaço no caminho para arquivos modificados (o que tínhamos), então tive que fazer isso de maneira um pouco diferente:

git status --short | grep "^ *M" | sed 's/^ *M//' | xargs -n 1 dos2unix

parabéns a @lloyd pela maior parte desta solução

Anton K
fonte
-2

Siga estas etapas se nenhuma outra resposta funcionar para você:

  1. Se você estiver no Windows, faça git config --global core.autocrlf true; se você estiver no Unix, façagit config core.autocrlf input
  2. Corre git rm --cached -r .
  3. Excluir o arquivo .gitattributes
  4. Corre git add -A
  5. Corre git reset --hard

Então seu local deve estar limpo agora.

zs2020
fonte
4
Realmente? Excluir .gitattributesarquivo é a solução para o problema de terminações de linha?
Aleksandr M
Sim, envie o comentário por @AleksandrM
Mr_and_Mrs_D