Symlinking

Hugo Locurcio edited this page Feb 8, 2018 · 21 revisions

Contents

Linking table

homeshick does not blindly symlink everything, instead it uses a carefully crafted linking strategy to achieve a high level of flexibility without resorting to configuration files.

The table below shows how homeshick decides what to do in different situations.

$HOME/castle directory not directory
nonexistent mkdir link
symlink to castle rm! && mkdir identical
file/symlinked file rm? && mkdir rm? && link
directory identical rm? && link
directory (symlink) identical rm? && link

Explanation

homeshick traverses through all resources (files, folders and symlinks), that reside under the home/ folder of a castle in a depth-first manner. Symlinks in the castle are not followed. Conversely: if $HOME contains a directory symlink that matches a normal directory in the castle it will be followed.
The table is consulted for each resource that is encountered. Files or directories that are not tracked by git are ignored and not traversed.

The columns directory and not directory represent the resource in the castle. The rows represent what resource is found at the corresponding location in the actual $HOME directory.

In the castle, directory is a simple directory (and not a symlink to a directory), while not directory is everything that is not the former (so: files, symlinked files, and symlinked directories).

The resources that can be encounter in the $HOME directory are categorized as follows:

  • nonexistent means that the corresponding resource in the $HOME folder does not exist.
  • symlink to castle represents a symlink to the current resource
  • file/symlinked file is a symlink to anything but the current resource
  • directory is a regular directory
  • directory (symlink) is a symlinked directory (but not to the castle)

The actions that can be taken always refer to the $HOME directory. A && means that the second action is only executed if the first one was executed as well.

  • identical: Do not do anything, resources are identical
  • mkdir: Create the directory
  • link: Create a symlink to the resource in the castle
  • rm!: Delete without prompting (this is only done for legacy directory symlinks)
  • rm?: Prompt whether the user wants to overwrite the resource
    (--skip answers "no" to this, while --force does the opposite. --batch selects the default, which is "no")

Combining directories

As you can see in the linking table, every folder that is encountered in the castle is replicated in $HOME. This allows you to combine multiple castles into one directory structure.

Say you have two vim plugins in different castles:

  • Castle 1: home/.vim/bundle/vim-colors-solarized
  • Castle 2: home/.vim/bundle/vim-git

When homeshick symlinks castle 1, it will create the directory structure home/.vim/bundle/vim-colors-solarized (and symlink whatever files are in that folder). Upon encountering castle 2, homeshick sees the folders home/.vim/bundle/ and considers them identical. It then creates the directory vim-git inside home/.vim/bundle/ and goes on with symlinking the vim-git plugin files.

Repos with no home directory

What do you do if you encounter a really cool repository that goes well with your existing setup, but it has no home/ folder and needs to be linked to a very specific place in your $HOME folder?

Let's say you want to add vundle to your Vim configuration. The documentation says it needs to be installed to ~/.vim/bundle/vundle, but you are not very interested in forking the repository solely for the purpose of changing the directory layout so that all files are placed four directories deeper in home/.vim/bundle/vundle/.

homeshick can solve this problem in two ways:

  1. Add vundle as a submodule to your dotfiles. This is definitely the quick and easy way.

     homeshick cd dotfiles
     git submodule add https://github.com/gmarik/vundle.git home/.vim/bundle/vundle
    
  2. Clone vundle with homeshick and symlink to the repo from the appropriate folder in your dotfiles:

     homeshick clone gmarik/vundle
     cd ~/.homesick/repos/dotfiles/home
     mkdir .vim/bundle
     cd .vim/bundle
     ln -s ../../../../vundle vundle # symlink to the location of the cloned vundle repository
     homeshick link dotfiles
    

We use a relative path for the symlink in case we log in with different username on other machines.
When running the link command, homeshick will create a symlink at ~/.vim/bundle/vundle pointing at the symlink we just created. This means there will be a symlinked directory at ~/.vim/bundle/vundle, which contains the files of the cloned vundle repository
Note: You can see how homeshick decides what to do when encountering different symlink situations by looking at the linking table.

The advantage of the second option is that you have more finegrained control over your repositories and can manage each of them individually (e.g. you want to refresh your own dotfiles every week, but you don't want to wait for all the submodules in your repository to refresh as well).

The downside of not using submodules is that you will need to add the additional repositories with homeshick clone on every machine. However, you can use the automatic deployment script to avoid having to do this manually.

Shallow symlinking

Since homeshick traverses into the directories of your home/ folder, it may encounter quite a lot of files that need symlinking. In many scenarios this is desirable (e.g. when combining directories). Some directories however may have a huge configuration setup with a lot of files. Traversing and symlinking these files can take a little while and you may have no interest in having fine-grained control over these directories.

You can use homeshick's linking strategy to your advantage in such cases and have homeshick create a simple directory symlink in your $HOME folder.

The not directory column in the linking table also covers directory symlinks. When encountering a directory symlink, homeshick will create a symlink in $HOME, which points to the symlink in your Castle. Therefore, all we need to do is to convert the directory in question into a symlink.

As an example, consider this directory structure:

dotfiles/
└─home/
  └─.emacs/
    ├─snippets/
    └─themes/

If we move .emacs outside the home/ directory, we can still keep it in the repository and create a symlink in its place like so:

dotfiles/
├─emacs/
│ └─snippets/
│ └─themes/
└─home/
  └─.emacs ➞ ../emacs

When running homeshick link dotfiles, homeshick will now not traverse into the .emacs folder in your castle. Instead it will create a symlink named .emacs in your $HOME, which links to $HOME/.homesick/repos/dotfiles/home/.emacs.

Files outside your home directory

Although homeshick is specifically made for files in your $HOME directory, you can get homeshick to venture outside those borders. Since homeshick follows directory symlinks in $HOME, you can create one that points to a different location on your machine.

Please note that managing files outside $HOME is beyond homeshick's intended scope. It is possible, but more often than not it is not the right tool for the job. The strategy outlined below is more of a "hack" than a real pattern.

Consider a scenario where you might have a webapp you want to add to /var/www/tools. This is the structure of that app:

tools/
├─public/
│ ├─assets/
│ │ ├─js/
│ │ │ └─jquery.js
│ │ └─css/
│ │   └─bootstrap.css
│ └─index.htm
└─app/
  └─controllers/
    └─router.py

You will need to place a symlink somewhere inside your $HOME that points at the intended location for your webapp. We don't want to clutter $HOME, so we place the symlink in the .homesick directory.

$HOME/
└─.homesick/
  ├─repos/
  │ └─...
  └─links/
    └─tools ➞ /var/www/tools

The structure of the castle now needs to mimick the location of that symlink:

webapp/
└─home/
  └─.homesick/
    └─links/
      └─tools/
        ├─public/
        │ ├─assets/
        │ │ └─...
        │ └─index.htm
        └─app/
          └─...

When running homeshick link webapp, homeshick will now traverse into the directory structure of your castle, discover that the resource .homesick/links/tools already exists, choose the identical action (see the linking table), follow the symlink you created, and go on with creating the public/ and app/ folders in /var/www/tools.

This method can of course be combined with the shallow symlinking method. In such a case it would be better to point the symlink in the .homesick/links directory at /var/www (to avoid having to create symlinks for both public/ and app/) and use the following castle directory structure

webapp/
├─tools/
│ ├─public/
│ │ ├─assets/
│ │ │ └─...
│ │ └─index.htm
│ └─app/
│   └─...
└─home/
  └─.homesick/
    └─links/
      └─var-www/
        └─tools ➞ ../../../../tools

If you want the links/ folder to be under version control, you should place it adjacent to a castles home/ folder like this

webapp/
├─tools/
│ └─...
├─home/
│ └─.homesick/
│   └─repos/
│     └─webapp/
│       └─links/
│         └─var-www/
│           └─tools ➞ ../../../../../../tools
└─links/
  └─var-www/ ➞ /var/www

This scenario is of course only useful if the destination of your webapp is the same on all the machines homeshick is deployed to. Deployment specific links (like /srv instead of /var/www on some installations) are only possible if you keep the symlinks outside of version control.