Git: Como remover um arquivo do índice sem excluir arquivos de qualquer repositório

163

Quando você usa

git rm --cached myfile

ele não exclui do sistema de arquivos local, que é o objetivo. Mas se você já fez a versão e comprometeu o arquivo, empurrou-o para um repositório central e o puxou para outro repositório antes de usar o comando, ele excluirá o arquivo desse sistema.

Existe uma maneira de simplesmente remover o arquivo do controle de versão sem excluí-lo de qualquer sistema de arquivos?

Edit: Esclarecido, espero.

Fletcher Moore
fonte
Talvez você possa expandir seu caso de uso. O índice é a área de preparação para a próxima confirmação. Por que você deseja remover o arquivo do índice se não deseja removê-lo da ramificação atual?
CB Bailey
1
Eu sinto Muito. Eu quis dizer que o meu arquivo, por algum motivo, foi confirmado e distribuído há muito tempo. Agora, o objetivo é removê-lo do controle de versão sem excluí-lo dos sistemas das pessoas. A razão pela qual isso surgiu foi porque eu acidentalmente versionei um arquivo de configuração. O arquivo de configuração é necessário, então não quero removê-lo de sistemas estrangeiros.
Fletcher Moore
Editei a pergunta para ficar mais clara.
Fletcher Moore
3
O git rm --cached não removerá o arquivo do outro diretório de trabalho. O arquivo será removido apenas se alguém que trabalha nesse diretório executar um pull. O arquivo pode ser facilmente recuperado com "CABEÇA git checkout @ {1} foo" (se executado imediatamente após a puxar.)
William Pursell

Respostas:

119

Eu não acho que um commit do Git possa registrar uma intenção como "pare de rastrear este arquivo, mas não o exclua".

A realização dessa intenção exigirá intervenção fora do Git em quaisquer repositórios que mesclem (ou se refiram a) uma confirmação que exclui o arquivo.


Salvar uma cópia, aplicar exclusão, restaurar

Provavelmente, a coisa mais fácil a fazer é dizer aos usuários downstream para salvar uma cópia do arquivo, retirar sua exclusão e restaurar o arquivo. Se eles estão recebendo via rebase e estão 'carregando' modificações no arquivo, eles terão conflitos. Para resolver esses conflitos, use git rm foo.conf && git rebase --continue(se a confirmação conflitante tiver alterações além daquelas no arquivo removido) ou git rebase --skip(se a confirmação conflitante tiver sido alterada apenas no arquivo removido).

Restaurar arquivo como não rastreado após obter uma confirmação que o exclui

Se eles já obtiveram seu commit de exclusão, ainda poderão recuperar a versão anterior do arquivo com o git show :

git show @{1}:foo.conf >foo.conf

Ou com o git checkout (por comentário de William Pursell; lembre-se de removê-lo novamente do índice!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

Se eles executaram outras ações desde que você efetuou a exclusão (ou estão recuperando com rebase para um HEAD desanexado), podem precisar de algo diferente @{1}. Eles poderiam usar git log -gpara encontrar o commit antes de remover sua exclusão.


Em um comentário, você menciona que o arquivo que você deseja “rastrear, mas manter” é algum tipo de arquivo de configuração necessário para executar o software (diretamente de um repositório).

Mantenha o arquivo como 'padrão' e ative-o manualmente / automaticamente

Se não for totalmente inaceitável continuar mantendo o conteúdo do arquivo de configuração no repositório, você poderá renomear o arquivo rastreado de (por exemplo) foo.confpara foo.conf.defaulte instruir seus usuários cp foo.conf.default foo.confapós aplicar a confirmação de renomeação. Ou, se os usuários já usam alguma parte existente do repositório (por exemplo, um script ou outro programa configurado pelo conteúdo no repositório (por exemplo, Makefileou similar)) para iniciar / implantar seu software, você pode incorporar um mecanismo padrão no lançamento / processo de implantação:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

Com tal mecanismo inadimplente no lugar, os usuários devem ser capazes de puxar um commit que renomeia foo.confa foo.conf.defaultsem ter que fazer qualquer trabalho extra. Além disso, você evita copiar manualmente um arquivo de configuração se criar instalações / repositórios adicionais no futuro.

Reescrever o histórico requer intervenção manual de qualquer maneira…

Se é inaceitável manter o conteúdo no repositório, você provavelmente desejará erradicá-lo completamente da história com algo parecido git filter-branch --index-filter …. Isso equivale a reescrever o histórico, o que exigirá intervenção manual para cada filial / repositório (consulte a seção “Recuperando da recuperação upstream” na página de manual do git rebase ). O tratamento especial necessário para seu arquivo de configuração seria apenas mais uma etapa que você deve executar enquanto se recupera da reescrita:

  1. Salve uma cópia do arquivo de configuração.
  2. Recupere da reescrita.
  3. Restaure o arquivo de configuração.

Ignore-o para impedir a recorrência

Qualquer que seja o método usado, você provavelmente incluirá o nome do arquivo de configuração em um .gitignorearquivo no repositório para que ninguém possa inadvertidamente git add foo.confrepetir (é possível, mas requer -f/ --force). Se você tiver mais de um arquivo de configuração, considere 'movê-los' todos para um único diretório e ignorar tudo. o mecanismo de inicialização / implantação) para copiar / mover os arquivos para seu novo local; você obviamente não gostaria de colocar um arquivo mv em um diretório que você estará ignorando).

Chris Johnsen
fonte
5
Resposta incrivelmente completa. Obrigado!
Fletcher Moore
A resposta de Tom Power, abaixo, parece contradizer sua primeira frase.
Mike S
1
@MikeS: O efeito de --{,no-}assume-unchangedé puramente local: seu estado não é registrado diretamente em confirmações. Ele pode ajudar a impedir que um repositório comprometa novas alterações no arquivo, mas não o remove do controle de versão. Se você pode configurá-lo para todos os seus repositórios relevantes, relacionados e não expostos, isso pode ajudar sua situação, mas não é algo que você poderia diretamente empurrar + puxar / rebase para outros repositórios não expostos relacionados (especialmente aqueles que você não controla, de acordo com os comentários esclarecedores do autor original sobre a questão: consulte “sistemas de pessoas” / “sistemas estrangeiros”).
Chris Johnsen
1
I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.- agora pode, comgit rm --cached foo.conf
Nick Volynkin
1
@NickVolynkin: A pergunta já não indica que isso é inadequado para o objetivo do solicitante: (automaticamente) manter o arquivo por perto quando o commit resultante for puxado para outro repositório?
precisa
86

Tive o mesmo problema nesta semana, quando acidentalmente cometi, tentei remover um arquivo de compilação de um repositório compartilhado, e isto:

http://gitready.com/intermediate/2009/02/18/temporariamente-ignoring-files.html

funcionou bem para mim e não foi mencionado até agora.

git update-index --assume-unchanged <file>

Para remover o arquivo do seu controle de versão, use todos os outros comandos normalmente.

git update-index --no-assume-unchanged <file>

Se você sempre quis colocá-lo de volta.

Editar: veja os comentários de Chris Johnsen e KPM, isso só funciona localmente e o arquivo permanece sob controle de versão para outros usuários se eles também não o fizerem. A resposta aceita fornece métodos mais completos / corretos para lidar com isso. Além disso, algumas notas do link se você usar este método:

Obviamente, existem algumas ressalvas que entram em jogo com isso. Se você adicionar o arquivo diretamente, ele será adicionado ao índice. A mesclagem de uma consolidação com esse sinalizador fará com que a mesclagem falhe normalmente, para que você possa manipulá-la manualmente.

Tom Power
fonte
7
Esta não é uma resposta para o problema específico mencionado. Ele funcionará apenas para seu próprio repositório local, portanto, cada usuário deve fazer isso sozinho. É uma dor.
KPM
28

Para remover o arquivo do índice, use:

git reset myfile

Isso não deve afetar sua cópia local ou de qualquer outra pessoa.

Armand
fonte
1
resetsomente remove o arquivo do índice se o arquivo não estiver na confirmação HEAD atual, caso contrário, apenas reverterá a versão do índice para a versão HEAD atual.
CB Bailey
1
Talvez eu tenha entendido mal a pergunta, mas ela parece estar relacionada à remoção de um arquivo do índice sem afetar nenhuma confirmação.
Armand
Como Charles disse, a redefinição não "remove um arquivo". Digite git help reset para obter mais informações.
Simon B.
4
Isso responde à pergunta intitulada "Como remover o arquivo do índice sem excluir arquivos de qualquer repositório". Embora o OP realmente estivesse perguntando "Como rastrear um arquivo sem excluir a cópia local".
hewsonism
@hewsonism, o OP disse "qualquer sistema de arquivos" (mesmo antes de qualquer edição).
Jbobbins 14/05/2019
19
  1. git rm --cached remove_file
  2. adicionar arquivo ao gitignore
  3. git add .gitignore
  4. git commit -m "Excluding"
  5. Diverta-se ;)
Анастасия Ермолик
fonte
1
Este é o mais fácil.
Julien
15

Depois de executar o git rm --cachedcomando, tente adicionar myfileao .gitignorearquivo (crie um se ele não existir). Isso deve dizer ao git para ignorar myfile.

O .gitignorearquivo é versionado, então você precisará confirmá-lo e enviá-lo ao repositório remoto.

desagradável
fonte
1

Minha solução é puxar a outra cópia de trabalho e depois:

git log --pretty="format:" --name-only -n1 | xargs git checkout HEAD^1

que diz obter todos os caminhos de arquivo no último comentário e verificá-los do pai do HEAD. Tarefa concluída.

Tom Viner
fonte
0

As soluções acima funcionam bem na maioria dos casos. No entanto, se você também precisar remover todos os rastreamentos desse arquivo (ou seja, dados confidenciais, como senhas), também será necessário removê-lo de todo o histórico de confirmação, pois o arquivo ainda poderá ser recuperado a partir daí.

Aqui está uma solução que remove todos os rastreamentos do arquivo de todo o seu histórico de consolidação, como se nunca tivesse existido, mantendo o arquivo no seu sistema.

https://help.github.com/articles/remove-sensitive-data/

Você pode realmente pular para a etapa 3 se estiver no seu repositório git local e não precisar executar uma execução a seco. No meu caso, eu só precisava das etapas 3 e 6, pois já havia criado meu arquivo .gitignore e estava no repositório em que queria trabalhar.

Para ver suas alterações, pode ser necessário acessar a raiz do GitHub do seu repositório e atualizar a página. Em seguida, navegue pelos links para acessar um commit antigo que já possuía o arquivo, para ver se ele foi removido. Para mim, simplesmente atualizar a página de confirmação antiga não mostrava a alteração.

Parecia intimidador a princípio, mas na verdade era fácil e funcionava como um encanto! :-)

SherylHohman
fonte