Using GNU Stow to manage your dotfiles
brandon.invergo.net
brandon.invergo.net
Right. Which is why I use git with a workspace detached from the git directory. The git directory is in ~/dotfiles/.git, with "git config core.worktree ~", and I set an alias "dgit=git --git-dir ~/dotfiles/.git". The advantage of this method is that there are no symlinks to maintain, however initial clone is a bit painful, since you have to move the files and set the config.
It's not perfectly straightforward, but it's as close to it as I could get it.
Thank you for this.
Since there is interest, I'll elaborate on the understated "a bit painful" part of moving the files after an initial clone which leaves the files in the ~/dotfiles dir. The problem is that I didn't find an easy "move and overwrite recursively" command in Linux.
Here's the magic command from my script: git ls-files --cached -z | rsync -av --from0 --remove-source-files --files-from - ${dir} ~/ && find . -type d -empty -delete
To spell it out, it's having git list all the files in the repo (so skipping any unversioned files such as the .git dir itself), using the NUL byte as file termination to avoid any (most?) special character issues in file names, and using rsync to move the files over thanks to --remove-source-files. However rsync doesn't remove empty directories (durr), so have the find do that.
Also, you probably want to set "git config status.showUntrackedFiles no" ;)
$ git clone --bare git@github.com:mbudde/homedir.git .homegit
$ git --git-dir=.homegit --work-tree=~ checkout -f # Overwrite existing files
$ echo '*' >> .homegit/info/exclude
And then I have a simple git wrapper script [1]. I moved from using an alias to a script for some reason I can't remember (and of cause I didn't write it in the commit message -_-).[1] https://github.com/mbudde/homedir/blob/master/usr/bin/hgit
You don't need recursiveness into commands, just use find (maybe with xargs) to generate the list of files to overwrite (or commands to execute).
No wait, I mean find is a really powerful and sophisticated tool, but it takes time and effort to get it to do precisely what you want. It turned out rsync did what I wanted more easily.
I also make an effort to push my config files into .config wherever possible. I really hate the ~ dotfile clutter.
It works really well for me.
git clean -dfx git() {
local toplevel=$(command git rev-parse --show-toplevel 2>/dev/null)
if [[ "${toplevel}" == "${HOME}" ]] && [[ "$1" == "clean" ]]; then
>&2 echo "Do NOT run git clean in this repository."
return
fi
command git "$@"
}I keep my dotfiles in a sub-directory (doesn't matter where) and use the following Rakefile to manage it:
https://github.com/tvon/dotfiles/blob/master/Rakefile
Then it's just `rake status` or `rake link` to symlink everything. I've only been using this for a few years but I haven't run into any problems yet.
https://github.com/marbu/dogit/blob/master/README.md
That said, I'm not sure if it would be direclty usable for anyone other that me in it's current state. It's tailored to my use case and there are few not so well solved use cases.
Few additional advantages of this approach in general:
You can use local branch which is private to the machine and a public branch, Whatewer you commit ends up in the local private branch and if you would like to share it, you need to cherry pick particular commit and rebase (so that you I don't end up sharing private ssh key on github).
You can use any git tool directly on your dotfiles.
One can directly add vim submodules into the dotfiles repo.
1. There could be many "hg-dirs" for a one regular "workdir" (e.g. .hg_foo, .hg_bar, .hg_baz, ...)
2. There'd be a wrapper tool, that on any hg command would autodetect to which hg-dir any given affected file belongs, if already committed earlier (with ability to override the autodetection by hand, including for adding brand new files to the repo).
However, didn't care enough to try implementing this yet. Also, not quite sure how/whether it could work with git, given the existence of staging area.
(full story: http://stackoverflow.com/a/5789827/98528)
Ended up using dotfiles (https://pypi.python.org/pypi/dotfiles). My dotfiles (https://github.com/cjoelrun/dotfiles). I handle system specific stuff in each config: OS checks in zsh, emacs, tmux configs. Allows me to move between OSX and Arch linux.
Not to argue with your experience, but it's hard to see how it could do less—it literally just creates a bunch of symlinks. (It seems to have grown a bunch of options since I last looked at it, but it seems that they can all be ignored if you want simplicity. (I've only ever used `-d`, `-t`, and `-D`.)
The stow directory is assumed to be the value of the "STOW_DIR" environment variable or if unset the current directory, and the target directory is assumed to be the parent of the current directory (so it is typical to execute stow from the directory /usr/local/stow). Each package given on the command line is the name of a package in the stow directory (e.g., perl). By default, they are installed into the target directory (but they can be deleted instead using "-D").
https://github.com/Lambdanaut/dotfiles/blob/master/cp_dotfil...
The only thing that bugs me is that I haven't figured out an elegant way to have some configs include machine specific configs. For example a set of bash aliases that I only use at work but don't want at home.
My best idea is to source all files in, say ~/.bashrc.d and put machine specific configs in there... Haven't tried it out yet.
if [ -f ~/config/bashrc_$(hostname -s) ]; then
source ~/config/bashrc_$(hostname -s)
fi
this gives me machine specific configs. if [ -f ~/config/bashrc_$(uname -s) ]; then
source ~/config/bashrc_$(uname -s)
fiThis separate repo has .private versions of all of my commonly used dotfiles (for example .bashrc.private), and my top-level files will check for the existence and source these, if present.
Both repos will be installed using stow, with the work dotfiles generally just extending the personal dotfiles.
A fair amount of the time when I'm setting up a .foorc, I'll want to add some functions to bashrc, or change a MANPATH or something, so this pattern is useful for that too; you could just have a ~/dotfiles/.bashrc.d/foo.sh and have your .bashrc unconditionally source everything in ~/.bashrc.d/. Then when you use stow to setup foo, its bashrc modifications get installed too.
With this software you also need to remember the magic invocation, a trivial thing in both scenarios, but nevertheless something the author complained about, and you also need to install stow. Previously the author complained about adding dependencies in the form or Python (which made no sense, but still, he complained about dependencies) but now he adds this new dependency (which is more rare than python anyway...).
"GNU Stow is a symlink farm manager which takes distinct packages of software and/or data located in separate directories on the filesystem, and makes them appear to be installed in the same place. This is particularly useful [to] facilitate a more controlled approach to management of configuration files in the user's home directory, especially when coupled with version control systems." [1]
Installing a single binary might be easier than installing a scripting environment on a server that may have existing versioning considerations. (brew install stow | sudo apt-get install stow)
You literally type `stow bash` and you're done.
----
This is particularly useful for keeping track of system-wide and per-user installations of software built from source, but can also facilitate a more controlled approach to management of configuration files in the user's home directory, especially when coupled with version control systems.
(from https://www.gnu.org/software/stow), where "especially …" links to http://lists.gnu.org/archive/html/info-stow/2011-12/msg00000....(There's nothing wrong with re-discovering it, of course!)
I looked at a lot of different things like Stow and homeshick and the like and really didn't like any of the solutions. So I ended up writing my own tool.
I've wanted to write up something for it for awhile now and post it around, but I just haven't yet. Anyway, here's my tool: https://github.com/EvanPurkhiser/dots
The answer is (of course) in the help output:
-d DIR, --dir=DIR Set stow dir to DIR (default is current dir)
-t DIR, --target=DIR Set target to DIR (default is parent of stow dir)The fact is that most people are going to be reusing (and probably be better of) a lot of code from other well tested dotfiles. `fresh` lets you pick and choose what you want directly from other's repos in addition to adding whatever you want and produces a single bundle (or multiple if you need it).
As far as I can tell, its a self contained shell script and does not have any dependencies (if that is a concern). Its definitely worth checking out.
This can also be done recursively: toplevel Makefile for a machine's configuration files calls the sub-makefiles that install apache, asterisk, openvpn configurations etc.
Some of these are work, some are home. Not all files overlap.
Additionally, I have scripts that are only relevant when I have a certain program installed, or a certain source tree available.
I ended up spending a weekend writing my own system that composed a PATH, .bashrc, .bash_profile etc. based on machine configuration. A single git clone isn't quite enough, especially if you don't want to smash everything into one repo (I don't have my home stuff installed at work, for example).
It's a fully contained Makefile that gives my dotfiles repo `make && make install` semantics. `make` initializes whatever needs to initialize locally on that system, and `make install` creates all the necessary symlinks in the $HOME directory.
I also do namespaced dotfile saving (folder by application), but I focused on WGET/CURL-ability (which is unfortunately hard to divine from the current website UI -- the curl command is not obvious, as you have to have content-disposition turned on.
It works well enough, and I have to set it up once.
[1] http://www.asic-linux.com.mx/~izto/checkinstall/docs/README
Then I have a script to automatically sync that files.
Super simple.
git clone ... ~/.my-config
~/.my-config/setup-links
Done.Some servers at work don't have Git installed (and certainly not Stow), so I rsync ~/.my-config from my workstation to my home directory. (There's little need for me to edit my .zshrc on a server.)
RCS is a version control system that is useful for individual files which must remain in a specific location.