home
home copied to clipboard
Manage $HOME with GNU Stow and git
typora-copy-images-to: ./images
| ⚠️ I've switched to using chezmoi to manage my dotfiles |
|---|
:house: home
Version control and automatic management of user files in $HOME
Overview
home uses GNU Stow and Git to manage revision controlled files in your home directory (.dotfiles, scripts, etc). Stow is used to automatically maintain all the symbolic links in the user's $HOME directory that resolve to actual revision controlled files in the HOME directory of this repository.
In addition, there are some other goodies included (like my Terminal.app profile for Mac).
Motivation
I wanted to be able to keep track of all the random files that end up in my $HOME directories on the machines I work on. To avoid turning my $HOME directory into a repository itself, I needed to isolate these files in a subdirectory of my $HOME directory (i.e. $HOME/.home), but I didn't want to have to manually manage symbolic links between the two. It turns out that GNU Stow works perfectly for this type of thing, hence the dependency on Stow.
Obviously, you probably don't want to use my personal files "as is" on your own machine, so the idea with this repository is that it can serve as an example workflow that you can emulate or fork for your own purposes.
Setup
-
Install
stow(make sure to use a recent version that supports--adopt) andgit# via Homebrew for Mac brew install stow git # via MacPorts for Mac sudo port install stow git # via apt for Ubuntu Linux sudo apt install stow git curl -
Change to
$HOMEdirectorycd ~ -
Checkout this repository as
$HOME/.homegit clone --recurse-submodules [email protected]:cdwilson/home.git .home -
Change to
.homedirectorycd .home -
Run
setup.shto install the gitpost-commithook. This will also automatically create symlinks between the files in$HOME/.home/HOME/and your$HOMEdirectory. It will prompt for permission to move ("adopt") any real files in$HOME(i.e. existing.bash_profile,.bashrc, etc) into the$HOME/.home/HOME/directory, and replace them with a symlink (see How it works below)../setup.sh -
Determine any changes between the newly adopted files and the previous revisions of those files stored in the repository
git status -
Add any adopted file to keep the version moved from
$HOMEgit add <any changed files go here> -
Revert any adopted file to replace the version moved from
$HOMEwith the version checked into the repositorygit checkout <any changed files go here> -
Commit your changes
git commit -m "<describe your changes here>" -
You're done! The git
post-commithook will automatically update any symlinks in$HOMEto point to the files in$HOME/.home/HOME/
Usage
Now that everything has been set up, keeping the files in $HOME revision controlled is pretty simple:
-
Any existing files already tracked in
$HOME/.home/HOME/can be edited directly or by opening their symlink in$HOMEin your favorite editor. -
Tracking new files in the repository is as simple as copying them from
$HOMEinto$HOME/.home/HOME/, and committing them into the repository. The gitpost-commithook will automatically convert the file in$HOMEinto a symlink.
How it works
There is no magic going on here—the whole thing boils down to two commands. GNU Stow is used to automatically create symlinks in the user's $HOME directory that point to the revision controlled files in $HOME/.home/HOME/.
Stow is run in two passes:
-
First, it "stows"
$HOME/.home/HOME/*into the user's$HOMEdirectory by creating symbolic links to all of the files in$HOME/.home/HOME/.stow --adopt HOMEIMPORTANT: this first pass is run with the Stow
--adoptoption. If Stow finds a file in the user's$HOMEdirectory with the same filename as a file in$HOME/.home/HOME/, it will move (adopt) the file from the user's$HOMEdirectory into$HOME/.home/HOME/and replace it with a symbolic link in$HOME. It will overwrite any existing files in$HOME/.home/HOME/without asking! While this is normally the behavior we want, if you're not careful, it has the potential to overwrite any unstaged changes in the repository. If you don't want this behavior simply remove--adoptfrom the command in the hook file. -
Second, Stow is re-run with the
-Roption. This causes Stow to "restow" all the files inHOMEensuring any stale links are pruned.stow --restow HOME
These commands must be run in this sequence and can not be combined (i.e. stow --adopt --restow HOME) to avoid errors when $HOME and $HOME/.home/HOME/ have conflicting files.
Git post-commit hook
There is a Git post-commit installed by setup.sh to run Stow automatically after committing changes to the repository. This makes sure that the symbolic links in $HOME are kept up to date and any new files are actually moved into the repository (as opposed to accidentally leaving a copy in $HOME and a separate copy in the repository). Under the hood, the post-commit hook just runs the two stow commands described above.
update.sh
If for some reason you need to update the symlinks in $HOME without actually committing anything to the repository, running update.sh runs the same stow commands as the git post-commit hook, prompting whether or not to bypass adopting files from $HOME. Any arguments passed to update.sh are passed to the internal stow commands (e.g. to display stow action details, run ./update.sh --verbose)
:computer: System Setup
The instructions below detail the additional system setup required to use the dotfiles in this repo on macOS and Ubuntu Linux.
:apple: macOS Setup
Homebrew Setup
Homebrew is a package manager for macOS that can be used to install packages that aren't included by Apple.
Install Homebrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
rbenv Setup
rbenv is a version manager that lets you easily install and switch between multiple versions of Ruby.
-
Install dependencies for building Ruby:
brew install openssl readline -
Install
rbenvusing the Basic GitHub Checkout instructions (I'm not using rbenv-installer because I don't want rbenv installed via Homebrew on macOS):git clone https://github.com/rbenv/rbenv.git ~/.rbenv -
Optionally, try to compile dynamic bash extension to speed up rbenv. Don't worry if it fails; rbenv will still work normally:
cd ~/.rbenv && src/configure && make -C src -
Install ruby-build plugin that lets you easily install Ruby versions:
mkdir -p "$(rbenv root)"/plugins git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build -
Verify the state of your rbenv installation:
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-doctor | bash
pyenv Setup
pyenv is a rbenv-style version manager that lets you easily install and switch between multiple versions of Python.
-
Install dependencies for building Python:
brew install openssl readline sqlite3 xz zlib -
Install
pyenvusing pyenv-installer:curl https://pyenv.run | bash -
Optionally, try to compile a dynamic Bash extension to speed up Pyenv. Don't worry if it fails; Pyenv will still work normally:
cd ~/.pyenv && src/configure && make -C src -
Verify the state of your pyenv installation:
pyenv doctor
direnv Setup
direnv lets you easily load and unload environment variables depending on the current directory.
brew install direnv
pipx Setup
pipx lets you easily install and run Python applications in isolated environments.
brew install pipx
pre-commit Setup
pre-commit is a framework for managing git pre-commit hooks.
pipx install pre-commit
# automatically enabling pre-commit on repositories
# https://pre-commit.com/#automatically-enabling-pre-commit-on-repositories
pre-commit init-templatedir ~/.git-template
Login Shell Setup
On macOS, if you're using a custom shell installed via Homebrew or MacPorts, remember to configure the login shell in the system preferences.
-
Right click on your user account and select "Advanced Options...":

-
Change the Login shell to the full path of your shell:

-
Add your login shell to
/etc/shells:# List of acceptable shells for chpass(1). # Ftpd will not allow users to connect who are not using # one of these shells. /bin/bash /bin/csh /bin/dash /bin/ksh /bin/sh /bin/tcsh /bin/zsh /opt/homebrew/bin/bash <-- Add your shell here
Terminal.app Setup

-
To use the prompt shown in the photo above, install https://starship.rs/:
# via Homebrew for Mac brew install starship -
To get started configuring starship, add your changes to
~/.config/starship.toml -
To use the Hasklig font shown in the photo above, make sure to install the patched "Hasklug" version from Nerd Fonts (Starship uses many of the icons in Nerd Fonts version):
# via Homebrew for Mac brew tap homebrew/cask-fonts brew install --cask font-hasklug-nerd-font -
Make sure to configure the macOS Terminal.app preferences so that shells open with the default login shell:

-
To use the Terminal.app profile shown in the photo above, just double click the terminal/cdwilson.terminal file in Finder.
:penguin: Ubuntu Linux Setup
apt Setup
Update package information:
sudo apt update
rbenv Setup
rbenv is a version manager that lets you easily install and switch between multiple versions of Ruby.
-
Install dependencies for building Ruby:
# Depending on your version of Ubuntu, libgdbm6 won't be available. # In that case, try an earlier version such as libgdbm5. sudo apt install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev -
Install
rbenvusing the Basic GitHub Checkout instructions (I'm not using rbenv-installer because I don't want rbenv installed via Homebrew on macOS):git clone https://github.com/rbenv/rbenv.git ~/.rbenv -
Optionally, try to compile dynamic bash extension to speed up rbenv. Don't worry if it fails; rbenv will still work normally:
cd ~/.rbenv && src/configure && make -C src -
Install ruby-build plugin that lets you easily install Ruby versions:
mkdir -p "$(rbenv root)"/plugins git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build -
Verify the state of your rbenv installation:
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-doctor | bash
pyenv Setup
pyenv is a rbenv-style version manager that lets you easily install and switch between multiple versions of Python.
-
Install dependencies for building Python:
sudo apt install make build-essential libssl-dev zlib1g-dev \ libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \ libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev -
Install
pyenvusing pyenv-installer:curl https://pyenv.run | bash -
Optionally, try to compile a dynamic Bash extension to speed up Pyenv. Don't worry if it fails; Pyenv will still work normally:
cd ~/.pyenv && src/configure && make -C src -
Verify the state of your pyenv installation:
pyenv doctor
direnv Setup
direnv lets you easily load and unload environment variables depending on the current directory.
sudo apt install direnv
pipx Setup
pipx lets you easily install and run Python applications in isolated environments.
sudo apt install pipx
pre-commit Setup
pre-commit is a framework for managing git pre-commit hooks.
pipx install pre-commit
# automatically enabling pre-commit on repositories
# https://pre-commit.com/#automatically-enabling-pre-commit-on-repositories
pre-commit init-templatedir ~/.git-template
GNOME Terminal Setup

-
To use the prompt shown in the photo above, install https://starship.rs/:
sh -c "$(curl -fsSL https://starship.rs/install.sh)" -
To get started configuring starship, add your changes to
~/.config/starship.toml -
To use the Hasklig font shown in the photo above, make sure to install the patched "Hasklug" version from Nerd Fonts (Starship uses many of the icons in Nerd Fonts version):
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/Hasklig.zip mkdir -p ~/.local/share/fonts unzip Hasklig.zip -d ~.local/share/fonts/ cd ~/.local/share/fonts/ rm *Windows* cd ~ fc-cache -fv
-
To use the GNOME Terminal profile colors shown in the photo above, install https://github.com/aarowill/base16-gnome-terminal:
git clone https://github.com/aaron-williamson/base16-gnome-terminal.git ~/.config/base16-gnome-terminal .config/base16-gnome-terminal/color-scripts/base16-tomorrow-night.sh
Development
Please feel free to submit pull requests and file bugs on the issue tracker.
License
The MIT License (MIT)
Copyright (c) 2013 Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.