Git - Diferença entre 'assumir inalterado' e 'ignorar-área de trabalho'

450

Tenho alterações locais em um arquivo que não quero confirmar no meu repositório. É um arquivo de configuração para criar o aplicativo em um servidor, mas quero criar localmente com configurações diferentes. Naturalmente, o arquivo sempre aparece quando eu faço o 'status git' como algo a ser preparado. Gostaria de ocultar essa alteração específica e não a comprometer. Não farei outras alterações no arquivo.

Depois de algumas investigações, vejo duas opções: 'assume inalterado' e 'pula a árvore de trabalho'. Uma pergunta anterior aqui fala sobre eles, mas realmente não explica suas diferenças. Minha pergunta é a seguinte: como os dois comandos são diferentes? Por que alguém usaria um ou outro?

ckb
fonte
1
Normalmente eu estou usando .gitignorepara fins semelhantes. Essa solução funcionaria para você?
samuil 29/11
45
samuil, .gitignore ignora adicionar, não alterar. Quando arquivo já está em git este será seguido evento, se ela está listada em .gitignore
Grigory
mas não se pode remover tudo e adicionar tudo para "atualizar", como explicado aqui? stackoverflow.com/questions/7075923/... @Grigory
Daniel Springer
2
O arquivo não deve ser ignorado, se eu receber a intenção do OP corretamente. O arquivo deve estar no repositório, mas essas alterações muito específicas que ele fez não devem ser confirmadas - não agora, pelo menos.
Simone

Respostas:

666

Você quer skip-worktree.

assume-unchangedfoi projetado para casos em que é caro verificar se um grupo de arquivos foi modificado; quando você define o bit, git(é claro) assume que os arquivos correspondentes a essa parte do índice não foram modificados na cópia de trabalho. Portanto, evita uma confusão de statchamadas. Este bit é perdido sempre que a entrada do arquivo no índice é alterada (portanto, quando o arquivo é alterado upstream).

skip-worktreeé mais do que isso: mesmo quando git sabe que o arquivo foi modificado (ou precisa ser modificado por um reset --hardou algo semelhante), ele fingirá que não o foi, usando a versão do índice. Isso persiste até que o índice seja descartado.

Há um bom resumo das ramificações dessa diferença e dos casos de uso típicos aqui: http://fallengamer.livejournal.com/93321.html .

Desse artigo:

  • --assume-unchangedassume que um desenvolvedor não deve alterar um arquivo. Esse sinalizador destina-se a melhorar o desempenho de pastas que não mudam, como SDKs.
  • --skip-worktreeé útil quando você instrui o git a não tocar em um arquivo específico, porque os desenvolvedores devem alterá-lo. Por exemplo, se o repositório principal upstream hospedar alguns arquivos de configuração prontos para produção e você não desejar confirmar acidentalmente alterações nesses arquivos, --skip-worktreeé exatamente o que você deseja.
Borealid
fonte
3
Isso faz sentido. skip-worktree realmente parece ser o caminho a percorrer. Obrigado!
Ckb
100
Uma pequena nota para economizar alguns segundos pesquisando e lendo. Para cancelar --skip-worktreeefeitos e desmarcar a bandeira, existe a --no-skip-worktreeopção. Funciona exatamente da mesma maneira. Isso é útil caso uma mão tenha escorregado e arquivos errados tenham sido sinalizados ou se as circunstâncias tenham mudado e os arquivos anteriormente ignorados não devam mais ser ignorados.
drdaeman 7/08/15
18
Para responder à minha própria pergunta acima, a diferença entre usar --skip-worktreee o .git/info/excludearquivo é que o primeiro funcionará mesmo para os arquivos rastreados no momento. .git/info/exclude, como .gitignore, impedirá a adição acidental de arquivos não rastreados ao índice, mas não fará alterações nos arquivos já rastreados.
precisa saber é o seguinte
13
Isso pode ser enviado para o controle remoto e preservado por todos os clones?
CMCDragonkai
4
Apenas o uso , senhora:git update-index --skip-worktree <file_name>
ruffin
108

Nota: o fallengamer fez alguns testes em 2011 (para que possam estar desatualizados), e aqui estão suas descobertas :

Operações

  • O arquivo é alterado no repositório local e no upstream
    git pull: o
    Git preserva as alterações locais de qualquer maneira.
    Assim, você não perderia acidentalmente nenhum dado marcado com qualquer uma das bandeiras.
    • Arquivo com assume-unchangedsinalizador: o Git não substitui o arquivo local. Em vez disso, geraria conflitos e conselhos sobre como resolvê-los
    • Arquivo com skip-worktreesinalizador: o Git não substitui o arquivo local. Em vez disso, geraria conflitos e conselhos sobre como resolvê-los

  • O arquivo é alterado no repositório local e no upstream, tentando puxar de qualquer maneira. Usando resultados em algum trabalho manual extra, mas pelo menos você não perderia nenhum dado se tivesse alguma alteração local.
    git stash
    git pull
    skip-worktree
    • Arquivo com assume-unchangedsinalizador: descarta todas as alterações locais sem a possibilidade de restaurá-las. O efeito é como ' git reset --hard'. ' git pull' chamada terá sucesso
    • Arquivo com skip-worktreesinalizador: o Stash não funcionaria em skip-worktreearquivos. ' git pull' falhará com o mesmo erro acima. O desenvolvedor é forçado a redefinir manualmente o skip-worktreesinalizador para poder ocultar e concluir a falha pull.

  • Nenhuma alteração local, arquivo upstream alterado Os dois sinalizadores não impediriam que você obtivesse alterações upstream. O Git detecta que você quebrou a promessa e escolheu refletir a realidade redefinindo a bandeira.
    git pull
    assume-unchanged
    • Arquivo com assume-unchangedsinalizador: o conteúdo é atualizado, o sinalizador é perdido.
      ' git ls-files -v' mostraria que o sinalizador é modificado para H(de h).
    • Arquivo com skip-worktreesinalizador: o conteúdo é atualizado, o sinalizador é preservado.
      ' git ls-files -v' mostraria a mesma Sbandeira de antes do pull.

  • Com o arquivo local alterado, o Git não toca no arquivo e reflete a realidade (o arquivo que prometeu permanecer inalterado foi realmente alterado) para o arquivo.
    git reset --hard
    skip-worktreeassume-unchanged
    • Arquivo com assume-unchangedsinalizador: o conteúdo do arquivo é revertido. O sinalizador é redefinido para H(de h).
    • Arquivo com skip-worktreesinalizador: o conteúdo do arquivo está intacto. A bandeira permanece a mesma.

Ele adiciona a seguinte análise:

  • Parece que skip-worktreeestá tentando muito preservar seus dados locais . Mas isso não impede que você receba alterações upstream, se for seguro. Além disso, o git não redefine a bandeira pull.
    Mas ignorar o reset --hardcomando ' ' pode se tornar uma surpresa desagradável para um desenvolvedor.

  • Assume-unchangedA flag pode ser perdida na pulloperação e as alterações locais dentro desses arquivos não parecem ser importantes para o git.

Vejo:

Ele conclui:

Na verdade, nenhuma das bandeiras é intuitiva o suficiente .

  • assume-unchangedassume que um desenvolvedor não deve alterar um arquivo. Se um arquivo foi alterado - essa alteração não é importante. Esse sinalizador é destinado a melhorar o desempenho de pastas que não mudam, como SDKs.
    Mas se a promessa for quebrada e um arquivo for realmente alterado, o git reverte a flag para refletir a realidade. Provavelmente, não há problema em ter alguns sinalizadores inconsistentes em pastas que geralmente não devem ser alteradas.

  • Por outro lado, skip-worktreeé útil quando você instrui o git a nunca tocar em um arquivo específico. Isso é útil para um arquivo de configuração já rastreado.
    O repositório principal upstream hospeda algumas configurações prontas para produção, mas você gostaria de alterar algumas configurações na configuração para poder fazer alguns testes locais. E você não deseja verificar acidentalmente as alterações nesse arquivo para afetar a configuração de produção. Nesse caso, skip-worktreefaz cena perfeita.


Com o Git 2.25.1 (fevereiro de 2020), o "Na verdade nenhum dos sinalizadores é intuitivo o suficiente" mencionado acima é esclarecido:

Consulte commit 7a2dc95 , commit 1b13e90 (22 jan 2020) por brian m. Carlson ( bk2204) .
(Mesclado por Junio ​​C Hamano - gitster- no commit 53a8329 , 30 de janeiro de 2020)
( lista de discussão do Git )

doc: dissuadir os usuários de tentar ignorar arquivos rastreados

Assinado por: Jeff King
Assinado por: brian m. Carlson

É bastante comum os usuários quererem ignorar as alterações em um arquivo que o Git rastreia.

Os cenários comuns para esse caso são as configurações e os arquivos de configuração do IDE, que geralmente não devem ser rastreados e possivelmente gerados a partir de arquivos rastreados usando um mecanismo de modelo.

No entanto, os usuários aprendem sobre os bits assumir-inalterados e ignorar-worktree e tentam usá-los para fazer isso de qualquer maneira.

Isso é problemático porque, quando esses bits são definidos, muitas operações se comportam conforme o usuário espera, mas geralmente não ajudam quando é git checkoutnecessário substituir um arquivo.

Não há comportamento sensível nesse caso, porque algumas vezes os dados são preciosos, como certos arquivos de configuração, e outras são dados irrelevantes que o usuário ficaria feliz em descartar.

Como essa não é uma configuração suportada e os usuários tendem a usar incorretamente os recursos existentes para fins não intencionais, causando tristeza e confusão geral , vamos documentar o comportamento existente e as armadilhas na documentação para git update-indexque os usuários saibam que devem explorar soluções alternativas.

Além disso, vamos fornecer uma solução recomendada para lidar com o caso comum de arquivos de configuração, pois existem abordagens conhecidas usadas com sucesso em muitos ambientes.

A git update-indexpágina de manual agora inclui:

Os usuários geralmente tentam usar os bits assume-unchangede skip-worktreepara dizer ao Git para ignorar as alterações nos arquivos rastreados. Isso não funciona como esperado, pois o Git ainda pode verificar os arquivos da árvore de trabalho em relação ao índice ao executar determinadas operações. Em geral, o Git não fornece uma maneira de ignorar as alterações nos arquivos rastreados, portanto, soluções alternativas são recomendadas.

Por exemplo, se o arquivo que você deseja alterar for algum tipo de arquivo de configuração, o repositório poderá incluir um arquivo de configuração de amostra que poderá ser copiado para o nome ignorado e modificado. O repositório pode até incluir um script para tratar o arquivo de amostra como um modelo, modificando e copiando-o automaticamente.

Essa última parte é o que descrevo um driver típico de filtro de conteúdo com base em scripts smudge / clean .

VonC
fonte
7
Se você tem skip-worktree em um arquivo e as alterações anteriores, você recebe "commit ou stash" quando tenta puxar, mesmo que o status git não relate o arquivo como alterado. Como você pode evitar isso, para que as alterações locais possam persistir enquanto as pessoas estão mexendo nas configurações de produção na origem?
GreenAsJade 11/04
3
Sim, posso confirmar que sim. Isso significa que ainda é muito difícil ter um arquivo local que você deseja manter de forma diferente da origem.
GreenAsJade 12/12
1
@GreenAsJade do que parece antigo. Alguma chance de testá-lo com um 2.2.x?
VonC 12/12/14
1
@VonC, O link para "Comentário do Junio" não está no histórico de revisões. É a isso que você estava se referindo?
Michael - Onde está Clay Shirky,
1
@ Michael Boa captura, obrigado. Eu coloquei esse link de volta na resposta.
VonC 24/10/19