O .gitignore não apaga o que já foi comitado
A regra é uma só, e explica toda a confusão que vem a seguir: o .gitignore só se aplica a ficheiros que o Git ainda não está a seguir. A partir do momento em que um ficheiro foi comitado uma vez, está dentro — e a linha que o manda ignorar passa a ser, ela própria, ignorada.
1. Como é que isto se nota
Acrescenta-se .env ao .gitignore. Confirma-se que está lá. E o ficheiro continua a aparecer no git status, alterado, a pedir para ser comitado. Não é um bug nem um cache estragado: o Git está a fazer exactamente o que lhe foi dito, porque aquele ficheiro já era seguido antes de a linha existir.
2. Deixar de o seguir sem o apagar
git rm --cached .env
git commit -m "chore: parar de seguir o .env"O --cached é a palavra toda: remove o ficheiro do índice e deixa-o em disco. Sem ele, o git rm apaga-o também, o que para um ficheiro de segredos é um fim de tarde desagradável. Para uma pasta, o mesmo com -r:
git rm -r --cached node_modules3. A armadilha da correção, que é pior do que o problema
Aquele commit não é neutro: para o Git, deixar de seguir um ficheiro é apagá-lo. Quem fizer pull depois disso vê o ficheiro desaparecer da sua própria cópia.
Para o node_modules não faz diferença nenhuma. Para o .env de um colega, acabou de se apagar a configuração local dele — e ele vai descobri-lo quando a aplicação não arrancar, sem ligar as duas coisas. Se a equipa é mais do que uma pessoa, este commit merece um aviso antes, não depois.
4. Quando o ficheiro devia ser ignorado e não é
O caso inverso é mais difícil de ver, porque não há nada para ler: o ficheiro aparece no git status e o .gitignore parece estar certo. Há um comando para isto, e é pouco conhecido:
git check-ignore -v .envA resposta diz o ficheiro, a linha e o padrão que decidiu:
.gitignore:13:.env .envSem resposta nenhuma significa que nenhuma regra o apanha — e aí o problema está no padrão, não no Git. Com várias origens de regras em jogo, é a diferença entre saber e tentar à sorte.
5. A negação, e o sítio onde ela não funciona
Um padrão pode ser desfeito com !. Mas há um limite que apanha todos pelo menos uma vez: a negação não resgata um ficheiro dentro de uma pasta já excluída, porque o Git nem desce lá para ver.
node_modules/
!node_modules/guardar-este.js # não faz nadaMuitas vezes a negação é desnecessária, porque o padrão já era mais estreito do que parecia. Uma linha com .env apanha o .env e mais nada: o .env.example continua versionado sem precisar de regra nenhuma, porque o padrão é exacto e não um prefixo.
6. O histórico continua lá, e isso é outro assunto
Deixar de seguir um ficheiro resolve o futuro. Todos os commits antigos continuam a ter o conteúdo dele, intacto e acessível a quem clone o repositório.
Para o node_modules é só peso. Para uma password é indiferente ter-se deixado de a seguir: ela está lá, e a resposta certa é a do artigo sobre um segredo comitado, que começa por rodar o segredo e não por reescrever a história.
7. Que regras pertencem ao projeto, e que regras não
O .gitignore do repositório é para o que todas as cópias têm de ignorar: o resultado do build, o código gerado, os segredos. Neste site são quatro entradas e cada uma tem um motivo — o cliente do Prisma, por exemplo, está lá porque é gerado a partir do esquema e regenerado na construção da imagem, e um ficheiro gerado em controlo de versões é um convite a um conflito que ninguém consegue resolver.
O que é da sua máquina não pertence ali. A pasta do seu editor, os seus ficheiros de rascunho: isso vai para o .git/info/exclude, que é local e não é comitado, ou para um .gitignore global. Pôr .idea/ num ficheiro partilhado porque é o editor que usa é impor a sua caixa de ferramentas a quem usa outra.
O resumo
O .gitignore trava ficheiros novos. Não desfaz nada, não apaga nada do histórico, e não tem efeito algum sobre o que já é seguido. Quando parece não funcionar, a pergunta não é «o padrão está certo?» — é «este ficheiro já foi comitado alguma vez?», e o git check-ignore -v responde à primeira.