Hacker News new | past | comments | ask | show | jobs | submit login
Using GNU Stow to manage your dotfiles (invergo.net)
149 points by dpeck on Sept 5, 2013 | hide | past | favorite | 69 comments



but many/most dotfiles reside at the top-level of your home directory, where it wouldn't be a good idea to initialize a VCS repository

I totally disagree. I love having much of my home directory in git. I have a .gitiignore for the stuff I don't want in there, but the rest. Ahhhhh, so nice to be able to clone somewhere else and use.


I take a similar approach. I have a dotfiles git repo in ~/dotfiles/ and have a Makefile which creates symlinks in my home directory. For example, ~/.bashrc is a symlink to ~/dotfiles/bashrc. That way, I can have a whitelist (whatever's in the Makefile) instead of a blacklist (like a .gitignore).



My main problem with this is it leads to accidentally executing git commands on your dotfiles repo.

Like, say you thought ~/src/xulrunner was a git repo, but it was actually managed with hg or maybe was extracted from a tarball. Whereas normally executing some git command would complain that this is not a repo, if your ~ is a git repo then git will happily execute whatever you told it to on your dotfiles repo.


For git repositories that are not touched very often, you can move the .git directory to something that isn't picket up automatically. Use it like this:

    mv .git dot_git
    git --git-dir="dot_git" add ...
Thats also a nice trick if you need a git repos in a git repos, e.g. for integration test for libraries interacting with git.


Be careful, this is how devs expose private ssh keys.


I push it to a local git repo that has the same perms as my account and .ssh directory. But, I agree. You need to be careful.


I also keep my whole homedir in git, and it's great. My approach isn't to .gitignore, because I want to ignore almost everything -- I just add and commit things when I want to track them. One of my many git aliases[1] is 'git sn' for showing status without untracked files; this helps when dealing with my homedir:

  status --short --branch --untracked=no
[1] https://github.com/cespare/dotfiles/blob/master/.gitconfig#L...


I have my homedir in git too and also ignore just about everything. I ended up using gitignore as a whitelist instead of a blacklist, e.g.:

    /*
    !/.gitignore
    !/.gitmodules
    !/.gitconfig
    !/.zshrc
    !/.zshenv
    !/.zsh
    !/.tmux.conf
    !/.vimrc
    !/.vim


I have a close setup. It just needs a little trick to add subdirectories :

  /*
  !/.emacs
  !/.emacs.d/
  /.emacs.d/*
  !/.emacs.d/themes/
  !/.emacs.d/functions/
You can check my .gitignore here [0].

[0] https://raw.github.com/dgellow/home/master/.gitignore


I actually prefer to .gitignore things explicitly, because then any new dotfiles show up in "git status" until I explicitly disposition them.


I wondered about this, but I pose this question to you: what happens if you have a merge problem with, say, a libreoffice doc or something. How do you handle that? I use hg, but I imagine my plight would be similar. TIA.


I don't keep anything but text files in my dotfile repo, so I haven't had to deal with this.


I'm very proud of my server's top-level[1] for the past fifteen years:

  $ ls -A
  .gitconfig@  .hushlogin  .local/  .notes/  .ssh/  www@  .zshrc@
All other dotfiles are configured to be picked up from somewhere inside .local either using environment variables or aliases.

[1] On the other hand, the laptop -- where I run X and gnome -- is a disaster.


If I may, a small plug for my solution to this problem:

https://bitbucket.org/davidn/dotstuff

I settled on copying rather than symlinking, which has the benefit that the source files can be passed through a simple preprocessor to do some customization for different systems. Also you can get a diff between your current files and the state that you just synced.


The primary complaint of the article is tools that manage the files in git and symlink things into place have lots of dependencies.

ghar, a project I wrote, is a single standalone python file: https://github.com/philips/ghar


Python is still a pretty large dependency in itself.


Not really. I've yet to see a distro in the past 10 years without it.


Arch Linux, for one.


Also Mac, Windows.


OS X has python preinstalled. Not bleeding edge though.


I use a similar method, but structured around a single Makefile: https://github.com/staticshock/seashell

This lets me perform any necessary initialization (e.g. cloning submodules) via `make` and installing the actual symlinks into $HOME via `make install`.


I use the homegit alias trick to manage my top level home directory stuff in a Git repo that doesn't reside at ~/: http://chneukirchen.org/blog/archive/2013/01/a-grab-bag-of-g...


I would really like to have a dotfile strategy. But I have another goal beyond synchronization: I would like to be able to organize my dotfiles in a modular fashion.

For example, I would like to construct my bashrc using something like run-parts. This system would take bash/★.bashrc, this-machine/local-env.bashrc, and special-app/proxy-env.bashrc and compile them into a single ~/.bashrc. I would also like to be able to build a ~/.nanorc that only includes features supported by the installed version of nano. Some dotfiles include both public and private data -- I'd like to publish my git aliases, but not my GitHub authentication token.

So I basically want a dotfile manager that's a combination of PHP, run-parts, and stow: dotfiles constructed through in-line Perl code (in my nanorc or ssh_config) with assembly of multiple pieces (in my bashrc or gitconfig) and installed into the right place in my home directory.

As far as I can tell there's nothing out there that can do this. (Although I also insist it be written in something classic that I can find anywhere, like Python 2.4 or Perl or something like that. So I didn't look at any of the many dotfile managers written in Ruby or Node.)

    <? @NANO_VER = `nano -V` =~ m/version (\d+)\.(\d+)/ ?>
    set const
    set cut
    <? if ( $NANO_VER[0] > 2 || $NANO_VER[1] >= 1 ) { # Assume nano 1.x isn't still an issue
    ?>
    bind M-f nextword main
    bind M-b prevword main
    <? }
    print "include \"$_\"\n" foreach </usr/share/nano/*.nanorc>;
    # Maybe I'll have to write this myself....
    ?>


Hi,

My dotstuff script can do most of that, with a little bit of work. With the current features, you'd put all your bashrc content into a single ~/dotstuff/bashrc file, marked off with preprocessor directives that turned on and off certain sections depending on the content of the "environment" (a simple key-value file).

Adding the ability to query the version of installed software would be easy to add. Adding the ability to concatenate multiple files would be fairly easy too. Although I think an "include" directive might make more sense than run-parts style, since many dotfiles have hierarchical structure.

It's written in 2.4-compatible Python and I'm happy to accept contributions.

https://bitbucket.org/davidn/dotstuff


I hae something like this in my ~/.bashrc file:

    for i in ~/.bash.d/* ~/.local.bash.d/; do
         test -e $i && source $i
    done
Which functionally does what you want, loading everything from a directory. Though it doesn't do version-testing, or actual concatenation.


also see GitMinutes #13: Richard Hartmann on Managing Your Homedir with vcsh:

http://episodes.gitminutes.com/2013/06/gitminutes-13-richard...

https://github.com/RichiH/vcsh


I've taken a different approach and store all my dotfiles on Dropbox and then symlinked them the local system. I get synchronisation between machine almost instantaneously and can move between around half a dozen boxes Including Linux, OSX and cygwin with little problem. Dropbox provide some version control which is useful If I really break something and I have a single script which can bootstrap a new machine. A few things don't liked to be symlinked (namely ssh keys) but other than that this had worked great for several years.


I wasn't clear to me from the article alone how GNU Stow decides where to put the files. Looking it up in the manfile stow uses the parent directory of the "stow directory" by default.

You can change this behaviour using the --target and --dir options, where setting --dir will set the target dir to the parent of what you set for --dir.

I don't think it's possible to configure a target in the package itself, as far as I can see from reading the man page.


Does anyone else use RCS [1] for this?

[1] https://www.gnu.org/software/rcs/


I know of a guy who used to use rcs back in 2001 and he probably is today. He is one of those old-school UNIX guys who know literally everything about a UNIX system.


WAT. Are you trolling us, or are you that uninformed about VCS history? Giving you the benefit of the doubt, just learn and use git[1]. Yes, there are things about the UI that suck. But the underlying machinery and power it provides is unparalleled.

RCS, in short, is that horrible feet-killing pair of boots that made you think you hated <insert sport here>, when in fact it was just frustration with sub-par equipment.

[1] If you need to learn git, check out Scott Chacon's excellent Pro Git, available for free online at: http://www.git-scm.com/book


Ha! Fair enough. No, I didn't intend to troll. I've found RCS useful for ad hoc versioning of configuration files on systems without git or other version control systems. Wondering whether anyone still finds it useful. I reckon not!


RCS is a lot less complexity and remembering commands than a full-on VCS for a few stray config files.


I've recently installed RCS on a Windows host for versioning my .emacs file, and I rely on Emacs VC to drive the tool.

On Solaris hosts, including locked-down "production", I use SCCS to version my dot-files because it's available by default. For development I use SVN (old too by today's standards).

I don't advocate using these old tools over modern alternatives; however I find their simplicity in the above cases to be beneficial.


I used to use RCS, but these days I use mercurial. Pretty much any DVCS is going to be a "better RCS" than RCS (bzr, mercurial, git, fossil, monotone...). Even if you never need to pull/push.


rcs doesn't have .gitignore files and accidentally running commands on your top-level repo, so I could see why one would consider it. You don't really need atomic commits for this use case, so rcs is barely worse than the alternatives.


I can't say that I've ever had a problem with accidentally running commands in my top-level repo despite many years of running it under git and other VCS'es.

That said, using tools mentioned in this thread (esp. vcsh[1] and mr[2]) it's possible to have one's cake and eat it too w.r.t. using git for homedir version control without the worries of accidentally running VCS commands on your homedir. They also allow some real benefits, like the ability to use and deploy subsets of your rcfiles. For example, you could easily create profiles like: "server-side minimal core", "main personal system", "work box with employer-specific stuff".

[1] https://github.com/RichiH/vcsh

[2] http://myrepos.branchable.com/


I've made my own similar solution. I have all mine on Github, so I git clone the repo, and run my bootstrap script, which initializes the submodules, Vundle's all my vim plugins, and symlinks all the files.

https://github.com/Aaronneyer/dotfiles


isn't that sort of info kinda sensitive https://github.com/Aaronneyer/dotfiles/blob/master/sshconfig ? I can see hosts and usernames...


Ya, that's actually something I've been meaning to pull out, although I don't think it really matters all that much if people know what hosts/usernames I have.


Oooh... submodules. Yours is better than mine. Maybe it's time I ditched my own half-baked setup...


I use Vcsh. It allows for more than one repository. It's a Shell script, uses Git and it doesn't symlink.


> I've come across various programs which aim to manage this for you by keeping all the files in a subdirectory and then installing or linking them into their appropriate places. None of those programs ever really appealed to me. They would require a ton of dependencies (like Ruby and a ton of libraries for it) or they would require me to remember how to use them, which is difficult when really for such a task you rarely use the program.

Ever heard of shell scripts? This is what I use: https://code.google.com/p/aram-dotfiles/source/browse/make.u...


I keep all my dotfiles in a directory called "~/dotfiles" and symlink just my .bashrc. everything else gets reconfigured via aliases.

eg.

     alias vim='vim -u /home/derf/dotfiles/vim/vimrc'
     alias tmux='tmux -f /home/derf/dotfiles/tmux/tmux.conf'
     
http://github.com/fredsmith/dotfiles


Isn't that a lot of work? And I doubt it will work for everything. Some programs don't give you an option to get configuration from non-standard location.


The article says the author doesn't want to depend on something like Ruby. Ruby (or Python) seem a lot more likely to be on a base install than GNU Stow, though.


Or just write a shell script. This is a pretty trivial task in Bash, let alone zsh


I gotta ask - what's with the tiny columns on this website? I'm reading on an iPhone and there's more white space than area that displays actual text!


Back to the old "dotfiles with git Best Practices".

My favorite is just to alias it all to a different name.

    alias dg='git --git-dir=/Users/dragonfax/.dotfiles --work-tree=/Users/dragonfax'
now regular git doesn't overlap with my dotfiles git, and I never have to worry about mis-executing the wrong git


I use the thoughtbot/dotfiles setup. https://github.com/thoughtbot/dotfiles

Pretty good customizations by default with an elegant approach for adding local settings. The install.sh script automatically symlinks appropriate files as well as downloads vim bundles.


Can Stow do hardlinks? The reason I ask is that IIRC systemd shits a brick if your service files are symlinks.

Conversely, I have a script that does this:

https://github.com/radiosilence/dotfiles/blob/master/relink

All I have to do is maintain a MANIFEST file.


uAs I ubderstood, systemd only uses symlinks. Any time you enable a service, systemd executes ln -s ..systemd.. ...service ...


Yeah but the services systemd links to cannot be symlinks.


On Fedora 18 it most certainly can. I did it yesterday with some service files under /usr/lib/systemd/system for some service files I created


Neat! Perhaps it's been updated then.


I did not know about GNU Stow. I want to try to use this for my dotfiles along with a simple Makefile to automate calling stow on all of the directories. This will also be a nice way to manage the differences between my home configuration and work configuration. One make rule per environment. Thanks for sharing.


You might also enjoy xstow, a reimplementation in c++ -- in case you might want to use stow without having perl:

http://xstow.sourceforge.net/

edit: I use xstow for managing ~/opt -- putting stuff like ~/opt/xstow/golang-git, with symlinks to  ~/opt/{bin,lib,man} -- making it easy to add the relevant folders to PATH, MANPATH etc in .bashrc.


I don't see dfm mentioned, curious if anyone has used that? I haven't, but may be interested in trying it out. https://github.com/justone/dfm


I use a shell script [0]. It also includes some gconf configuration etc.

[0] https://github.com/qznc/dot/blob/master/install.sh


Pretty neat. I may try it. I just wish there were a way for the dot files themselves to not be actual dot files while in the dofiles directory, but then become dot files when symlinked by stow.


If you add more features, like store the settings in a sqlite, universal config r/w interface, and live config change notification to apps, you'll come up with a system like Registry.


The difference is that dotfiles are simple, text based, and human readable.

When you re-install a Windows system, do you overwrite the registery with a backup you had? I doubt it.


just for completeness sake and because no one mentioned it yet: https://github.com/technicalpickles/homesick

yes, you need ruby for it but I dont really see that as a problem. Its a bit confusing at first (could be just me, but I still dont get why you have basically 2 repos, one managed by homesick and the one you checked out yourself) but it works fairly well. Takes care of symlinking, updating and whatever you want.


some files i symlink, some i copy, but i prefer to use the language's import/include/source/load command if the dot file is actually a program rather than a conf file. that way, i can add machine-specific configuration. to anyone who makes tools like gnu stow or those mentioned in the comments:

consider adding support to modify existing dot files as well as replace them.


ln -s ~/dotfiles/.bashrc ~/.bashrc


I keep the dotfiles in my dotfiles directory without the dots, as there's no need for them to be hidden.


I'm using a bash script [0] too, it is bit complicated because of I've have three different workplaces and some conf files that have account information (such as email passwords) should be hidden.

[0] https://github.com/seletskiy/dotfiles/blob/master/dotfiles.s...


I "solved" that problem by having two repositories: dotfiles & dotfiles-private.

The public repository has .bashrc, etc in it. The private repository has ~/.mutt/work-muttrc, etc in it.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: