Como fazer o Git “esquecer” de um arquivo que foi rastreado mas agora está no .gitignore?

Existe um arquivo que estava sendo rastreado pelo git , mas agora o arquivo está na lista .gitignore .

No entanto, esse arquivo continua aparecendo no git status depois de ser editado. Como você força o git a esquecer completamente?

.gitignore impedirá que arquivos não rastreados sejam adicionados (sem um add -f ) ao conjunto de arquivos rastreados pelo git, entretanto o git continuará a rastrear quaisquer arquivos que já estejam sendo rastreados.

Para parar de rastrear um arquivo, você precisa removê-lo do índice. Isso pode ser conseguido com este comando.

 git rm --cached  

A remoção do arquivo da revisão principal ocorrerá no próximo commit.

A série de comandos abaixo removerá todos os itens do Git Index (não do diretório de trabalho ou repository local) e atualizará o Git Index, respeitando o git ignores. PS. Índice = Cache

Primeiro:

 git rm -r --cached . git add . 

Então:

 git commit -am "Remove ignored files" 

git update-index faz o trabalho para mim:

 git update-index --assume-unchanged  

Nota: Esta solução é independente de .gitignore pois gitignore é apenas para arquivos não rastreados.

edit: Desde que esta resposta foi publicada, uma nova opção foi criada e deve ser preferida. Você deve usar --skip-worktree que é para arquivos rastreados modificados que o usuário não deseja mais confirmar e manter --assume-unchanged para desempenho para evitar que o git verifique o status de grandes arquivos rastreados. Veja https://stackoverflow.com/a/13631525/717372 para mais detalhes …

 git ls-files --ignored --exclude-standard -z | xargs -0 git rm --cached git commit -am "Remove ignored files" 

Isso pega a lista dos arquivos ignorados, remove-os do índice e, em seguida, confirma as alterações.

Eu sempre uso este comando para remover esses arquivos não rastreados. Saída de uma linha, estilo Unix, limpa:

 git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached 

Ele lista todos os seus arquivos ignorados, substitui cada linha de saída por uma linha entre aspas, para manipular caminhos com espaços dentro, e passar tudo para git rm -r --cached para remover os caminhos / arquivos / diretórios do índice.

Se você não pode git rm um arquivo rastreado porque outras pessoas podem precisar dele (aviso, mesmo se você git rm --cached , quando alguém recebe essa mudança, seus arquivos serão apagados em seu sistema de arquivos) por favor, olhe https: // gist .github.com / 1423106 de maneiras que as pessoas têm trabalhado em torno do problema.

mova-o para fora, comprometa-o e volte a colocá-lo. Isso funcionou para mim no passado. Há provavelmente uma maneira mais “engenhosa” de realizar isso.

O que não funcionou para mim

(Sob o Linux), eu queria usar os posts aqui sugerindo ls-files --ignored --exclude-standard | xargs git rm -r --cached ls-files --ignored --exclude-standard | xargs git rm -r --cached abordagem em ls-files --ignored --exclude-standard | xargs git rm -r --cached . No entanto, (alguns dos) arquivos a serem removidos tinham uma nova linha incorporada / LF / \n em seus nomes. Nenhuma das soluções:

 git ls-files --ignored --exclude-standard | xargs -d"\n" git rm --cached git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached 

lidar com esta situação (obter erros sobre arquivos não encontrados).

Então eu ofereço

 git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached 

Isso usa o argumento -z para ls-files e o argumento -0 para xargs para atender com segurança / corretamente caracteres “desagradáveis” em nomes de arquivos.

Na página de manual git-ls-files (1) , ele afirma:

Quando a opção -z não é usada, os caracteres TAB, LF e barra invertida nos nomes de caminho são representados como \ t, \ n e \\, respectivamente.

então eu acho que minha solução é necessária se os nomes de arquivos tiverem algum desses caracteres neles.

EDIT: Fui solicitado a adicionar que — como qualquer comando git rm — isso deve ser seguido por um commit para tornar as remoções permanentes, por exemplo, git commit -am "Remove ignored files" .

Eu consegui isso usando git filter-branch . O comando exato que usei foi retirado da página man:

AVISO : isso excluirá o arquivo de todo o seu histórico

 git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD 

Este comando recriará todo o histórico de commits, executando git rm antes de cada commit e assim irá se livrar do arquivo especificado. Não se esqueça de fazer o backup antes de executar o comando, pois ele será perdido.

Use isto quando:

1. Você deseja remover muitos arquivos ou

2. Você atualizou seu arquivo gitignore

Link da fonte: http://www.codeblocq.com/2016/01/Untrack-files-already-added-to-git-repository-based-on-gitignore/

Digamos que você já tenha adicionado / enviado alguns arquivos para o seu repository git e, em seguida, adicione-os ao seu .gitignore; esses arquivos ainda estarão presentes no seu índice de repository. Este artigo vamos ver como se livrar deles.

Etapa 1: confirme todas as suas alterações

Antes de prosseguir, certifique-se de que todas as suas alterações sejam confirmadas, incluindo o seu arquivo .gitignore.

Etapa 2: remover tudo do repository

Para limpar seu repo, use:

 git rm -r --cached . 
  • rm é o comando de remoção
  • -r permitirá a remoção recursiva
  • –Cached só irá remover arquivos do índice. Seus arquivos ainda estarão lá.

O comando rm pode ser implacável. Se você quiser experimentar o que ele faz de antemão, adicione o --dry-run -n ou --dry-run para testar as coisas.

Passo 3: Re adicionar tudo

 git add . 

Etapa 4: confirmar

 git commit -m ".gitignore fix" 

Seu repository está limpo 🙂

Empurre as alterações para o seu controle remoto para ver as mudanças efetivas lá também.

  1. Atualize seu arquivo .gitignore – por exemplo, adicione uma pasta que você não deseja rastrear .gitignore .

  2. git rm -r --cached . – Remova todos os arquivos rastreados, incluindo os desejados e indesejados. Seu código estará seguro, desde que você tenha salvo localmente.

  3. git add . – Todos os arquivos serão adicionados novamente, exceto aqueles em .gitignore .


Hat dica para @AkiraYamamoto por nos apontar na direção certa.

Eu acho que talvez o git não possa esquecer totalmente o arquivo por causa de sua concepção ( seção “Snapshots, Not Differences” ).

Esse problema está ausente, por exemplo, ao usar o CVS. O CVS armazena informações como uma lista de alterações baseadas em arquivos. As informações para o CVS são um conjunto de arquivos e as alterações feitas em cada arquivo ao longo do tempo.

Mas no Git toda vez que você faz commit, ou salva o estado do seu projeto, ele basicamente tira uma foto de como todos os seus arquivos se parecem naquele momento e armazena uma referência àquele snapshot. Então, se você adicionou o arquivo uma vez, ele estará sempre presente no instantâneo.

Esses dois artigos foram úteis para mim:

git assume-inalterado vs skip-worktree e como ignorar as mudanças nos arquivos rastreados com o Git

Baseando-me nele, faço o seguinte, se o arquivo já estiver sendo rastreado:

 git update-index --skip-worktree  

A partir deste momento, todas as alterações locais neste arquivo serão ignoradas e não serão remotas. Se o arquivo for alterado no remoto, ocorrerá um conflito, quando git pull . Stash não funciona. Para resolvê-lo, copie o conteúdo do arquivo para o local seguro e siga estas etapas:

 git update-index --no-skip-worktree  git stash git pull 

O conteúdo do arquivo será substituído pelo conteúdo remoto. Cole suas alterações de um lugar seguro para outro e execute novamente:

 git update-index --skip-worktree  

Se todo mundo, que trabalha com o projeto, executar o git update-index --skip-worktree , problemas com pull devem estar ausentes. Esta solução é válida para arquivos de configurações, quando cada desenvolvedor tem sua própria configuração de projeto.

Não é muito conveniente fazer isso todas as vezes, quando o arquivo foi alterado no remoto, mas pode protegê-lo de sobrescrever por conteúdo remoto.

Mova ou copie o arquivo para um local seguro, para não perdê-lo. Então git rm o arquivo e commit. O arquivo ainda será exibido se você reverter para uma das confirmações anteriores ou outra ramificação em que não foi removida. No entanto, em todos os commits futuros, você não verá o arquivo novamente. Se o arquivo está no git ignore, então você pode movê-lo de volta para a pasta, e o git não o verá.

A resposta de Matt Fear foi a mais eficaz IMHO. A seguir, há apenas um script do PowerShell para os usuários do Windows removerem apenas os arquivos do repository do git que correspondem à lista de exclusão.

 # Get files matching exclusionsfrom .gitignore # Excluding comments and empty lines $ignoreFiles = gc .gitignore | ?{$_ -notmatch "#"} | ?{$_ -match "\S"} | % { $ignore = "*" + $_ + "*" (gci -r -i $ignore).FullName } $ignoreFiles = $ignoreFiles| ?{$_ -match "\S"} # Remove each of these file from Git $ignoreFiles | % { git rm $_} git add . 

O BFG é projetado especificamente para remover dados indesejados, como arquivos grandes ou senhas do Git repos, então ele tem um sinalizador simples que removerá qualquer arquivo grande histórico (não em seu commit atual): ‘- blip-blobs- maior que’

 $ java -jar bfg.jar --strip-blobs-bigger-than 100M 

Se você quiser especificar arquivos por nome, também pode fazer isso:

 $ java -jar bfg.jar --delete-files *.mp4 

O BFG é 10-1000x mais rápido do que o git filter-branch, e geralmente é muito mais fácil de usar – verifique as instruções completas de uso e exemplos para mais detalhes.

Fonte: https://confluence.atlassian.com/bitbucket/reduce-repository-size-321848262.html

Se você não quer usar o CLI e está trabalhando no Windows, uma solução muito simples é usar o TortoiseGit , ele tem a ação “Delete (keep local)” no menu que funciona bem.

Eu gostei da resposta de JonBrave, mas eu tenho diretórios de trabalho bagunçados o suficiente que cometem – me assusta um pouco, então aqui está o que eu fiz:

git config –global alias.exclude-ignorado ‘! git arquivos-l -z –ignored –exclude-standard | xargs -0 git rm -r –cached && git ls-arquivos -z –ignored –exclude-standard | xargs -0 git stage && git stage .gitignore && git commit -m “novo gitignore e remove arquivos ignorados do índice” ‘

quebrando:

 git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached git ls-files -z --ignored --exclude-standard | xargs -0 git stage git stage .gitignore git commit -m "new gitignore and remove ignored files from index" 
  • remover arquivos ignorados do índice
  • stage .gitignore e os arquivos que você acabou de remover
  • cometer

Isso não é mais um problema no último git (v2.17.1 no momento da escrita).

O .gitignore finalmente ignora arquivos rastreados, mas deletados. Você pode testar isso sozinho executando o seguinte script. A declaração de git status final do git status deve informar “nada para confirmar”.

 # Create empty repo mkdir gitignore-test cd gitignore-test git init # Create a file and commit it echo "hello" > file git add file git commit -m initial # Add the file to gitignore and commit echo "file" > .gitignore git add .gitignore git commit -m gitignore # Remove the file and commit git rm file git commit -m "removed file" # Reintroduce the file and check status. # .gitignore is now respected - status reports "nothing to commit". echo "hello" > file git status 

No caso de DS_Store já comprometida:

 find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch 

Ignore-os por:

 echo ".DS_Store" >> ~/.gitignore_global echo "._.DS_Store" >> ~/.gitignore_global echo "**/.DS_Store" >> ~/.gitignore_global echo "**/._.DS_Store" >> ~/.gitignore_global git config --global core.excludesfile ~/.gitignore_global 

Finalmente, faça um commit!