Excelente estratégia para "dotfiles"

Recentemente estive pesquisando sobre "dotfiles" para migrar facilmente minhas configurações entre diferentes máquinas e sistemas e acabei esbarrando numa estratégia que considerei excelente, mas que não vi uma alta divulgação, principalmente em português.

Isso me motivou a criar esse 1º post meu aqui no TabNews, pois achei que realmente agregaria valor.

TL; DR;

  • "dotfiles" são arquivos de configuração do sistema
  • Uma estratégia muito comum para sincroniza-los é o uso de links simbólicos para os arquivos de um repositório Git. Porém, isso pode ser inviável em algumas situações
  • Nesse post procuro divulgar uma estratégia alternativa que cria um alias para um comando git com 2 argumentos específicos

Descrevendo rapidamente o que são "dotfiles"

São arquivos de configuração que normalmente tem o nome começando com "ponto" ("." ou "dot") e que são usados comumente para configurar a execução de ambientes ou ferramentas.

Exemplos de arquivos "dotfiles" comumente conhecidos: .gitconfig, .profile, .bashrc, .zshrc, .condarc, etc . . .

Geralmente também se localizam nas pasta home do usuário (o que não é obrigatório) e comumente são pré-definidos como arquivos ocultos, que é uma convenção comum para arquivos ou pastas cujo nome começam com ponto "." (eu particularmente prefiro nunca ocultar esses arquivos).

Estratégia comum para "dotfiles": usando "links simbólicos"

Nas minhas pesquisas percebi que uma estratégia muito comum para definir dotfiles passa por armazená-los em um subdiretório da sua escolha, tornando-o um repositório Git versionado, e depois criar links simbólicos dos dotfiles da sua máquina que apontam para os arquivos deste diretório.

Essa estratégia está inclusive mencionada em outro post aqui do TabNews e popularizada em português por esse vídeo do Mario Souto.

A minha dificuldade com essa estratégia foi exatamente na criação dos links simbólicos, pois sou usuário de Windows e acho que a criação desses links não é tão trivial no Windows quando você não tem privilégios de administrador do sistema.

Estratégia alternativa excelente para dotfiles: usar um "alias" para um comando git customizado que passa 2 argumentos específicos

Essa estratégia busca usar sua pasta home de usuário (a qual contém os dotfiles) como o "árvore de trabalho" do Git, porém sem inicializá-la diretamente como um repositório Git, pois isso poderia gerar algumas confusões, principalmente quando você possuir algum repositório interno a ela.

Essa estratégia tem o benefício de rastrear dotfiles diretamente com o Git e requer apenas Git e não envolve links simbólicos.

Ideia da estratégia

Toda a estratégia se baseia em passar 2 argumentos específicos para o comando git: --git-dir e --work-tree.

  • --work-tree: esse argumento é usado para definir um diretório fixo como a "árvore de trabalho" do Git, que será apontada para a pasta home do usuário. Normalmente (quando não usamos esse argumento) a árvore de trabalho do Git é a própria pasta onde o comando git está sendo chamado.
  • --git-dir: esse argumento é usado para definir um diretório fixo como o "banco de dados" do repositório, que pode ser apontado para qualquer outra pasta da sua preferência. Normalmente (quando não usamos esse argumento) o diretório do "banco de dados" do repositório é uma pasta (comumente oculta) nomeada .git que se localiza no mesmo diretório da arvore de trabalho, criada no instante da inicialização do repositório.

Execução da estratégia

Uma vez definida a pasta onde você quer manter o "banco de dados" do seu dotfiles, basta definir um alias da sua preferência no shell da sua preferência.

Por exemplo, caso o banco de dados do repositório dotfiles seja a pasta $HOME/Repos/dotfiles/.git (pode ser qualquer outro) podemos usar o alias config (que também pode ser outro nome):

  • bash/zsh:
alias config='git --git-dir=$HOME/Repos/dotfiles/.git --work-tree=$HOME'
  • PowerShell: No PowerShell é mais fácil definir um alias com uma função A variável @Args serve para passar outros argumentos adicionais para o comando git.
function config() {git --git-dir=$HOME/Repos/dotfiles/.git --work-tree=$HOME @Args}
  • cmd: Pode-se tentar usar o doskey para criar um alias no CMD, mas há muita limitação. Talvez seja mais fácil criar um script .bat para utilizar o comando. Eu particularmente não uso mais o CMD, mas coloquei esse shell aqui para caso alguém tenha uma solução para utilizá-lo nos comentários.

Exemplo de uso da estratégia

config add .bashrc
config commit -m "Adicionando arquivo .bashrc"
config log
config push

Ignorando arquivos da pasta home

Você pode adicionar alguns arquivos da pasta home usando o comando config add (como mostrado acima) e ignorar todos os outros arquivos não adicionados da sua pasta home por padrão, para evitar mostrar todos eles quando executar um config status.

Você pode fazer isso com um simples arquivo .gitignore contendo um único *. Porém isso tem duas desvantagens:

  1. Vai obrigá-lo a adicionar novos arquivos com o comando config add -f para forçar adição dos arquivos não adicionados antes.
  2. Vai ignorar todos os outros arquivos não adicionados, mesmo aqueles que você queria que fossem rastreados.

Para evitar isso, ao invés de ignorar tudo com apenas um único * no .gitignore, você poder escolher uma pasta que você não quer que seja ignorada e definir um padrão que adicione essa pasta para o Git rastrear. Por exemplo:

Escolhendo $HOME/minhasConfigs como uma pasta que não deve ser ignorada, defina seu .gitignore da seguinte forma:

/*
!minhasConfigs/

Caso escolha alguma outra subpasta para rastrear, pesquise a forma de excluí-la do .gitignore. Isso pode ser um pouco não trivial e mereça um post separado pois foge do tema aqui, mas esse tópico do Stack Overflow discute algumas formas simples de trabalhar com o .gitignore.

Uma outra estratégia para evitar usar o -f em config add -f é não usar o .gitignore e evitar que os arquivos não adicionados sejam mostrados com a seguinte configuração:

config config status.showUntrackedFiles no

Porém, essa estratégia mantém a 2ª desvantagem: vai ignorar todos os outros arquivos não adicionados.

Usando repositório bare

Eu particularmente prefiro definir o banco de dados do repositório dotfiles apontando-o para uma pasta .git comum, vinda de outro repositório interno da minha máquina, como feito no exemplo acima.

Isso permite que eu utilize ferramentas de visualização de histórico, como o Git Graph ou o SourceTree, que pressupõem a existência dessa pasta padrão .git.

Porém, você pode definir o banco de dados como qualquer outra pasta do computador, inicializando-a como um repositório do tipo bare, passando o argumento --bare para o comando git init.

Por exemplo: caso eu queira que o banco de dados do repositório dotfiles seja simplesmente a pasta $HOME/Repos/dotfiles (pode ser qualquer outra), eu deveria inicializá-la da seguinte forma:

git init --bare $HOME/Repos/dotfiles