Trabalhando com mais de uma configuração do git em um único computador

A configuração do Git pode mudar automaticamente conforme a pasta do projeto, e isso resolve um problema comum de quem usa o mesmo computador para trabalho e projetos pessoais. Com o recurso includeIf, você define uma única vez o nome e o e-mail de cada contexto e nunca mais precisa configurar repositório por repositório.

Neste artigo, você vai entender como o Git organiza suas configurações, montar uma estrutura de pastas e criar arquivos separados para cada contexto. Em seguida, vai ligar tudo no arquivo global e validar o resultado.

O problema de manter mais de uma configuração do Git

Em muitas empresas, a pessoa desenvolvedora usa a própria máquina para trabalhar. Nesse cenário, surge uma dúvida frequente: como separar a configuração do Git dos projetos pessoais da configuração dos projetos profissionais?

O risco é real. Afinal, um commit no repositório da empresa assinado com o e-mail pessoal, ou o contrário, fica registrado para sempre no histórico.

Uma solução inicial bastante comum é configurar cada repositório manualmente logo depois do clone. Por exemplo, o fluxo costuma ser este:

  • Clonar o repositório.
  • Entrar na pasta do repositório.
  • Definir o nome e o e-mail locais do autor dos commits.

Os comandos usados nesse último passo ficam assim:

git config user.name "John Doe"
git config user.email "john@example.com"

Funciona, mas é repetitivo e fácil de esquecer. Por isso, a ideia aqui é fazer essas configurações uma única vez e deixar o Git escolher a identidade certa sozinho.

Este guia foi testado em uma distribuição Linux, e os passos valem também para macOS. Mais adiante, há uma seção específica com observações para o Windows.

Como funciona a configuração do Git em níveis

Antes de separar os contextos, vale entender onde a configuração do Git fica guardada. O Git lê as opções de vários arquivos, cada um com um escopo diferente.

O arquivo .gitconfig

O .gitconfig é um arquivo oculto que armazena as configurações globais do Git do seu usuário. Nele ficam, por exemplo, o nome exibido no log dos commits, o seu e-mail e os aliases que você criou.

Você manipula os valores desse e de outros arquivos com o comando git config, escolhendo o nível com uma das opções abaixo:

# Nível do repositório atual (.git/config)
git config --local user.name "John Doe"

# Nível do usuário (~/.gitconfig)
git config --global user.name "John Doe"

# Nível do sistema (/etc/gitconfig)
git config --system user.name "John Doe"

O .gitconfig global será o responsável por carregar a configuração certa para cada projeto. Ou seja, ele funciona como ponto de entrada de toda a solução.

Precedência entre os níveis

A documentação oficial do git config descreve três níveis principais. O arquivo /etc/gitconfig vale para o sistema todo, o $HOME/.gitconfig vale para o usuário e o .git/config vale para um repositório.

Segundo a documentação, o $HOME/.gitconfig fornece valores de reserva para o .git/config. Além disso, quando a mesma opção aparece mais de uma vez, o último valor lido prevalece.

Essa regra é a chave de tudo. Então, se o arquivo global incluir outro arquivo no final, os valores desse arquivo incluído substituem os valores definidos antes.

Se você quiser revisar a instalação e a configuração inicial, veja o artigo sobre Git e GitHub: instalação, configuração e primeiros passos.

Estabelecendo um padrão de organização

Para alcançar o objetivo, você precisa primeiro definir um modelo de organização para os projetos. A sugestão é simples e funciona bem na prática.

Primeiro, crie um diretório chamado www em um local de sua preferência. Em seguida, entre nele e crie um diretório para a empresa e outro para os projetos pessoais:

mkdir -p ~/www
cd ~/www
mkdir side-projects && mkdir any-company

Essa estrutura é necessária porque o Git vai aplicar uma configuração específica para cada diretório. Dessa forma, todo projeto clonado em side-projects terá uma configuração do Git diferente da aplicada em any-company.

O exemplo usa apenas dois diretórios, mas você pode criar quantos quiser. Por exemplo, um consultor pode ter uma pasta para cada cliente, cada uma com sua própria identidade.

Criando um arquivo de configuração do Git para cada contexto

Com as pastas prontas, o próximo passo é criar um arquivo de configuração do Git para cada contexto. Esses arquivos ficarão na raiz do seu usuário, ao lado do .gitconfig global.

Criando os arquivos individuais

Volte para a raiz do seu usuário com um dos comandos abaixo, que têm o mesmo efeito:

cd $HOME
# ou
cd ~

Na raiz do usuário, crie um arquivo .gitconfig para cada diretório que você criou dentro de www. Para isso, use o comando touch:

touch .gitconfig-side-projects && touch .gitconfig-any-company

O nome dos arquivos é livre. No entanto, usar o nome da pasta como sufixo deixa claro qual arquivo pertence a qual contexto.

Definindo os valores de cada arquivo

Agora abra os arquivos criados no terminal ou no editor de sua preferência. Com o Visual Studio Code, por exemplo, o comando fica assim:

code .gitconfig-side-projects

Com o arquivo aberto, comece pela configuração mais básica, que é o nome e o e-mail. Então, copie e cole o trecho abaixo, trocando os valores pelos seus:

[user]
    name = Your Name
    email = you@personal-example.com

Salve o arquivo e repita o processo para os demais arquivos criados. Lembre que cada um deve conter a configuração específica do seu contexto, como o e-mail corporativo no arquivo da empresa:

[user]
    name = Your Name
    email = you@any-company.com

O formato é o mesmo do .gitconfig global. Portanto, qualquer opção válida no arquivo principal também funciona nesses arquivos menores.

Incluindo os arquivos na configuração do Git global

Abra no terminal ou no editor o arquivo chamado .gitconfig, que fica na raiz do seu usuário. Em seguida, cole o trecho abaixo a partir da última linha do arquivo:

[includeIf "gitdir:~/www/side-projects/"]
    path = ~/.gitconfig-side-projects
[includeIf "gitdir:~/www/any-company/"]
    path = ~/.gitconfig-any-company

Salve o arquivo e volte para a raiz do usuário. Com isso, a configuração do Git global passa a ter condicionais que identificam em qual diretório está o repositório atual.

Quando a condição é verdadeira, o Git carrega o arquivo indicado em path. Assim, todo commit feito dentro de ~/www/side-projects/ usa o nome e o e-mail declarados no .gitconfig-side-projects.

Colocar os blocos no fim do arquivo não é por acaso. Como o último valor lido prevalece, o arquivo incluído sobrescreve um [user] padrão que esteja definido antes no .gitconfig global.

Regras do gitdir e a barra final

A documentação explica que o texto depois de gitdir: funciona como um padrão glob. Então, se o local do diretório .git do repositório corresponder ao padrão, a condição é atendida.

Algumas regras desse padrão merecem atenção:

  • O prefixo ~/ é expandido para o diretório $HOME do usuário.
  • O prefixo ./ aponta para o diretório do arquivo de configuração que contém o includeIf.
  • Um padrão sem ~/, ./ ou / no início recebe **/ automaticamente.
  • Um padrão terminado em / recebe ** no final, o que inclui todas as subpastas.

A última regra é a mais importante. Sem a barra final, o padrão gitdir:~/www/any-company não cobre os repositórios dentro da pasta, e a configuração do Git da empresa simplesmente não é carregada.

Por isso, sempre termine o caminho com /. Dessa forma, qualquer repositório clonado em qualquer nível abaixo da pasta herda a configuração correta.

Outras condições do includeIf

Além do gitdir, a documentação lista outras condições. O gitdir/i faz a mesma comparação sem diferenciar maiúsculas de minúsculas, o que ajuda em sistemas de arquivos que não fazem essa distinção.

O onbranch verifica o nome do branch atual. Já o hasconfig:remote.*.url aplica a configuração quando algum remote do repositório tem uma URL compatível com o padrão.

Essa última condição é útil quando você não quer depender da pasta. Por exemplo, ela permite aplicar o e-mail da empresa a todo repositório cujo remote aponte para a organização da empresa, onde quer que ele esteja clonado.

[includeIf "hasconfig:remote.*.url:https://github.com/any-company/**"]
    path = ~/.gitconfig-any-company

A documentação traz uma restrição para essa condição: o arquivo incluído por ela não pode conter URLs de remotes. Portanto, mantenha nesse arquivo apenas opções como nome, e-mail e chave de assinatura.

Validando a configuração do Git em cada pasta

Depois de salvar tudo, vale conferir se a configuração do Git está sendo aplicada corretamente. O teste mais simples é entrar em um repositório e consultar o e-mail ativo:

cd ~/www/any-company/some-repository
git config user.email

O comando deve exibir o e-mail corporativo. Em seguida, repita o teste em um repositório dentro de side-projects e confirme que o e-mail pessoal aparece.

Para descobrir de qual arquivo veio cada valor, use a opção --show-origin:

git config --list --show-origin

A saída mostra o caminho do arquivo ao lado de cada opção. Assim, você identifica rapidamente se o arquivo incluído foi carregado ou se algum .git/config local está sobrescrevendo o valor.

Lembre que a condição gitdir depende da existência do diretório .git. Portanto, fora de um repositório inicializado, o Git não carrega o arquivo condicional.

Exemplo completo do arquivo global

Para visualizar o resultado final, observe um .gitconfig global completo. Ele define uma identidade padrão, alguns aliases e, no fim, os blocos condicionais:

[user]
    # Identidade usada fora das pastas mapeadas
    name = Your Name
    email = you@personal-example.com
[alias]
    st = status
    co = checkout
[includeIf "gitdir:~/www/side-projects/"]
    path = ~/.gitconfig-side-projects
[includeIf "gitdir:~/www/any-company/"]
    path = ~/.gitconfig-any-company

Repare que o bloco [user] aparece antes dos condicionais. Assim, ele vale como padrão, e os arquivos incluídos só o substituem quando o repositório está em uma das pastas mapeadas.

Os aliases, por outro lado, continuam valendo em todos os contextos. Ou seja, você centraliza no arquivo global o que é comum e deixa nos arquivos menores apenas o que muda.

Erros comuns na configuração do Git por pasta

Mesmo sendo simples, essa configuração do Git costuma falhar por alguns motivos recorrentes. Então, vale conhecer cada um antes de começar a investigar.

  • Esquecer a barra final no gitdir, o que impede a correspondência com os repositórios dentro da pasta.
  • Ter um user.email definido no .git/config do repositório, que vence o valor global.
  • Escrever o caminho do path errado, apontando para um arquivo que não existe.
  • Colocar os blocos includeIf antes do [user] global, que acaba sobrescrevendo os valores incluídos.
  • Clonar o repositório fora da estrutura de pastas planejada.

Além disso, a documentação avisa que links simbólicos no caminho do $GIT_DIR não são resolvidos antes da comparação. Portanto, se você acessa os projetos por um atalho simbólico, confira qual caminho o Git realmente enxerga.

Configuração do Git no Windows e no WSL

Os passos deste artigo foram executados em Linux, e funcionam igualmente em qualquer distribuição e no macOS. No Windows, o caminho mais direto é manter o fluxo de trabalho dentro do WSL, que se comporta como uma distribuição Linux.

O includeIf é um recurso do próprio Git, descrito na documentação oficial sem restrição de sistema operacional. Então, quem usa o Git diretamente no Windows também pode adotar a mesma configuração do Git, ajustando os caminhos para o padrão local.

Nesse caso, o gitdir/i costuma ser a escolha mais segura. Afinal, o sistema de arquivos do Windows normalmente não diferencia maiúsculas de minúsculas nos nomes de pastas.

Se você usa o Windows sem WSL, teste a configuração com o git config --list --show-origin antes de confiar nela. Dessa forma, você confirma que o padrão de caminho está correto na sua máquina.

Indo além do nome e do e-mail na configuração do Git

Este artigo abordou apenas nome e e-mail, mas os arquivos por contexto aceitam qualquer opção. Por exemplo, você pode definir aliases diferentes, um editor específico ou regras de formatação de commit para a empresa.

Também é comum separar a assinatura de commits. Nesse caso, cada arquivo define a sua própria chave em user.signingkey, e o Git usa a chave certa conforme a pasta.

Para quem quer entender como o Git guarda essas informações internamente, o artigo Git por dentro do encanamento aprofunda o funcionamento do diretório .git. Com essa base, a configuração do Git deixa de parecer mágica.

O autor original deste conteúdo também compartilhou o próprio arquivo global como referência. Você pode conferir o .gitconfig global no Gist e adaptar as ideias ao seu ambiente.

Conclusão

Agora você tem um ambiente pronto para receber quantos projetos quiser, sem se preocupar com as configurações iniciais de cada repositório. Isso traz mais agilidade e evita commits assinados com a identidade errada.

O segredo está em combinar uma boa organização de pastas com o includeIf no arquivo global. Além disso, a barra final no gitdir e a ordem dos blocos garantem que o Git carregue o arquivo certo.

Por fim, valide sempre a configuração do Git com git config --list --show-origin. Assim, você tem certeza de que cada contexto usa exatamente a identidade esperada.

FAQ

O includeIf funciona para repositórios já clonados?

Sim. A condição é avaliada sempre que o Git lê as configurações, então basta o repositório estar dentro da pasta indicada no gitdir.

Por que o meu e-mail da empresa não está sendo aplicado?

Confira se o caminho do gitdir termina com barra e se nenhum .git/config local define outro e-mail. O comando git config --list --show-origin mostra a origem de cada valor.

Preciso apagar o user global do .gitconfig?

Não é obrigatório. Como os blocos includeIf ficam no fim do arquivo, os valores incluídos substituem o [user] definido antes.

Posso separar contextos pela URL do remote em vez da pasta?

Sim. A condição hasconfig:remote.*.url aplica o arquivo quando algum remote tem uma URL compatível com o padrão informado.

Isso funciona no macOS?

Sim. Os mesmos passos usados no Linux funcionam no macOS, já que o recurso faz parte do próprio Git.