Manage Your Configuration Files Using Git
There are dozens of popular ways to manage user dotfile configurations, but I think the simplest might be to just use a bare git repository for each platform.
2024-06-20 Update: I started using chezmoi a few months ago and have been very satisfied with it. Check the comparison table to see some of the tradeoffs between popular methods.
This approach is easy to set up, requires no additional tools beyond git
and allows any file below the user’s home directory to be tracked.
For example, to share my emacs config between Windows and Linux, I created a bare git repo called .dot-all
, which I use to manage any configuration that is to be shared between platforms.
I also have a Windows-only repo (.dot-win
) and one for configs shared between Linux and FreeBSD (.dot-unix
)
C:\Users\ccammack
λ cd %USERPROFILE%
C:\Users\ccammack
λ git init --bare .dot-all.git
Initialized empty Git repository in C:/Users/ccammack/.dot-all.git/
C:\Users\ccammack
λ ls .dot-all.git\
config description HEAD hooks/ info/ objects/ refs/
To manage files in one directory using a bare repository in a different directory, git
relies on the --work-tree
and --git-dir
parameters to specify those two directories for each git
operation.
To ensure that both of these parameters are specified properly when working with configuration files, most articles recommend using a shell alias to create an alternate version of the git
command that assigns these parameters their proper values.
However, I think a less-common approach works better; rather than creating a shell alias for each bare repo, create a separate git alias for each bare repo instead.
For example, to run git
commands on the bare .dot-all
repo, create a git alias that sets the parameters to the correct values.
C:\Users\ccammack
λ git config --global --replace-all alias.dot-all "!git --git-dir=C:\\Users\\ccammack\\.dot-all.git --work-tree=C:\\Users\\ccammack"
C:\Users\ccammack
λ git config --global --list
[...]
alias.dot-all=!git --git-dir=C:\\Users\\ccammack\\.dot-all.git --work-tree=C:\\Users\\ccammack
Now, to operate on files managed by the .dot-all
repo, use git dot-all
rather than git.
C:\Users\ccammack
λ git dot-all status
[...]
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
[...]
nothing added to commit but untracked files present (use "git add" to track)
Home directories usually have a lot of files in them and the default status
command will show all of them.
To fix this, change the local
configuration for the repo to disable showUntrackedFiles
and try again.
C:\Users\ccammack
λ git dot-all config --local status.showUntrackedFiles no
C:\Users\ccammack
λ git dot-all config --list
[...]
status.showuntrackedfiles=no
C:\Users\ccammack
λ git dot-all status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
Now that the setup is complete, add and commit the files that are to be tracked using the .dot-all
repo.
C:\Users\ccammack
λ git dot-all add .doom.d\*
C:\Users\ccammack
λ git dot-all status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .doom.d/config.el
new file: .doom.d/init.el
new file: .doom.d/packages.el
Untracked files not listed (use -u option to show untracked files)
C:\Users\ccammack
λ git dot-all commit -m "Add emacs configuration."
[master (root-commit) a0508e6] Add emacs configuration.
3 files changed, 310 insertions(+)
create mode 100644 .doom.d/config.el
create mode 100644 .doom.d/init.el
create mode 100644 .doom.d/packages.el
C:\Users\ccammack
λ git dot-all remote add origin http://git.ccammack.com:3000/ccammack/dot-all.git
C:\Users\ccammack
λ git dot-all push -u origin master
[...]
To replicate the shared configuration onto another machine, git clone
the repo using the --bare
option, set up the global dot-all
git alias and disable showUntrackedFiles
.
For example, the following commands replicate the .dot-all
repo and its files onto an Ubuntu desktop machine.
ccammack@ubuntu:~$ cd $HOME
ccammack@ubuntu:~$ git clone --bare http://git.ccammack.com:3000/ccammack/dot-all.git $HOME/.dot-all.git
Cloning into bare repository '/home/ccammack/.dot-all.git'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), 6.17 KiB | 6.17 MiB/s, done.
ccammack@ubuntu:~$ git config --global --replace-all alias.dot-all '!git --git-dir=$HOME/.dot-all.git --work-tree=$HOME'
ccammack@ubuntu:~$ git config --global --list
alias.dot-all=!git --git-dir=$HOME/.dot-all.git --work-tree=$HOME
ccammack@ubuntu:~$ git dot-all config --local status.showUntrackedFiles no
ccammack@ubuntu:~$ git dot-all config --list
[...]
status.showuntrackedfiles=no
ccammack@ubuntu:~$ git dot-all checkout
error: The following untracked working tree files would be overwritten by checkout:
.doom.d/config.el
.doom.d/init.el
.doom.d/packages.el
Please move or remove them before you switch branches.
Aborting
The checkout
will fail if local copies of the repository files already exist. If the local changes should be preserved, copy them to a temporary location, perform the checkout
again and
then overwrite the checked out files with the temporary copies.
If the local changes are not important, simply overwrite them with checkout -f
.
ccammack@ubuntu:~$ git dot-all checkout -f
ccammack@ubuntu:~$ git dot-all log
commit a0508e6d89880d744ec7122cecb1da27ddc62488 (HEAD -> master)
Author: ccammack <ccammack@example.com>
Date: Sun Jun 20 20:07:47 2021 -0700
Add emacs configuration.