Skip to content
This page has been auto-translated and may contain errors.View in English

Ignorando arquivos e boas práticas ​

docs.scrimba.com

Você clona um projeto chamado weather-app, roda npm install e confere o status do seu novo repositório.

bash
$ git status
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	node_modules/
	dist/
	.env

Três pastas e arquivos aparecem como não rastreados, e nenhum deles deveria estar em um commit. node_modules/ são milhares de arquivos que o npm install regenera a partir do package.json em segundos. dist/ é o resultado do build, que é reconstruído a partir do seu código-fonte a cada vez. E .env guarda uma chave de API. Nada disso deveria entrar no histórico do seu projeto, e digitar git add . sem pensar é exatamente como isso acaba acontecendo de qualquer jeito.

Dizendo ao Git o que ignorar ​

Um arquivo .gitignore lista padrões de arquivos e pastas que o Git nunca deve rastrear. Você o cria uma única vez na raiz do seu projeto, o mesmo lugar onde você rodou git init quando configurou o repositório em Seu primeiro repositório, e a partir daí, git status e git add . simplesmente ignoram tudo que corresponder aos padrões.

bash
# .gitignore
node_modules/
dist/
.env

Cada linha é um padrão: um nome de pasta simples como dist/ ignora essa pasta inteira, em qualquer lugar em que ela apareça no projeto. Faça commit do próprio arquivo .gitignore. Ele é pequeno, é útil para todo mundo que clona o projeto, e merece estar no histórico assim como o seu código-fonte.

bash
$ git status
On branch main
nothing to commit, working tree clean

Com os padrões configurados, o git status fica quieto. E é exatamente esse o objetivo: as pastas e arquivos que você nunca quer commitar deixam de aparecer como ruído, então o que aparece é o que realmente importa.

Os padrões aceitam globs, então *.log ignora todo arquivo que termina em .log, seja qual for o nome. Uma barra no final restringe um padrão a diretórios, então dist/ corresponde só a uma pasta chamada dist e deixa de lado qualquer arquivo que por acaso tenha esse mesmo nome. Comece uma linha com ! para deixar de ignorar algo que um padrão mais amplo capturaria, útil quando um arquivo dentro de uma pasta ignorada precisa ser rastreado:

bash
# .gitignore
logs/
!logs/keep-this.log

O Git também lê um arquivo de ignore global, um por máquina, para padrões que você quer em todo repositório, independente do projeto: lixo de editor como .DS_Store ou *.swp. Configure-o uma vez com git config --global core.excludesfile ~/.gitignore_global, e você nunca mais precisa adicionar esses padrões projeto por projeto.

Os padrões de ignore são comparados do mesmo jeito que globs de shell: * para qualquer sequência de caracteres, ? para um único caractere, ** para correspondência entre diretórios. Um padrão sem barra corresponde em qualquer profundidade do projeto, enquanto um padrão que contém uma barra fica ancorado naquele caminho. Essa correspondência é um comportamento puramente local: o Git só consulta o .gitignore na hora de decidir o que mostrar em git status e git add. Isso não impede que um arquivo continue rastreado depois que o Git já o tem sob controle, o que importa muito na próxima seção.

JunoDizendo ao Git o que ignorar Um arquivo .gitignore lista o que o Git nunca deve rastrear: node_modules/, dist/ e .env são os suspeitos de sempre em praticamente qualquer projeto. Faça commit do próprio arquivo .gitignore para que todo mundo que clonar o projeto tenha o mesmo status limpo. Uma vez eu perdi uma tarde inteira porque adicionei por acidente milhares de arquivos de dependência antes de aprender isso.
JunoDizendo ao Git o que ignorar Os padrões aceitam globs como *.log, uma barra no final significa "só diretório", e um ! no início deixa de ignorar algo dentro de uma correspondência mais ampla. Um arquivo de ignore global, configurado com core.excludesfile, é o lugar certo para lixo de editor e do sistema operacional, assim você para de adicionar .DS_Store manualmente no .gitignore de cada projeto.
JunoDizendo ao Git o que ignorar As regras de ignore são correspondências de glob no estilo shell, aplicadas do lado do cliente, e elas só mudam o que o Git mostra como não rastreado. Lembre-se de que uma regra só pode afetar um arquivo que o Git ainda não conhece. Essa distinção parece pequena no papel, mas custa caro na prática.

Nunca faça commit de segredos ​

Uma chave de API, uma senha de banco de dados ou um certificado de assinatura nunca deveriam aparecer em um commit. Isso vale tanto para um repositório privado quanto para um público, e vale desde o primeiro commit em diante. Coloque os segredos em um arquivo como .env, adicione esse arquivo ao .gitignore desde o primeiro dia, e carregue os valores no seu app a partir dali, em vez de escrevê-los diretamente nos seus arquivos de código.

bash
# .env
WEATHER_API_KEY=sk_live_9f8e7d6c5b4a
bash
# .gitignore
.env

Siga essa regra automaticamente, sempre, sem exceções. Uma chave presente em um commit fica acessível para qualquer pessoa com acesso ao repositório, e, em um repositório público, para absolutamente qualquer pessoa.

Em um projeto real, os segredos geralmente ficam em mais de um lugar: um arquivo .env para a sua própria máquina, e um gerenciador de segredos ou as configurações de variáveis de ambiente do seu provedor de hospedagem para tudo que estiver em produção. Os dois funcionam do mesmo jeito na prática: o valor fica fora dos seus arquivos de código e fora do Git, e seu código o lê a partir do ambiente em tempo de execução. Em vez disso, distribua um arquivo .env.example, com os nomes das variáveis mas sem os valores reais, para que seus colegas saibam o que configurar sem nunca ver sua chave real.

Aqui está o fato que torna essa regra inegociável: um commit do qual você remove um segredo não o remove do histórico. Todo commit anterior que tinha a chave ainda a tem, salva para sempre no histórico do repositório, e todo clone que alguém já fez leva esses commits antigos junto. Excluir o arquivo em um novo commit só significa que o snapshot mais recente não o tem mais; o objeto que guarda o valor antigo continua ali no .git, acessível para qualquer pessoa que dê checkout em um commit anterior ou pesquise no log.

"Excluir o arquivo e fazer commit de novo" nunca resolve um segredo que já foi exposto. Se um segredo real acabar entrando em um commit, revogue-o ou gere um novo no provedor (emita uma chave nova, invalide a antiga) para que o valor exposto deixe de ser útil, independentemente do que você fizer no repositório depois. Reescrever o histórico para remover um segredo é possível, mas isso não desfaz nenhum uso que a chave já teve enquanto estava exposta. Trate a rotação da chave como a solução que realmente importa, e a limpeza do histórico como um extra opcional.

JunoNunca faça commit de segredos Nunca coloque uma chave de API, senha ou certificado real em um commit. Guarde os segredos em um arquivo como .env e adicione o .env ao seu .gitignore antes mesmo de rodar o primeiro git add. Essa é a única regra deste capítulo inteiro que vale tratar como absoluta.
JunoNunca faça commit de segredos Segredos locais vão no .env, segredos de produção vão nas variáveis de ambiente do seu provedor de hospedagem ou em um gerenciador de segredos, e nenhum dos dois nunca é commitado. Distribua um .env.example só com os nomes das variáveis, para que seus colegas saibam o que configurar sem ver um valor real.
JunoNunca faça commit de segredos Excluir um segredo em um novo commit não o remove do histórico. Todo commit anterior e todo clone existente ainda o tem. Gere uma nova chave no provedor no instante em que uma vazar: essa rotação é a solução que realmente importa.

Adicionando um arquivo ao .gitignore depois que ele já está rastreado ​

Essa é a pegadinha que apanha quase todo mundo pelo menos uma vez. Você comita um arquivo por acidente, percebe o erro, adiciona ele ao .gitignore (a linha com echo abaixo acrescenta o caminho a esse arquivo) e espera que o Git se esqueça dele. Mas não é isso que acontece.

bash
$ git add config/settings.json
$ git commit -m "Add app settings"

# later, you realize this was a mistake
$ echo "config/settings.json" >> .gitignore
$ git status
On branch main
nothing to commit, working tree clean

O git status mostra uma árvore limpa, mas config/settings.json continua rastreado e continua aparecendo em todo git log futuro e em todo clone. As regras de ignore só valem para arquivos que o Git ainda não conhece. Uma vez que um arquivo foi adicionado e commitado, ele passa a fazer parte do seu histórico, e o .gitignore não tem nenhuma influência sobre arquivos que já estão nesse histórico.

Para realmente parar de rastrear esse arquivo daqui para frente, diga ao Git para deixar de acompanhá-lo, mantendo o arquivo no seu disco:

bash
$ git rm --cached config/settings.json
$ git commit -m "Stop tracking config/settings.json"

git rm --cached <arquivo> remove o arquivo do rastreamento do Git sem excluí-lo do seu diretório de trabalho. Faça commit dessa remoção, e a partir daí seu padrão no .gitignore passa a fazer o trabalho que você queria desde o início.

O mesmo ajuste funciona para uma pasta inteira com git rm -r --cached <pasta>, que é o recurso mais comum depois de perceber que node_modules/ ou dist/ foram commitados logo no início da vida de um projeto, antes de alguém ter adicionado um .gitignore. Rode o comando uma vez, faça commit da remoção, e a pasta desaparece dos commits futuros, enquanto a cópia local dos arquivos de cada colega continua intacta no disco.

Esse comportamento é consequência direta do que a área de staging realmente é. O Git rastreia arquivos por meio do índice, o registro exato do que vai entrar no próximo commit. Adicionar e commitar um arquivo escreve uma entrada para ele no índice e no snapshot do commit; o .gitignore só é consultado quando o Git está decidindo o que fazer com um arquivo que ainda não tem entrada no índice.

Uma vez que a entrada existe, os padrões de ignore não têm efeito algum sobre ela. O git rm --cached remove a entrada do índice mantendo o arquivo no disco, e é por isso que esse é o comando que de fato resolve o problema, enquanto editar só o padrão de ignore não faz nada.

JunoAdicionando um arquivo ao .gitignore depois que ele já está rastreado Adicionar um arquivo ao .gitignore depois que o Git já o rastreia não muda nada para esse arquivo. As regras de ignore só valem para arquivos que o Git ainda nunca adicionou. Para parar de rastrear um arquivo que entrou por acidente, rode git rm --cached <arquivo> e faça commit dessa mudança.
JunoAdicionando um arquivo ao .gitignore depois que ele já está rastreado O .gitignore só afeta arquivos não rastreados. Para qualquer coisa que já foi commitada, remova-a com git rm --cached <arquivo>, ou git rm -r --cached <pasta> para um diretório inteiro, e depois faça commit. Os arquivos locais de todo mundo continuam onde estavam, só o rastreamento do Git sobre eles muda.
JunoAdicionando um arquivo ao .gitignore depois que ele já está rastreado O índice guarda uma entrada para cada arquivo rastreado, e o .gitignore só é consultado quando ainda não existe uma entrada. O git rm --cached remove a entrada do índice sem tocar no arquivo no disco, e é esse o ajuste que realmente resolve o problema aqui. Adicione também o padrão de ignore, por segurança, para que o arquivo não volte a entrar sem querer.

Boas práticas: commits pequenos e um repositório limpo ​

Um .gitignore bem cuidado é só metade do caminho para manter um repositório agradável de se trabalhar. A outra metade é como você organiza os seus commits. Faça de cada commit uma mudança focada: uma correção de bug, uma pequena funcionalidade, um único refactor. Se uma tarde de trabalho toca em várias coisas sem relação entre si, divida em vários commits em vez de jogar tudo em um só no fim do dia.

bash
$ git add src/weather-widget.js
$ git commit -m "Fix temperature rounding in weather widget"

Commits pequenos são mais fáceis de revisar, mais fáceis de desfazer se algo quebrar, e mais fáceis de entender seis meses depois, quando você está tentando lembrar por que uma linha de código existe. O ciclo de commits explica o que faz uma mensagem de commit ser útil; esse hábito é sobre manter a própria mudança pequena o suficiente para que escrever uma boa mensagem seja até possível.

Combine esse hábito com um .gitignore bem cuidado e o resultado se multiplica: um histórico feito de commits pequenos e objetivos, sem pastas geradas automaticamente ou arquivos de configuração perdidos sujando o diff. Revisar uma mudança cheia de ruído do dist/ junto com as duas linhas reais que alguém editou é péssimo para todo mundo envolvido, e isso é totalmente evitável com um .gitignore configurado desde o primeiro dia.

Em escala, isso compensa de formas que vão além da legibilidade. Um repositório livre de arquivos gerados e pastas de dependências fica pequeno o suficiente para clonar rápido e pesquisar com facilidade, e um histórico de commits focados é o que faz o git log --oneline e o git diff entre dois pontos no tempo serem realmente úteis para entender o que aconteceu e por quê. Nada disso funciona bem contra um histórico em que metade dos commits é ruído de node_modules/ e a outra metade mistura cinco mudanças sem relação entre si.

JunoBoas práticas: commits pequenos e um repositório limpo Mantenha cada commit em uma única mudança focada, e mantenha seu .gitignore cobrindo as pastas e os segredos que nunca deveriam ser rastreados. Esses dois hábitos juntos são a maior parte do que torna um repositório agradável de se trabalhar. Hábitos pequenos, formados desde o início, poupam bastante limpeza mais tarde.
JunoBoas práticas: commits pequenos e um repositório limpo Commits pequenos e focados, junto com um .gitignore bem cuidado, tornam revisões e diffs realmente legíveis, em vez de enterrados em ruído gerado automaticamente. Configure o .gitignore antes do seu primeiro commit. Esperar até o node_modules/ aparecer em um pull request pela quinta vez custa mais limpeza do que economiza.
JunoBoas práticas: commits pequenos e um repositório limpo Um repositório leve e um histórico de commits focados são o que mantêm os clones rápidos e o histórico legível quando você precisa consultá-lo. Todo arquivo gerado ou pasta de dependência que se infiltra no histórico é um pequeno custo que todo clone e toda pesquisa futura vão pagar. Configure as regras de ignore desde o início, e isso deixa de ser um problema com o qual você precisa se preocupar de novo.