symlink-free dotfiles using the
$HOME
as thegit work-tree
technique
Used daily on macOS & BluefinDX/Bazzite/BazziteDX Fedora Linux systems.
- Quickstart
- Longstart
- Tips
- Tools
- Git Conditional Configuration
- Creating your own git work-tree dotfiles repository
- Licence
-
Clone the bare repository (this stores the Git history without checking out files yet):
git clone --bare https://github.com/jthegedus/dotfiles.git "$HOME/.dotfiles.git"
-
Define a temporary alias for interacting with the dotfiles repo:
alias dotfiles='git --git-dir="$HOME/.dotfiles.git/" --work-tree="$HOME"'
-
Attempt to checkout the files:
dotfiles checkout
-
Handle Conflicts: If the
checkout
command fails due to existing files you can perform one of two actions:- a) Backup existing files and replace (recommended):
Repeat the above for any other reported conflicting folders/files.
# eg: backup the existing .config directory and git checkout mv ~/.config ~/.config.bak dotfiles checkout
- b) Overwrite existing files with force checkout:
dotfiles checkout -f
- a) Backup existing files and replace (recommended):
-
Configure the repository to ignore other untracked files in
$HOME
:dotfiles config --local status.showUntrackedFiles no
This creates the following files in your $HOME
directory:
# in $HOME
.config/** <-- configuration files
.dotfiles/ <-- template files for this repo
.dotfiles.git/ <-- .git dir for this repo
.gitconfig <-- core git config file
.gitconfig.proj <-- created from .dotfiles/*.template
README.md <-- this repo README
.zshenv <-- tell ZSH to look at ~/.config/zsh/*
- Copy the template
cp ~/.dotfiles/.gitconfig.project.template ~/.gitconfig.proj
- Fill out the
name
andemail
properties in~/.gitconfig.proj
- If you do NOT wish to use SSH with Git then:
- remove the
gpg
,commit
,tag
, anduser
sections from the file
- remove the
- If you wish to use SSH with Git then:
- fill out the
signingkey
properties in~/.gitconfig.proj
- Create and Store an SSH key and use for Authentication/Commit-Signing in GitHub
- I recommend using BitWarden or an equivalent as an SSH Agent for secure storage and ease of use.
- fill out the
Install tools in .config/brewfile/Brewfile*
using Homebrew and Homebrew File:
brew install rcmdnk/file/brew-file
brew file install
- Fork this repository
- Modify the contents using the in-browser GitHub edit & commit capabilities
- Remove the SSH git configuration if you do not wish to use SSH
- Clone the repository to your machine from your own Fork
- Follow the above Manual post-clone steps instructions
- I recommend using Fish over ZSH. ZSH configuration is here for convenience but I am considering dropping maintenance of it.
- Fork and modify this repository instead of cloning it directly.
- Files that may require modification are:
.zshenv
.config/brewfile/Brewfile*
- Files that may require modification are:
- Instead of using branching codepaths in your scripts for system-specific configurations you could use git branches to manage a copy for each system, with common/shared configuration in the master branch.
- You would have to merge the common/shared configuration into the other branches on change.
I use Development Containers in most projects to manage project-specific dependencies. This keeps my OS installation relatively clean. Since devcontainers can mount your home directory, this repository contains some configuration files for tooling I commonly use within devcontainers that may not appear on my machine and therefore Brewfile.
The list of tools I use as a base on each system/OS are:
- fish: shell
- git: source control
- carapace: terminal completions
- macchina: terminal util
- starship: terminal prompt
- ghostty: terminal
- visual studio code: code editor (use vscode native settings sync)
- zed: code editor
Use the following tools to change the shell on most Unix systems:
chsh -s $(which <DESIRED_SHELL>) $USER
sudo usermod --shell $(which <DESIRED_SHELL) $USER
(whenchsh
is not available)
I use the .gitconfig
conditional includeIf
directive to manage per-project Git settings in a separate configuration file to the ones committed to this repository (see the template in .dotfiles/.gitconfig.project.template
directory for an example).
This is useful to manage different usernames, emails or authentication settings per project.
The includeIf
directive allows for the following conditionals:
gitdir:<pattern>
: Matches if the Git directory path matches the pattern, useful for applying settings to projects in specific locations (e.g.,~/work/
).onbranch:<branch-name-pattern>
: Matches if the current branch name matches the pattern, useful for branch-specific workflows or settings.hasconfig:remote.<name>.url:<pattern>
: Matches if a remote's URL matches the pattern, useful for loading different user configs for work vs. personal projects or different Git platforms.
See the documentation for full explanations - https://git-scm.com/docs/git-config#_conditional_includes
As an example, this configuration applies the same config for remotes using GitHub via SSH & HTTPS, with another config for a singular organisation remote on BitBucket:
# git settings - GitHub SSH & HTTPS
[includeIf "hasconfig:remote.*.url:https://github.com/**"]
path = ~/.gitconfig.proj
[includeIf "hasconfig:remote.*.url:[email protected]:**"]
path = ~/.gitconfig.proj
# git settings - project specific SSH
# NOTE: ensure this does not overlap with any other includeIf conditions
[includeIf "hasconfig:remote.*.url:[email protected]:<organisation_name>/**"]
path = ~/<org_name>/.gitconfig.proj_<project_name>
This includes the specified configuration file if the repository you are running git
commands against ( where the .git
directory is) matches the provided pattern.
Here is a quick guide to creating your own bare repository for dotfiles using the $HOME
as the git work-tree
technique:
- create a bare repository
- set a temporary alias to use the bare repository (you should add this alias to your shell config as well for future use)
- set the repo to ignore all untracked files (using our alias "dotfiles")
- set the remote origin to your GitHub (or other) repository
git init --bare $HOME/.dotfiles.git
alias dotfiles='git --git-dir=$HOME/.dotfiles.git/ --work-tree=$HOME'
dotfiles config --local status.showUntrackedFiles no
dotfiles remote add origin [email protected]:<username>/<repo>.git
Now add any of your existing .config
files, track with git
& push:
cd $HOME
dotfiles add .config/starship.toml
dotfiles status
dotfiles commit -m "feat: capture starship configuration"
dotfiles push
For more examples and inspiration see the HackerNews post where I learnt about this technique.
NB: The dotfiles
alias is a temporary alias for the current shell session. I include it in my Fish & ZSH configuration files in this repository as a permanent way to quickly interact with my dotfiles repository.
NB: Even though all files are untracked thanks to the status.showUntrackedFiles no
setting, I still recommend using .gitignore
files when the configuration you wish to include is in a directory with other files you do not wish to include. This is purely a precautionary measure to avoid accidents. Though allow-listing files in .gitignore
is a recommended practice.
Zero-Clause BSD
=============
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.