This repo is a clone of https://github.com/andsens/homesHick that has been simplified to support only zsh and bash and has been renamed to homesick again and extended to support bootstrapping scripts as part of the first install.
By the power of git, homesick enables you to synchronize dotfiles across computers.
However bare bones these machines are, provided that at least Bash 3 and Git 1.5 are available you can use homesick. homesick can handle multiple dotfile repositories. This means that you can install larger frameworks like oh-my-zsh or a multitude of emacs or vim plugins alongside your own customizations without clutter.
homesick is installed to your own home directory and does not require root privileges to be installed.
git clone https://github.com/michaelrommel/homesick.git $HOME/.homesick/repos/homesick
To invoke homesick, source the homesick.sh
script from your rc-script:
# from sh and its derivates (bash, dash, ksh, zsh etc.)
printf '\nsource "$HOME/.homesick/repos/homesick/homesick.sh"' >> $HOME/.bashrc
homesick comes with its own tab completion scripts. There are scripts for bash and zsh. They allow you to tab complete all available commands and supply you with possible castle names. [[tab-completion.gif]]
To get tab completion working in bash, simply source
completions/homesick-completion.bash
somewhere in your .bashrc
.
printf '\nsource "$HOME/.homesick/repos/homesick/completions/homesick-completion.bash"' >> $HOME/.bashrc
ZSH autoloads its completion scripts.
In order for ZSH to know that there exists a homesick completion script
you have to add the homesick/completions
directory to the ZSH tab completion
lookup path.
printf '\nfpath=($HOME/.homesick/repos/homesick/completions $fpath)' >> $HOME/.zshrc
NOTE: you may need to ensure that this line comes before the compinit
line. If you are using oh-my-zsh
, the homesick fpath
modification may need to come before sourcing oh-my-zsh
. For example:
source "$HOME/.homesick/repos/homesick/homesick.sh"
fpath=($HOME/.homesick/repos/homesick/completions $fpath)
source $ZSH/oh-my-zsh.sh
homesick is used via subcommands, so simply typing homesick
will yield a helpful message
that tersely explains all of the things you can do with it.
Most subcommands accept castlenames as arguments. You can also run a subcommand on all castles by not specifying any arguments.
homesick link [castlename ...]
This command symlinks all files that reside in the home
folders of your
castles into your home folder. Note that only files tracked by git will be
linked, untracked files are ignored.
You will be prompted if there are any files or folders that would be overwritten.
If the castle does not contain a home folder it will simply be ignored. To further understand how the linking process works you can consult the linking table.
homesick clone URI..
The clone
command clones a new git repository to .homesick/repos
.
The clone command accepts a github shorthand url, so if you want to clone
oh-my-zsh you can type homesick clone robbyrussell/oh-my-zsh
.
homesick pull [castlename ...]
The pull
command runs a git pull
on your castle and any of its submodules.
If there are any new dotfiles to be linked, homesick will prompt you whether you want to link them.
homesick check [castlename ...]
check
determines the state of a castle.
- There may be updates on the remote, meaning it is behind.
- Your castle can also contain unpushed commits, meaning it is ahead.
- When everything is in sync,
check
will report that your castle is up to date.
homesick list
You can see your installed castles by running the list
command.
homesick track castlename .file [.folder ...]
If you have a new dotfile or folder that you want to put in one of your castles, you can ask
homesick to do the moving, symlinking and git-adding for you.
To track your .bashrc
and .zshrc
in your dotfiles
castle
run homesick track dotfiles .bashrc .zshrc
,
the files are automatically added to the git index.
Alternatively you may want to track an entire folder like your vim configuration,
when given a path to a folder homesick will traverse the entire structure
and track all the files contained within:
homesick track dotfiles .vimrc .vim
Any files that would be git-ignored by the castle will be left untouched.
homesick generate castlename [castlename2 ...]
generate
creates a new castle.
All you need to do now is call track
to fill it with your dotfiles.
homesick refresh
Run this command to check if any of your repositories have not been updated the last week. This goes very well with your rc scripts (check out the tutorial for more about this).
homesick cd castlename
After you have used the track
command, you will want to commit the changes and push them.
Instead of cd
'ing your way into the repository simply type homesick cd dotfiles
;
homesick will place you right inside the home/
directory of your dotfiles
castle.
From there you can run whatever git commands you like on your castle.
Tip: cd -
places you in your previous directory, so if you did not change directories
after running homesick cd dotfiles
you can simply type cd -
to get back to where you left off.
homesick has commandline switches that can automate some of its actions or modify its output. Their applicability is not enforced despite the fact that some only work on specific commands.
The --quiet
flag (shorthand: -q
) suppresses all status output except when input is required.
When running homesick link
, the --skip
flag (shorthand: -s
) causes the command
to automatically not overwrite files in your $HOME
that conflict with files in your castles.
--force
(shorthand: -f
) will cause homesick to overwrite any conflicting files in your $HOME
when
symlinking with homesick link
and any conflicting files in your castle when running homesick track
.
When integrating homesick with other scripts you can use --batch
(shorthand: -b
)
to avoid any interactive prompts. homesick will instead pick the defaults.
You can combine this switch with the other switches to e.g. overwrite all conflicting files in $HOME
.
In the Installation, you added a homesick
alias to your .bashrc
file.
Let's create your first castle to hold this file. You use the generate
command to do that:
homesick generate dotfiles
This creates an empty castle, which you can now populate.
Put the .bashrc
file into your dotfiles
castle with:
homesick track dotfiles .bashrc
Be aware that your file has now been moved into the castle and a symlink to it has been created in its stead. So any modifications to your dotfiles
repo will directly affect your dotfiles.
Let's now enter the castle, commit the changes, add your github remote and push to it.
homesick cd dotfiles
git commit -m "Initial commit, add .bashrc"
git remote add origin [email protected]:username/dotfiles.git
git push -u origin master
cd -
Note: The .homesick/
folder is not a typo, it is named as such because of compatibility with
homesick, the ruby tool that inspired homesick
To get your custom .bashrc
file onto other machines you install homesick and
clone
your castle with:
$HOME/.homesick/repos/homesick/bin/homesick clone username/dotfiles
homesick will ask you immediately whether you want to symlink the newly cloned castle.
If you agree to that and also agree to it overwriting the existing .bashrc
you can run
source $HOME/.bashrc
to get your homesick
alias running.
You can run check
to see whether your castles are up to date or need pushing/pulling.
This is a task that is easy to forget, which is why homesick has the refresh
subcommand.
It examines your castles to see when they were pulled the last time and prompts you to pull
any castles that have not been pulled over the last week.
You can put this into your .bashrc
file to run the check everytime you start up the shell:
printf '\nhomesick --quiet refresh' >> $HOME/.bashrc
.
(The --quiet
flag makes sure your terminal is not spammed with status info on every startup)
If you prefer to update your dotfiles every other day, simply run homesick refresh 2
instead.
To make changes to one of your castles you simply use git.
For example, if you want to update your dotfiles
castle
on a machine where you have a nice tmux configuration:
homesick track dotfiles .tmux.conf
homesick cd dotfiles
git commit -m "Added awesome tmux configuration"
git push origin master
cd -
There are times when some programs attempt to auto generate / save config files and in so doing will overwrite the symbolic link that homesick creates. This means files that were tracked become untracked and attempts to re-link result in either overwriting your changes or staying untracked. The most common culprit are programs that offer an internal method for making and saving config changes.
I'll show you how to diagnose such a situation and then how to fix it. For this tutorial I will use the tin news reader as the sample program. Tin uses the following directory structure for its config:
~/.tin
├── attributes
├── filter
├── news.example.com
│ ├── newsgroups
│ └── serverrc
├── posted
└── tinrc
The actual configuration variables are stored in tinrc
and the rest are caches and other runtime files. So we need homesick to track ~/.tin/tinrc
. If you run a homesick track myCastle ~/.tin/tinrc
then it will create the following:
~/.homesick/repos/myCastle/home
└── .tin
└── tinrc
~/.tin
├── attributes
├── filter
├── news.example.com
│ ├── newsgroups
│ └── serverrc
├── posted
└── tinrc@ -> ../.homesick/repos/myCastle/home/.tin/tinrc
Normally this is what we want but the next time we quit tin it will delete ~/.tin/tinrc
and recreate it losing the symlink. To verify this is the case do an ls -la ~/.tin
and you'll see the symlink missing. You will also experience homesick complaining next time you do a link command.
To fix this we have to use the shallow symlink trick. However we need to prevent homesick from tracking all the caches and temp files.
- Remove the bad tracking file/directory.
- Move the original config directory (temp files and all) into a custom directory in the myCastle repo.
- Add a
.gitignore
file to ignore everything but the wanted files. - Add a symlink from home to the custom directory in the same repo.
- Save to git.
- Run
homesick link
to finish the setup.
~ $ homesick cd myCastle
~/.homesick/repos/myCastle $ git rm -r home/.tin # Step 1
~/.homesick/repos/myCastle $ mkdir dotdirs
~/.homesick/repos/myCastle $ mv ~/.tin dotdirs/ # Step 2
~/.homesick/repos/myCastle $ nano dotdirs/.tin/.gitignore # Step 3
# .gitignore contents:
* # Ignore everything...
!.gitignore # Except .gitignore
!tinrc # Except tinrc
~/.homesick/repos/myCastle $ ln -s dotdirs/.tin home/.tin # Step 4
~/.homesick/repos/myCastle $ git add dotdirs/.tin home/.tin
~/.homesick/repos/myCastle $ git commit # Step 5
~/.homesick/repos/myCastle $ homesick link # Step 6
homesick 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 homesick 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 |
homesick 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 identicalmkdir
: Create the directorylink
: Create a symlink to the resource in the castlerm!
: 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")
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 homesick 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, homesick 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.
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/
.
homesick can solve this problem in two ways:
-
Add vundle as a submodule to your dotfiles. This is definitely the quick and easy way.
homesick cd dotfiles git submodule add https://github.com/gmarik/vundle.git home/.vim/bundle/Vundle.vim
-
Clone vundle with homesick and symlink to the repo from the appropriate folder in your dotfiles:
homesick 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 homesick 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, homesick 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 homesick 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 homesick clone
on every machine.
However, you can use the automatic deployment script to avoid having
to do this manually.
Since homesick 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 homesick's linking strategy to your advantage in such cases and have homesick 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,
homesick 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 homesick link dotfiles
, homesick 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
.