How to manage dotfiles with git bare repo

20 Aug, 2022 (updated: 02 Sep, 2023)
772 words | 4 min to read | 2 hr, 19 min to write

I first heard of this method from the Atlassian’s blogpost The best way to store your dotfiles: A bare Git repository.

My suspicion bar strikes up high whenever I hear something as hyperbolised as “the best”, it turned out it very well may be. At least I could never be happier ever since I started using it.

I do believe that if you’ve never tried it you should give it a shot.

Before (my dotfiles.old)

Before I stumbled upon the Atlassian’s blogpost I used to store my dotfiles in a git repo and then I would have a script that symlinks all of those into ~ (home directory). Very straightforward, I bet many people still using this approach.

You can still access my dotfiles.old repo on github. Basically all you do is clone it anywhere on your system and then run a simple python script to symlink all the files into ~:

git clone https://github.com/Nemoden/dotfiles.old.git && cd dotfiles
./bootstrap
vim +PlugInstall +qa

This method has a significant drawback though which comes with managing what can go and what can not go under the version control inside directories. Sometimes you want to have the whole directory in your github repo, but sometimes you want all the files but 1, or 2… or many. The reason for this is sometimes the contents of the directory may partially be not for public access. Good luck with that if you are using symlinked dotfiles.

Let’s say you are working for a company and you have company-specific configs, or scripts sitting in your ~/.scripts/ or something like that. Some of those may contain sensitive data, or expose structural information about the company’s infrastructure.

Not only that, but some of your files may contain license keys. Like I’m storing an intelephense license key in ~/.vim/coc-user-config.vim. This means I’d like to commit the whole ~/.vim, but a single file into my git repo.

This is solvable but in not a very straightforward way.

Alternatives

There are dotfiles managers, such as chezmoi, dotbot, yadm, and some others.

I have never tried any of those. I just feel like managing dotfiles with git bare repo is a very straightforward and clean solution and I don’t want to rely on a tool other than git.

But if you feel like giving one of those tools a go, go ahead, they must not be bad as so many people are using them.

One tip I can give you here is to check those projects’ issues on github. Just open up the “Issues” tab and see if they have any bugs that may make using the tool for you cumbersome or straight-up unbearable.

Git bare repo

Well, it’s all in the Atlassian’s blogpost, but I believe I’ve made a couple of adjustments. And since I’m using fish shell I also replaced their config alias with a fish function (and renamed it to dot).

If you are looking to use this method with fish, feel free to steal anything from my new dotfiles

First things first, init a bare repo anywhere you like on your system, I like mine to be in ~/.dot:

git init --bare $HOME/.dot

Now define a fish function named dot (or you can pick any name you like more):

function dot -w git -d "Manages dotfiles"
git --git-dir=$HOME/.dot --work-tree=$HOME $argv
end

store it in ~/.config/fish/functions/dot.fish, source it source ~/.config/fish/functions/dot.fish to make it available straight away.

And now, Atlassian’s guide suggests setting a git config setting status.showUntrackedFiles to no. But what it allows is accidental dot add . && dot commit && dot push. You’ll ask how can somebody just add the whole contents of ~ into git accidentally? I don’t know… I might. I have git add . && git commit -am in my muscle memory too deep.

I definitely prefer a method that I use which is adding * to .gitignore:

echo "*" > ~/.gitignore

Now adding any dotfile into git is less straightforward but also less accidental since now you have to force-add it. So the following doesn’t work

dot add sensitive-information.pem
The following paths are ignored by one of your .gitignore files:
sensitive-information.pem
hint: Use -f if you really want to add them.
hint: Turn this message off by running
hint: "git config advice.addIgnoredFile false"

The way you add files or directories is:

dot add -f .dotfile
dot add -f .config/fish/functions/public-function.fish
dot add -f .whole-directory

Conclusion

Git bare repo is a nearly perfect solution for managing dotfiles.

If you still doing it with symlinks, I strongly recommend you look into this solution.