What issue do symlinks have that makes them worth listing as a negative? Asking as someone who wrote an installer script to make symlinks a decade ago, and has used it whenever setting up a new computer ever since.
I use them and have no issues, but some editors don’t respect symlinks when making backups when you’ve opened the symlink (Rename existing file, make new file with the old name, and now the symlink is renamed and subsequent updates are unlinked as well). I seem to recall I had to set an emacs var to avoid this behavior.
Flatpaks can be an issue depending on the sandboxing. The application may be restricted from accessing the target. Using a hardlinks works, so it isn't that big of an issue to have a couple hardlinks, but it is still something to keep in mind.
Enabling/disabling systemd services is done via managing symlinks. Unfortunately, when disabling a service in systemd, it follows and remove symlinks which can remove your symlinked service files.
I use this method, it's great. WAY superior to and WAY easier than using symlinks and having to constantly copy/move and symlink things to a git repo.
I use this script (which I modified a bit from the Atlassian one) to bootstrap it on a new $HOME dir (replace "***YOUR_GIT_REPO_ADDRESS***" with your address; also, you need to copy the function ,cfg into your .*rc file for command-line use):
#!/bin/bash
set -euo pipefail # the usual stuff
# TODO: copy the below function into your .*rc file
function ,cfg {
/usr/bin/git --git-dir="$HOME/.cfg/" --work-tree="$HOME" "$@"
}
# TODO: replace '***YOUR_GIT_REPO_ADDRESS***' below
git clone --bare git@***YOUR_GIT_REPO_ADDRESS***/.cfg "$HOME/.cfg"
,cfg config status.showUntrackedFiles no
if ,cfg checkout; then
# worked fine, we're done
echo "Checked out config."
else
# it failed, so assume we need to move some files out of the way
echo "Backing up pre-existing dot files."
mkdir -p "$HOME/.config-backup"
,cfg checkout 2>&1 | grep -e "\s+\." | awk "{'print $1'}" | xargs -I{} mv {} "$HOME/.config-backup"/{}
,cfg checkout || (echo "Still cannot checkout configs; see messages and fix." && exit 1)
echo "Checked out config."
fi
I also have a git alias to commit files that are tracked with a message:
ctm = commit -a -u -m #commit all tracked files with message
So you can do like this to commit changes and push them:
I love the atlassian method for dotfiles. I have been using for a few years now and currently stored on my private gitea server. I also create a branch for each variation, linux, macOS, macOS arm, windows. Basically they’re the same but they all have slightly different things I cannot use universally.
I had different branches for different machines too in the past but it felt very inefficient to me. The configuration was the same for 99% and for the last 1% I had to manually copy stuff back and forth. At the moment, I just try to use tools that allow me to have different configurations for different hostnames or OSs but maybe I should look into one of the fancier dotfile managers…
tl;dr: Create a .gitignore in your ~ with an asterisk on the first line and `!.dotfilename` on subsequent lines; the asterisk means Git will ignore everything, and the ! means Git will un-ignore that specific file or directory. (Be sure to include the .gitignore file itself.) Use Git as normal and push. To deploy on another machine/account, the easiest method is to check out as normal into a temporary directory, then move the .git directory from that temporary directory into your ~ and run `git revert`. Git will see all of the missing dotfiles as changes to be reverted and revert them into existence.
One potential drawback of doing that is that every directory under ~/ that is not itself part of another git repository becomes part of that git repository, which could make some programs act weird, particularly project auto-detectors in IDEs and such.
To avoid that, one can rename the `.git` folder, effectively disabling auto-discovery, and run git with the option `--git-dir=<newname>`.
So the IDE or whatever would basically traverse up the filesystem looking for the first directory with a .git subdirectory? I've never encountered something like that but I suppose it's possible. Do you know of any IDEs specifically that do that?
At any rate, I didn't know renaming the .git directory was possible. Do you have to add the --git-dir option to every subsequent git command? Sounds like a pain but my dotfiles change rarely enough that I guess I could live with it if I had to.
you can also create a .git file with the path for the git-dir folder (git-dir: /path-to-git-folder/) but that is kind of like having the .git folder with the exception that some programs don't seem to support it.
Yeah, the git command traverses up the filesystem until it finds a ./.git/HEAD file or until it reaches /, whatever happens first. The problem comes when a 3rd program uses git to find the root of a project that doesn't exist and instead of failing finds ~/.git and naively decides to index everything under ~/. I don't know of any specific IDEs that do that, although I vaguely remember running into a similar issue in the past, maybe not with an IDE, which is probably why I do it that way.
This is what I do. It's simple enough and doesn't require any external dependencies. Highly recommend this approach.
The only issue I've had with this approach is that new files which I'd like to commit to the repository are ignored by default. One could use the .gitignore negation strategy like OP mentions to first unignore the file and then add it to the repository. I just end up using git add -f foo to force the addition even though foo is ignored.
I used vim years ago, and the list looks like it was written then.
e.g. the package management. vim-plug seems fine, it seems odd to me to mention the others. (Pathogen's readme now mentions that it recommends vim's built-in package management. Vundle and neoplug haven't had a commit in years).
I highly recommend Mathias Bynens's dotfiles under the Inspirations section. Have been using it for many years now and it makes it so much faster for me to work in terminal.
You can .gitignore everything. I used to have my homedir as git repo, but abadoned that because it interfered with other git repos inside my home. Nowadays i have ~/.config as git root, with some symlinks supporting the programs that don't look there on their own.
after trying a few different ways i've settled on chezmoi which they also mention on their utilites page. it supports quite a lot of things that many other solutions miss like templates so that you can use the same repo across different operating systems and being able to run scripts just once, in order and so on.
It's somewhat involved to set up but now across windows/linux/macos my entire setup is literally just a one-liner.
I started using Chezmoi as well a year or so ago. Its the only one I've managed to stick with and not end up with a mess of different setups across machines.
After fiddling around with lot of options, I have finally homed in on a nix / home-manager[1] based setup. There are a few minor annoyances on Mac OS that I haven't sorted out yet (most of them are symlink/permissions related), but so far it's been working great for everything including support for VSCode dev container dotfiles setup[2].
It took me 20 years to have an important realization about version controlled dotfiles: what I wanted to do was manage a set of sensible, personalized, non-standard behaviours for each tool I used, and have a way of distributing those across systems.
What I didn’t need at all, after all, was anything that installed different things on different hosts or classes of hosts.
Luckily, every tool I want to configure has a config file option to include another file, so I have a working copy of my version controlled defaults here:
~/.config/defaults
…and a one time installation script that makes various tool’s config files look like this:
# begin defaults
source ~/.config/defaults/tool.conf
# end defaults
local specific config here
I am quite happy managing the local bits by editing files on local machines. All my configuration stuff splits into either “used everywhere” or “used just on this one host”, so that makes it easier.
As soon as I figured this out I had to pinch myself as to why it had taken so long to come up with this idea.
I do something similar, though I use the "defaults" as the starting point and add overrides in local files which aren't checked in. Something like this:
# .dotfiles/.bashrc
# shared baseline config...
if [ -f ~/.bashrc.local ]; then
. ~/.bashrc.local
fi
I still want the versioning of host-specific stuff though, as well as the backup (by having the repo on GitHub too), and easy reference to other hosts' config that I might want on my current one after all (or setting it up for the first time).
I do this by having ~/.config/tool/blah.conf as you'd expect; but also ~/.config/by-host/$hostname/tool/blah.conf
I agree 'classes of hosts' is way overkill for probably anyone's personal machines though. Only thing close to that I do is with aconfmgr for more system-oriented stuff - but that's all simply on the basis 'if SSD enable TRIM' type 'classes', no manual specification.
A few years ago I spent some time developing my own solution to this, which I published on GitHub. I drew inspiration from the best dotfiles managers at the time, but I found deficiencies in all of them which is why I made my own. Basically: no configuration file, uses symlinks and not copies or templates, and can clean up files after you remove them. The result is dfm [0]. I talk more about the differences from other high-quality projects in the README. If you like DFM, I’d appreciate a star on GitHub so that I can hit 100 to qualify to be listed on the dotfiles.github.io list :)
This is fascinating to me. I have been a unix user since 1994 and I have carried my dotfiles around with me. I almost never edit them. Things like "oh-my-zsh" just look to me like supply chain attacks as a service. What is the benefit of these dotfile schemes?
I certainly hope I did not give the impression that I spent a great deal of time editing these files. My zshrc is three lines, the last of which was added in the 90s.
My pick is yadm - clean way to setup working environment on any host any *nix-like OS and bootstrap it in seconds. password-store (plus yubikey) organically complements it with simple and effective way to access secrets on any host. However I'm using private gitea instance to store repos for both. Just because. :)
Ansible is wonderful. I'd strongly consider it if I wasn't against the idea of having interesting enough dotfiles to version.
What I do with RasPi servers is I make a pull and a push playbook that live in a repo, which let me deploy what I have onto a new server, and pull any changes made in server web GUIs into the repo where I can commit them.
Then of course any other random periodic task also becomes an Ansible task.
If I had a use case for lots of random little scripts on my main machine, I think I'd choose Ansible over bash where practical, you get so many tools premade, and you can run local or remote all the same.
I used to manage my dotfiles with stow (xstow actually) and do some simple links. Now I run an Ansible playbook that creates links or copy depending on the files and also install tools on different OSes.
The big benefit is that it can be organized in a modular way with roles and tags, and sensitives files (like my gpg keys...) can be encrypted with the vault.
I create my .vimrc and .tmux.conf when I set up my machine or when I get annoyed at the defaults.
I find the problem with "set and forget" kind of jobs is that you forget how to do something because someone else already did it for you, so you don't learn anything. It's similar to joining a complicated project where everything has been set up for you and all problems were solved and if you were to join a new project from day 1 you would have to learn everything again. (See setting up CI/CD, the structure of a java project, a gradle file, a maven POM, Dockerfiles, git, Kubernetes manifests and other devops tasks)
You still need to learn how to do things. I want to be fluent in the tools I use, so I would rather do things from memory.
Shameless plug for "yet another" dotfile manager I'm writing.
It doesn't get in the way, and allows you to manage them in quite a flexible way.
Templating, conditional linking, copying, custom code execution... You name it!
Just wanted to mention Stow[1] here, which I use to symlink my dotfiles on a new machine on a per app basis for many years now. My set up script just installs basic compiler toolchain and runs a bunch of stow commands based on Linux/Mac OS. I also adhere to XDG base spec[2] so I can keep my ~/ clean.
Yeah, I don't understand why people like reinventing the wheel, Stow works, Make works, both are available everywhere, you don't need 30 layers of JS to manage dotfiles, I'm not against progress, but at least make something better than what's already around.
Also, the latest version (>= 2.3.0, released June '19) has a new flag specific to managing dotfiles. When passing `--dotfiles`, stow replaces `dot-` at the beginning of filenames with `.` in the symlinks. The online help does not mention it, but the release notes and man pages do.
I really like stow. It handles almost everything for me.
One thing, I didn't split things by apps per se. I split them based on task. In some machines, I do Python work, so I created a 'python' package. In some I do PHP, so I created a 'php' one. I have a 'base' one where it has my .bashrc, .profile, .bash_profile, fish configs and other things.
Everything in the .bashrc. .profile, etc is loaded around conditionals. So, while the files are long, the section only activates if the other package is available. I thought about splitting it onto files, but most editors have a way of collapsing sections, so I decided to keep it on one so it's easy to edit in one place.
weirdly, nobody mentioned vcsh[1] yet. it's a git-based tool that gives all git goodies. I use it and a couple of bash micro scripts to pull/push the latest changes upon logging in/out into shell (again bash, but seeking for POSIX or fish-based version)
In 2015, GitHub sent me a DMCA takedown notice for a repository of dotfiles. When I tried to convince them how ridiculous that is, their only suggestion was to file a DMCA counter notice.
I've used my custom bash script for managing dotfiles for ages and also have tried all other tools, but just couldn't get it right because I work on multiple OS. Then I found https://github.com/rhysd/dotfiles. It is the only tool I know of that can support multiple OS (Linux, mac, windows). I have to work with multiple OS and having a single dotfiles repo has been great, especially its ability to set different config on different OS, well as a unified Unix-like config setting.
64 comments
[ 4.4 ms ] story [ 139 ms ] thread[This]: https://www.atlassian.com/git/tutorials/dotfiles
I use this script (which I modified a bit from the Atlassian one) to bootstrap it on a new $HOME dir (replace "***YOUR_GIT_REPO_ADDRESS***" with your address; also, you need to copy the function ,cfg into your .*rc file for command-line use):
I also have a git alias to commit files that are tracked with a message: So you can do like this to commit changes and push them:Also, pretty sure they even say at the start of the post they got the original alias from a HN commenter.
To avoid that, one can rename the `.git` folder, effectively disabling auto-discovery, and run git with the option `--git-dir=<newname>`.
At any rate, I didn't know renaming the .git directory was possible. Do you have to add the --git-dir option to every subsequent git command? Sounds like a pain but my dotfiles change rarely enough that I guess I could live with it if I had to.
The only issue I've had with this approach is that new files which I'd like to commit to the repository are ignored by default. One could use the .gitignore negation strategy like OP mentions to first unignore the file and then add it to the repository. I just end up using git add -f foo to force the addition even though foo is ignored.
https://statico.link/vim3 https://statico.link/dotfiles
e.g. the package management. vim-plug seems fine, it seems odd to me to mention the others. (Pathogen's readme now mentions that it recommends vim's built-in package management. Vundle and neoplug haven't had a commit in years).
In terms of vim distributions, https://github.com/SpaceVim/SpaceVim probably deserves a mention, as inspired by the popular Spacemacs.
It'd probably be worth mentioning NeoVim.
https://docs.github.com/en/codespaces/customizing-your-codes...
I keep a second copy repo on a thumb drive so I can start my home directory on an offline computer.
It's somewhat involved to set up but now across windows/linux/macos my entire setup is literally just a one-liner.
[1]: https://github.com/nix-community/home-manager [2]: https://code.visualstudio.com/docs/remote/containers#_person...
https://vcs-home.branchable.com/
What I didn’t need at all, after all, was anything that installed different things on different hosts or classes of hosts.
Luckily, every tool I want to configure has a config file option to include another file, so I have a working copy of my version controlled defaults here:
…and a one time installation script that makes various tool’s config files look like this: I am quite happy managing the local bits by editing files on local machines. All my configuration stuff splits into either “used everywhere” or “used just on this one host”, so that makes it easier.As soon as I figured this out I had to pinch myself as to why it had taken so long to come up with this idea.
I do this by having ~/.config/tool/blah.conf as you'd expect; but also ~/.config/by-host/$hostname/tool/blah.conf
I agree 'classes of hosts' is way overkill for probably anyone's personal machines though. Only thing close to that I do is with aconfmgr for more system-oriented stuff - but that's all simply on the basis 'if SSD enable TRIM' type 'classes', no manual specification.
[0] https://github.com/cgamesplay/dfm
Not everyone has the time to create their own custom dotfiles.
Some just want sane and beautiful terminal defaults.
What I do with RasPi servers is I make a pull and a push playbook that live in a repo, which let me deploy what I have onto a new server, and pull any changes made in server web GUIs into the repo where I can commit them.
Then of course any other random periodic task also becomes an Ansible task.
If I had a use case for lots of random little scripts on my main machine, I think I'd choose Ansible over bash where practical, you get so many tools premade, and you can run local or remote all the same.
The big benefit is that it can be organized in a modular way with roles and tags, and sensitives files (like my gpg keys...) can be encrypted with the vault.
I find the problem with "set and forget" kind of jobs is that you forget how to do something because someone else already did it for you, so you don't learn anything. It's similar to joining a complicated project where everything has been set up for you and all problems were solved and if you were to join a new project from day 1 you would have to learn everything again. (See setting up CI/CD, the structure of a java project, a gradle file, a maven POM, Dockerfiles, git, Kubernetes manifests and other devops tasks)
You still need to learn how to do things. I want to be fluent in the tools I use, so I would rather do things from memory.
https://github.com/cquintana92/dotfilers
1: https://www.gnu.org/software/stow/ 2: https://wiki.archlinux.org/title/XDG_Base_Directory
I use it in combination with a make to automate the setup of a new install.
One thing, I didn't split things by apps per se. I split them based on task. In some machines, I do Python work, so I created a 'python' package. In some I do PHP, so I created a 'php' one. I have a 'base' one where it has my .bashrc, .profile, .bash_profile, fish configs and other things.
Everything in the .bashrc. .profile, etc is loaded around conditionals. So, while the files are long, the section only activates if the other package is available. I thought about splitting it onto files, but most editors have a way of collapsing sections, so I decided to keep it on one so it's easy to edit in one place.
[1] https://github.com/RichiH/vcsh
Glad they've come a long way since then.
In the home directory (~) run:
On the second machine: