Introduction and Dotfile Storage Method
When I started heavily using Linux in college and started customizing and creating configuration files, I quickly needed a way to store, backup, and set up my various configuration dotfiles. A logical place to store configuration files is in git. You can see modifications to the dotfiles, easily commit new changes, and even make multiple branches if you need different versions of the dotfiles. I do not remember the exact tutorial I followed, but a form of the method I used seems to be floating around on the internet in multiple blog posts. Managing Your Dotfiles With Git from 2020 seems to use the same method as me, and credits Using Git and Github to Manage Your Dotfiles from 2012 for the method. Given the age of the older post and how closely his example on GitHub matches the first commit of my dotfiles repo, I would guess that this is the template I followed.
I started with a simple base and then built up the dotfiles and configuration over many years. My dotfiles repository currently has 623 commits and was started in 2016. The original structure from the example can still be found in my setup. Therefore, instead of reproducing the same content as Using Git and Github to Manage Your Dotfiles, I recommend you pause reading this blog and read about this method of storing dotfiles from the original blog post.
My Public Dotfiles
Now that you have read about the dotfile setup, you know how the dotfile directory is stored in git. A setup script is used to create symlinks from the configuration files in the home directory to point back to configuration files in the dotfiles folder. I have multiple branches in my dotfiles repo. I have one main branch which I use on my personal Linux machines; then I also have a branch, that I used on the University machines at school, a branch that I use on machines at work, and a branch that I use on WSL machines. Each of these types of machines has some deviations from the main branch, but I periodically compare the contents back to the main branch to ensure that all the good features are present in all and that the places they diverge still apply. Since some of these configuration files are specific to work or to my own machines, I decided to make a public dotfiles repo, where I will copy the content of my dotfiles which is not sensitive and which is generic across the branches and that I think will be of general use and interest. This public dotfiles repository can be found at https://github.com/Geekdude/dotfiles. I hope you find my dotfiles interesting and helpful as you create your own dotfiles repository.
As you create your own dotfiles repository, I recommend that you start from a simple base, go through example configurations, and understand what the configuration lines are doing. Then you can copy in the lines which change the configuration in a way that you like. Don’t just use public configuration examples without taking the time to understand what the configuration is doing. You probably do not want parts of my configuration, but some of it you will likely find helpful. I enjoy learning about the features of other people’s configurations and potentially incorporating some of the configurations into my own config. My configurations started simple and grew over time as I wanted my system to behave differently and searched for a solution or saw a cool configuration on the internet or from colleagues. Sometimes I ran into a specific problem and found the solution in a configuration change, then incorporated the change into my configuration files.
Over time I also changed the programs I use, so I have some older configuration files for these programs, even though I do not use these programs as much. I also changed which shell I use primarily. I started with Bash, then switched to Zsh for the added features. Now I am currently using Fish. I still keep my Bash and Zsh config in case I go back to these shells, but I primarily use Fish now. One downside to Fish, is that it is not compatible with Bash, so I still find myself going back to Bash or Zsh frequently to run Bash scripts. I liked the general look I had for Bash, so I kept the same general look as I configured Zsh and Fish.
Now that you know the general dotfile setup and have access to my public dotfiles, I will talk in more detail about my dotfiles, and as I do, you are free to look through the dotfile repository as well. I tried to be clear in the comments of the dotfiles with what the configuration lines do, and in this blog post, I will give broader information about the features and reasoning behind the setup.
The README file goes over how to set up the dotfile configuration on a new system. From the steps, you can see how the relatively simple process is made more difficult when I need to use ssh keys to pull from a private repository.
This file creates the symlinks for the dotfiles back to the corresponding file in the dotfiles directory. Over time, this script was also extended to pull and update external submodules, create other additional symlinks, and link symlinks to folders. This script is run to set up all the dotfiles. I have more submodules listed in .gitmodules than I actually use. That is why I explicitly list the submodules I want in the script. This is primarily done since it is much easier to add a new submodule than to remove it, and I might want to use it again.
In my private dotfiles repo, I keep passcode protected and GPG encrypted ssh keys. The makesymlinks.sh script will call the command to decrypt the keys as part of the setup. In practice, I have to add the keys before I can clone the dotfiles directory, and I have to manually set up the symlink to .ssh. That’s is the problem with using a private dotfiles repo; the keys need to be in place before you can clone the repo. I also use a .ssh/config file to configure connections to remote machines. Since the information in .ssh is sensitive, I have not included any of it in the public dotfiles repo.
The git configuration is stored in
gitconfig, global git ignores are stored in
gitignore_global, and my git hook templates are stored in
I removed the
user.email config items from the
gitconfig, but those can be easily added back when git prompts you to set them.
I use Beyond Compare 4 as my main diff tool.
I also added other tools with aliases.
gvimmerge use Vim and GVim respectively to perform the diff or merge.
I also found difftastic, which is a better terminal diff than
diff and is aliased to
My favorite alias, which I use all the time, is
tree, which prints out a nice tree view of the git log.
Other aliases are
sup which recursively updates all submodules, and
sfor is an alias to run a for loop over all submodules.
I also have
hash which prints out the current git hash, and
pwd, which prints out the top-level directory of the git repo.
pwd might be a misleading name, but I can remember it easily.
Finally, I also have
d, which prints the status or diff of the current directory respectively.
My template directory has the necessary hooks to set up ctags.
These ctags are then used by my Vim configuration to jump to tags in my file quickly.
Each time a git operation occurs the, ctags are regenerated, which keeps them up-to-date.
gitignore_global ignores the generated ctag files.
Ctags are further discussed in my Ctags blog post
I like to use fast-forward-only so that I have to explicitly choose to make a merge. That way, it is easier for me to pick to merge or to rebase instead of defaulting to merge.
I like to use the Fish shell. I think it is a well-designed shell with an easy-to-understand configuration structure. Out of the box, it has many of the features by default that are a pain to set up in Zsh. I was also able to keep the same basic theme as my previous shell configurations. Some of my favorite features are:
- Automatic prediction and auto-completion of commands based on previously used commands.
- Command completion based on man pages. (I also have a function,
fish_add_completions, to generate a manpage from
--helpoutput, and reload the man completions so that programs I write can have easy command completions)
- Powerful prompt with git status, exit code, time, and the duration of the last command.
- Easy configuration with a conf.d for config files and functions for custom functions.
config.fish has inline comments describing what everything does.
One thing to point out is that I call
xcape commands to replace capslock with control/escape and both shifts with capslock.
conf.d I have configuration files broken down by the name of what they are configuring.
Any file in this directory will be included in the configuration of Fish.
ssh_agent.fish to make sure I always have an ssh-agent running.
Inside functions, I have all my custom functions.
In Fish, the prompts are just a shell function, so my prompt is defined in
funcedsave.fish combines the built-in funced and funcsave functions.
git-add-submodule.fish adds an existing clone nested repository as a submodule of the parent.
git-reload-hooks-all.fish reload hooks based on the git template for either the current repo or recursively into all the submodules.
My Tmux config is set up to work with nested local and remote Tmux sessions.
tmux.conf specifies the local Tmux configuration and
tmux/tmux.remote.conf adjusts the status bar on the remote Tmux session.
<F12> is used to toggle the disable/enable of the outer Tmux session to make the inner Tmux session easier to work with.
My Tmux config also has mouse support, plugins to show resource monitoring, and more Vim-like bindings.
I spent a while figuring out the full-color range for both Tmux and Fish, and the color settings at the top of the file seem to work. Since I use MobaXTerm, which has some nifty features that only work if Bash is your default shell, I use Tmux to set my default shell to Fish. That way, Fish is started anytime I start my Tmux session.
bin, I also have some helpful Tmux scrips.
tmuxes opens or connects to a new single Tmux session with the same view as a previous connection.
This command only opens a common Tmux session, and multiple sessions will follow the same cursor.
tmuxs also opens the same Tmux session, but this time it creates a new view into it so that the cursor does not follow the previous window.
tmux_disallow changes the permissions of the Tmux socket to allow other users on the same machine to connect to the same Tmux session.
I use Vim and Visual Studio Code as my main text editors.
Vim is still the editor that I am the most proficient with and also the one that I have customized the most.
With the various plugins I have, my Vim behaves more like a modern tool like Visual Studio Code compared to the vanilla Vi editor.
I have and still use Vim as a code editor, word processor, note taker, and documentation writer, and various plugins make it more suited for these tasks.
To learn about the plugins I use see My Favorite Vim Plugins.
These plugins are installed using pathogen, and the plugins are added as submodules to my dotfiles repo.
makesymlinks.sh is used to initiate and update the submodules.
Some of the plugins I have modified and the submodules point to my forks of the original repos.
Just like with my other config files, I have inline comments to explain my configuration. However, I will mention here some specific features I have. See my Ctags blog post for more information on how Ctags is set up. I like to use three spaces by default, even though that seems like a chaotic-neutral choice on the alignment chart. I have a check which disables many features when the text file is over 10MB. With this setting, Vim can open humongous text files that break most other text editors. I have a function that automatically strips trailing whitespace from files on save except when editing markdown files since trailing whitespace has meaning in markdown. To help see trailing whitespace in those file formats, I add a · in place of the trailing space. I created my own color scheme, which is a modification of the default color scheme. I also set up proper 256 and GUI colors so that the color scheme looks good.
I added two useful functions for when I edit text. To make line diffing of text easier, and since LaTeX and Vim treat newlines without a blank as part of the same paragraph, I write one sentence per line in the source document.
" Search for paragraphs that do no end in a newline. nnoremap <leader>l<space> /[.!?]'\?'\?\zs\s\+\ze\(\w\\|\\\)<cr> " Join lines and add a newline at the ends of sentences. vnoremap <leader>l<space> :s/[.!?]'\?'\?\zs\s\+\ze\(\w\\|\\\)/\r/g<cr>
<leader>l<space> is pressed, the sentence ending periods that are not followed by a newline are searched for and highlighted.
When a visual selection is made, and
<leader>l<space> is pressed, the newlines are added to the selected region.
This makes it easy to reformat text note in the one sentence per line format to this format.
J to join all the lines in a paragraph, then press
:DuplicateTabpane is useful for quickly duplicating your current window layout to a new tab. Then
:TabooRename can be used to rename the tab to a new name.
Here are my keybindings that I use when working with vim-zettel:
Zettel Vim Keybindings <leader>zs ZettelSearchMap <leader>zy ZettelYankNameMap <leader>z ZettelNewSelectedMap <leader>nv Notational Vim :ZettelNew <title>
Basic Apt Installs
This script is used to update and upgrade apt packages, then install a common set of apt packages that I expect every system to have. Between the dotfiles repo, this script, and Synology drive, I can go from a fresh Ubuntu install to a configure system very quickly. This is useful since I redo my computers frequently or set up a new virtual machine.
create-thumbnails is used to reduce the resolution of images to create thumbnail versions.
I use this for my website to reduce the size of the smaller images in a gallery.
pdf-shrink reduces the size of PDFs by calling other commands with the correct arguments.
Reorder is a program I wrote and is discussed in the Reorder blog post
Here I store all the fonts I want to use across my machines.
makesymlinks.sh will create a symlink to the local font location and run
fc-cache to cache the fonts so that they are properly installed as locally installed fonts.
I did not include the fonts in the public repo since I didn’t want to check the licensing on each font.
I set up Inkscape with the extensions I use. I created my own addition to circuit symbols to make a black box symbol.
No system is complete without Zork, so as a bonus, I install Zork by default.