Introduction
Isaac Newton's famous third law of motion says: "For every action, there's an equal (in size) and opposite (in direction) reaction." A classic case of this is how the thrill of getting a new machine is often matched by the dread of setting up the said machine. With developers, for instance, the list of things to migrate can go on endlessly. From SSH keys to tools (or their equivalents in a different operating system), account details, program files, environment setups, and configurations. These can take a chunk of someone's time. Even worse, some of them (e.g. configurations) are only possible to the extent of the previous machine still being available. I imagine configurations being a nightmare to remember if something crashes unless there is a previous backup available on demand. I'll talk about a nifty way of keeping configurations.
Backstory
I recently switched devices and found myself using the zsh
terminal. It seemed very compatible with the bash
commands that I was more familiar with, but I had a big gripe with it right away: "tab completions were not case-sensitive". This was a big deal because I like the idea of my directory names being capitalized, and having to account for this caused a lot of friction. I decided to switch to bash
in the hopes of that being resolved, but I had no such luck. I figured I would have to play around with my terminal configurations and found some commands that did this. I created the required .zshrc
file, copied the commands, saved them, and tested them. It worked, but I encountered another problem shortly after: My active GitHub branches weren't showing in the terminal.
I use branches a lot, and having the one I'm in show on my terminal is a big relief, and saves me the stress of manually checking every time. This is something I cannot live without, and I got some code from the internet to add to my .zshrc
. Things worked fine, but three questions came to my mind:
- Would I always have to do this while setting up a new machine?
- What if I (or someone else) accidentally deleted the
.zshrc
file? - What if I made a change and wanted to undo it later on?
The third question had the easiest solution: write comments for every new addition. And while having a trash folder felt like an answer to the second question, it wasn't guaranteed that it would always be retrievable.
As for the answer to all the questions? Well, that's what I'm here to talk about.
The Approach
I shared some of my annoyances with the new setup process with a friend of mine and we talked about possible ways of solving these problems. He mentioned a solution he developed for himself after several years of switching machines because he had very precise settings he liked to keep. Hint: it involved Version Control.
Version Control allows you to track and store different states of a file, with an option to store these versions in a remote repository. This answers the 3 questions from earlier, but brings with it, its own question: "How?"
There are two ways of doing this with git:
- Make the location of the
.zshrc
a git repo. - Have the content of the
.zshrc
file in a different location (which will be a git repo), and then point to that location.
The first approach seems good at first glance. However, the usual location for the .zshrc
file is in the home directory, which also has lots of files. To achieve the desired result, you would need a .gitignore
file in place to ignore everything else except the pertinent config file. The drawback with this is that you could somehow end up committing something new (and sensitive) or even damage something in your home directory while setting up. It's a high-risk low-reward process.
The second approach, on the other hand, might seem a bit confusing to do at first, but is safer. The trick to achieving this is knowing how to point files to other files, similar to how variables work in programming languages. This is where Symbolic links (symlinks) come in. A symlink is a file that points to another file. Beneath the hood, it's simply a file that contains a text string file path, in a format that the operating system understands to follow up with. Essentially, we could write the configs in a safe (or even public) location, and have their counterparts in the home directory point to them instead. This way, you could make a change in the new location, and everything would reflect as expected. Best of all, you could commit changes without fear of damaging anything, and replicate the process for as many different machines as you want.
The Process
The first step would be knowing how to create a symlink. For POSIX, the shell command to do this is:
ln -s <target-path> <link-path>
Where the <target-path>
is what you want to point to, and the <link-path>
is the copy that points to it. Let's say you have a file a
in the documents
directory and you want to make it read-only and accessible from the desktop
directory, the command would look like this:
ln -s path/to/documents/a path/to/desktop/a
The path/to/desktop/a
will be created if no such file exists, but the path/to/documents/a
should ideally exist, else there would be nothing to be linked to.
For my symlink, I created my new .zshrc
file in the following path:
Documents/Code/Scripts/dotfiles/.zshrc
Afterward, I initialized the Scripts
folder into a git repo and pushed it to my GitHub remote. Having done this, I edited the .zshrc
and filled it with the copied-over commands from my findings.
By default, shell terminals look for configuration files in the home directory (~/
). My next step was to link one to the target I created in the git repo. To do this, I ran:
ln -s Documents/Code/Scripts/dotfiles/.zshrc ~/.zshrc
I reloaded my terminal afterward, and all my new terminal configurations worked. To be sure changes would reflect, I appended some random characters to my target file and ran cat ~/.zshrc
to see if the characters would reflect, and that worked. Essentially, problem solved.
Takeaways
With this technique, I can persist not only shell configs, but also other text file settings I would like to carry along to other machines, and it would take me only one terminal command to get everything working as intended.
Going even further, I could have this for multiple types of files, and then have a script.sh
that I could run to set up everything automatically. For example, the following:
ln -s path/to/target/.zshrc ~./zshrc
ln -s path/to/target/.zshrc_profile ~./zshrc_profile
ln -s path/to/target/.zshrc_completion ~./zshrc_completion
would make ./script.sh
a convenient script to run, especially on a new machine.
A Few Things to Note
- The target file and link file do not need to have the same name. It just helps for recognition.
- The
.zshrc
file would work similarly to.bashrc
, depending on the shell terminal you're using. The principles still hold. - These methods are not limited to just configurations and will work in pretty much any situation where you need to have something in a location you would rather not turn into a repo, but would like to keep for future usage.
- There are different types of config files. I used the one I was most familiar with.
Credit
Special thanks to my friend Tom for pointers on how to solve the problem.
Finally, thank you for reading.
Cheers. 🥂
Top comments (0)