Intro #
Since I switched to linux 6 months ago I have had my dotfiles backed up to my NAS. These dotfiles specify how some of my environments and tools are configured and I’ve been wanting to publish them for others to view. The problem was that I wasn’t happy with some of the approaches to achieve this until I came across this post which mentioned using a bare git repository to manage dot files. The post made it seem like a good solution so I investigated further and gave it a try.
What is a bare git repository? #
To understand a bare git repository, first let’s look at a traditional git repository. Under a traditional git repository the .git
metadata is a folder within the project folder. The rest of your code and files are then within this project folder. Here’s a basic example:
my-project/
├── .git/ # Git metadata (hidden)
│ ├── HEAD
│ ├── objects/
│ ├── refs/
│ ├── config
│ └── ...
├── src/ # ← Your project files
├── README.md # ← Your project files
└── ... # ← Your project files
Files within the my-project
folder are then inherently part of git’s working tree which means git will automatically pick up on changes to files made within your project folder.
Conversely, a bare git repository instead has the git
metadata at a folder’s root level, and there is no direct working tree which means your bare git repository would look something like this:
my-project.git/ # `.git` in folder name is a convention
├── HEAD # Git metadata at root level
├── objects/
├── refs/
├── config
└── ...
Since there is no direct working tree under a bare git repository, you can instead designate a folder as the working tree. This can be useful for servers or backup situations where perhaps you want to designate a high-level folder as the working tree. There’s a bit more nuance to bare git repositories but for our purposes here this should be a sufficient background.
Dotfile options; why not symlink? #
The obvious problem with managing/sharing dotfiles is that their contents are spread throughout your system, so under a traditional repository you aren’t able to capture these files unless you use symlinks or a symlink manager like Stow. Symbolic links work by making a file appear to be in two separate locations. To make these symbolic links work best with git you need to first move the files into your traditional dotfiles
repository and then symlink the files to the place your computer normally expects them.
The downside to these symlink approaches for me was added complexity. I would either be adding extra steps to make it work with each interaction, or I would be adding a layer (Stow) on top of my dotfiles and git.
When I read Marcel’s post the simplicity of the bare git repository seemed to be exactly what I was looking for. No symlinks, no additional packages, and pretty straightforward setup. I also really liked the idea of using a native git feature I had not heard of before–I had to try it.
Setting up a bare git repository for dotfiles #
The whole set-up took me less than 5 minutes. Here are the exact steps I took, with some commentary along the way:
1. create bare git repo #
$ DOTFILES=$HOME/.config/dotfiles.git
$ git init --bare $DOTFILES
Initialized empty Git repository in /home/rogue/.config/dotfiles.git/
The DOTFILES
can be wherever you want the git folder. The .git
in the name is a convention to show that it is a bare git repository.
2. create an alias to use #
$ alias gitdf='git --git-dir=$DOTFILES --work-tree=$HOME'
Here we specify a --work-tree
which for my purposes I set as $HOME
. If you know you’ll want to use this for dotfiles outside of your home directory, you could also set this to your root directory.
You’ll also want to take a moment and add the alias to your shell’s rc file for future use.
Note: we’ll be using
gitdf
for git commands going forward.
3. update config to not show untracked files #
$ gitdf config status.showUntrackedFiles no
You’ll want this so you dont see all the untracked files listed when using gitdf status
4. add specified directories and files #
Use gitdf add
to add any directory and file you want to be tracked by your bare git repository. If you intend to make the dotfiles available in a public git forge then make sure you avoid adding any sensitive files or directories like ~/.ssh
.
Here are the files and directories I chose to start:
$ gitdf add ~/.config/hypr/
$ gitdf add ~/.config/kitty/
$ gitdf add ~/.config/waybar/
$ gitdf add ~/.bashrc
5. check status of tracked files #
$ gitdf status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .bashrc
new file: .config/hypr/hyprland.conf
new file: .config/hypr/hyprpaper.conf
new file: .config/kitty/current-theme.conf
new file: .config/kitty/kitty.conf
new file: .config/waybar/config
new file: .config/waybar/hypr-shared.json
new file: .config/waybar/power_menu.xml
new file: .config/waybar/scripts/toggle-audio.sh
new file: .config/waybar/style.css
Untracked files not listed (use -u option to show untracked files)
This will show the files you’ve added to your bare git repository, or if you’ve made any changes to a tracked file.
6. Initial commit #
$ gitdf commit -m "initial commit"
[main (root-commit) 18b0531] initial commit
12 files changed, 1228 insertions(+)
create mode 100644 .bashrc
create mode 100644 .config/hypr/hyprland.conf
create mode 100644 .config/hypr/hyprpaper.conf
create mode 100644 .config/kitty/current-theme.conf
create mode 100644 .config/kitty/kitty.conf
create mode 100644 .config/waybar/config
create mode 100644 .config/waybar/hypr-shared.json
create mode 100644 .config/waybar/power_menu.xml
create mode 100755 .config/waybar/scripts/toggle-audio.sh
create mode 100644 .config/waybar/style.css
7. set remote #
$ gitdf remote add origin ssh://[email protected]/racehd/dotfiles.git
Just specify your own remote for wherever you intend to push the files to.
8. push #
$ gitdf push --set-upstream origin main
Enumerating objects: 20, done.
Counting objects: 100% (20/20), done.
Delta compression using up to 32 threads
Compressing objects: 100% (19/19), done.
Writing objects: 100% (20/20), 12.52 KiB | 12.52 MiB/s, done.
Total 20 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To ssh://codeberg.org/racehd/dotfiles.git
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
For this first gitdf push
we do need to specify --set-upstream origin main
, but going forward you’ll be able to push with just gitdf push
.
9. Future changes #
The following are all basic git commands (just using our gitdf
alias). If you need a git refresher then these may be helpful in the future:
gitdf status
to see changed filesgitdf diff
to see the diffs of changed filesgitdf add -u
to add all changes to tracked filesgitdf commit -m "update dotfiles"
to make your commitsgitdf push
to push to your remote.gitdf ls-files
to see a list of all tracked filesgitdf add ~/path/to/dir-or-file
to add new tracked directories or files.gitdf rm --cached ~/path/to/file
if you need to remove a file from tracking but keep it locally.
Conclusion #
That’s it! What I really liked about this was that it only uses git (via the gitdf
alias) so I didn’t need to introduce any complexity stemming from using native symlinks or using a symlink manager. I can just stick with the a tool I already know.
If you’re interested, my dotfiles can be found here.