<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Wesley Schwengle on Medium]]></title>
        <description><![CDATA[Stories by Wesley Schwengle on Medium]]></description>
        <link>https://medium.com/@waterkip?source=rss-cf5aae5efd60------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*SmWqoe8Ay4vfaBN-NlFwOg.jpeg</url>
            <title>Stories by Wesley Schwengle on Medium</title>
            <link>https://medium.com/@waterkip?source=rss-cf5aae5efd60------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 19 May 2026 14:59:53 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@waterkip/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Managing my dotfiles with GNU Stow]]></title>
            <link>https://medium.com/@waterkip/managing-my-dotfiles-with-gnu-stow-262d2540a866?source=rss-cf5aae5efd60------2</link>
            <guid isPermaLink="false">https://medium.com/p/262d2540a866</guid>
            <category><![CDATA[dotfiles]]></category>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[stow]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[unix]]></category>
            <dc:creator><![CDATA[Wesley Schwengle]]></dc:creator>
            <pubDate>Mon, 08 Apr 2019 16:27:33 GMT</pubDate>
            <atom:updated>2019-08-16T19:57:04.174Z</atom:updated>
            <cc:license>https://creativecommons.org/licenses/by-sa/4.0/</cc:license>
            <content:encoded><![CDATA[<p>Dotfiles, every Linux/Unix user has them. I’ve had mine on FreeBSD, OSX/MacOS, Solaris, RedHat, Ubuntu and Debian machines and probably on other machines as well. They date back to at least October 4th 2009 when I made the switch from CVS to Git, but they are older than that. I just tarballed them before that and untarred. Or just simple scp/rsync across servers. Because I’ve moved my git scripts to a separate repository in order to freely share them (see <a href="https://medium.com/p/7f2c145c9d6d">My git workflow explained</a>) I got an itch to also open source my dotfiles. Or Dotty as I like to call this project.</p><p>Since the inception of my dotfile directory I haven’t really looked at the way I manage or maintain them. I just updated the custom build installation script as it did its job. Then there was the day my colleague asked me to share my git scripts and aliases. I moved all the scripts and aliases away to <a href="https://gitlab.com/waterkip/bum">Bum</a>. My dotfiles felt abandoned, they needed some loving too.</p><p>So I went looking for a tool to manage my dotfiles. There are plenty of tools that do the job. I looked at them and they weren’t my cup of joe. And then there was GNU Stow. The best thing about Stow is, it isn’t really written for dotfile management. It tries to solve another problem, installing software in place to make sure once you install it you can remove it safely from your machine and not have to worry about “forgotten” files. Managing dotfiles is just a nice perk to have.</p><h3>How does Stow work?</h3><p>Well, Stow is a glorified symlink farm manager. It manages symlinks from A to B. It is a tool made for system administrators that need to maintain software on machine and may need to add/remove software packages. In order to do so you need to be able to remove software and all of the files associated with them. You could do an installation directly into.. let’s say /usr/local, but then you would need to make sure you know about every little file and where it is installed: painful! Some projects allow you to do make uninstallbut not all do this. And you would need to keep that Makefile in order to remove a given package. You could also go for the option to install the package in /usr/local/&lt;packagename&gt;and then educate your users to add that path in<br>their PATH, also sub optimal. This is where Stow shines. You enter /usr/local and then do stow &lt;packagename&gt;. Now every thing gets symlinked in /usr/local, &lt;packagename/bin goes to /usr/local/bin, &lt;packagename&gt;/libto /usr/local/lib, you get the point.<br>Removing a package would start with stow -D &lt;packagename&gt; and Stow<br>will happily remove all the symlinks and you can rm -rf /usr/local/&lt;packagename&gt;to remove the whole lot. System administration made easy. You may think that this problem may be a thing of the past with all the package managers Linux distro’s currently offer (apt, yum, apk, ports). But there are still use cases for Stow, you don’t always install from your package manager. For example, if you build zsh from their git repo and want to try it out on your box, you can install it into /usr/local/zshand you then stow zsh to /usr/local. Easy does it.</p><h3>Dotfiles</h3><p>Back to the dotfiles. My dotfiles are in my .shell directory in my home dir and I did a thing similar to stow, but only without the de-installation process and not being open source. I reorganized my .shell dir to be Stow compatible. I now have two shell directories. One is called .dotty-private, which isn’t for you and the other is called .dotty, which I share with the world.</p><h4><strong>Dotty</strong></h4><p>In this repo I have everything I need to setup a home dir to my liking. It includes application configuration (MPlayer, Vim, Git, etc), (bin-)scripts, my zsh configuration, Debian related things, etc. I just hit setup-env.shand it rolls out all the things I need. It basicaly does stow stow zsh vim script apps debian perl git. I’ve also included a cpanfilefor all of the Perl scripts that are found in the bin-dir and a Debian package list for all of the tools that I want to use. It feels much more organized than my home brew solution.</p><p>So how does this work? Well, this is my file tree:</p><pre>$ tree -a -d -L 2 -I .git<br>.<br>├── apps<br>│   ├── .config<br>│   ├── .conky<br>│   ├── .local<br>│   ├── .mplayer<br>│   ├── .ncftp<br>│   ├── .siege<br>│   ├── .texmf-var<br>│   └── .wireshark<br>├── awesome<br>│   └── .config<br>├── debian<br>├── git<br>│   └── .config<br>├── i3<br>│   └── .config<br>├── perl<br>│   └── .dzil<br>├── scripts<br>│   └── bin<br>├── stow<br>├── vim<br>│   └── .vim<br>└── zsh<br>    └── .zsh</pre><p>Each important thing that I use has its own directory which then has a similar layout as they would have in my homedir. Now if I stow zsh, it will put all the files found in zsh into my homedir:</p><pre>zsh<br>├── .Xdefaults<br>├── .aliasrc<br>├── .functions<br>├── .profile<br>├── .shellrc<br>├── .zlogin<br>├── .zlogout<br>├── .zprofile<br>├── .zsh<br>│   ├── .zcompdump<br>│   ├── autoload<br>│   ├── autoloadrc<br>│   ├── cache<br>│   ├── completion<br>│   ├── endzshrc<br>│   ├── keybindrc<br>│   ├── minimal-zshrc<br>│   ├── nameddirrc<br>│   ├── optionsrc<br>│   ├── prompt<br>│   ├── promptrc<br>├── .zshenv<br>└── .zshrc</pre><p>It won’t create a symlink for everything in the .zsh directory, stow will just symlink .zsh to .dotty/zsh/.zsh. If however I would have another stow dir that provides files into.zsh it will then symlink all the files in .zsh and add new symlinks to the other stow directory. In Stow lingo the former is called a <a href="https://www.gnu.org/software/stow/manual/stow.html#Installing-Packages">foldable </a>directory and the latter is unfolded.</p><h4>The secret Dotty</h4><p>In my secret dotty are all the things I don’t want to share with the world. It contains my .ssh and .gnupgand some other things I just don’t want you to know about. Because OpenSSH doesn’t allow for symlinks from within the .sshdirectory I had to move over all the files to this secret dotty repo. I do however don’t want my private and public keys to be in my git repo, so I added them to my .gitignorefile. The only things that I commit are my authorized_key and my ssh config file. For GnuPG most files are ignored as well, except for the config files.</p><p>Now I go into .dotty-privateand hit stow gnupg sshand my setup<br>just roles out automatically.</p><h4>Stowing container</h4><p>I can test all the things in a Docker container if I want to, as both repositories have a Dockerfile and a docker-compose file. They use the base image of my <a href="https://gitlab.com/waterkip/stowing">Stowing</a> project. This project is GPL licensed because Stow is GPLv2 and I publish the image, but if you don’t publish the image you can fork my dotfiles and redistribute them under the MIT license as I don’t ship stow within the repository. All the legal stuff aside. You can try it out by just running</p><pre>docker run -ti \<br>  -v/path/to/allyourdotfiles:/root/stow \<br>  registry.gitlab.com/waterkip/stowing:latest zsh</pre><p>Or you can create a Dockerfile with the following contents in your dotfiles repository:</p><pre>FROM registry.gitlab.com/waterkip/stowing:latest</pre><pre>COPY . .</pre><p>Now you can build and run it:</p><pre>docker build -t dotfiles .<br>docker run -ti dotfiles zsh # or bash if you are a bash user</pre><p>You get to see your dotfile directory in /root/stow and can now see what/when happens when you have a go at stow.</p><h4>Pros</h4><p>There is very little configuration on your part, just enter a directory and Stow will do its job. A very strong plus in my book. Truth be told, Stow manages itself within my repository, I have a .stow-global-ignore file that deals with the files I want stow to ignore (mostly Docker and Git files). I’m considering opening an pull request to add these to the default set of ignored files so I don’t have to manage this. <br>A personal pro, it is written in Perl. As a Perl developer this is a big plus, others may not share this viewpoint.<br>As said earlier, Stow isn’t just for dotfile management, it is used to solve another problem. Having Stow means you can maintain your custom build software in the same way you can maintain your dotfiles. I do this for example with Zsh , Git and the Lastpass CLI client.</p><h4>Cons</h4><p>All the yays aside, there are some limitations with Stow. You need to have the same directory layout for each stow directory. For example, i3 stores its configuration in .config/i3/config . Your i3 directory needs to have the same layout: i3/.config/i3 . Now stow i3 will place the configuration in the correct directory. The dotfiles themselves need to be .dotfiles, there is a PR merged into Stow that deals with this problem, but it isn’t released yet in a tagged version. Once this is done, you can call your .zshrc dot_zshrc. Much nicer than to go into a directory and it looks seemingly empty. <br>You can’t have a stow directory somewhere else. I can’t get my ~/code/bum project to be stowed into my home dir. This might very well be PEBKAC and is a minor annoyance. I already solved this via other means in my pre-Stow era.</p><p>You can find my dotfiles at <a href="https://gitlab.com/waterkip/dotty">https://gitlab.com/waterkip/dotty</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=262d2540a866" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My git workflow]]></title>
            <link>https://medium.com/@waterkip/my-git-workflow-7f2c145c9d6d?source=rss-cf5aae5efd60------2</link>
            <guid isPermaLink="false">https://medium.com/p/7f2c145c9d6d</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[git]]></category>
            <category><![CDATA[open-source]]></category>
            <dc:creator><![CDATA[Wesley Schwengle]]></dc:creator>
            <pubDate>Sun, 17 Mar 2019 12:43:21 GMT</pubDate>
            <atom:updated>2025-10-04T00:28:11.943Z</atom:updated>
            <cc:license>https://creativecommons.org/licenses/by-sa/4.0/</cc:license>
            <content:encoded><![CDATA[<h3>My git workflow explained</h3><p>This post has been rewritten significantly in 2025 and can be found on my personal blog: <a href="https://wesley.schwengle.net/article/bum-11-years-of-git-wrapped-in-a-toolkit-92a9/">https://wesley.schwengle.net/article/bum-11-years-of-git-wrapped-in-a-toolkit-92a9/</a>. I kindly invite you to read that one instead. Thank you.</p><p>At work we use git as a versioning system for all features, server configurations, chef recipes and more. Our main project is an open source (EUPL) licensed software that is used by local government in the Netherlands. We use Agile to develop our software, scrum style. We work with feature branches and have a development branch where all the bugfixes and features come together. Once merged into our master branch it gets deployed to our cloud via Gitlab CI. When we tag a release, our release gets deployed to our customers. We release once every week, in these releases we ship new features as well as bugfixes. We have a development team of around 12 people (devops included, two permanent ops and three rotating devs who op). In this article I want to tell you about my development workflow and the features of git that I use to make my life easier.</p><p>TL;DR: My git tools can be found at <a href="https://gitlab.com/waterkip/bum.git">https://gitlab.com/waterkip/bum.git</a> (MIT license).</p><p>When starting with an issue I branch off of master and start working. This action is repetitive, so I automated this. When you start with git, (almost) everyone tells you to do the following:</p><p>git checkout master<br> git pull upstream master #optional <br>git checkout -b ZS-12345-some_useful_description <br>git set-upstream-to=upstream/master</p><p>I made a git alias that deals with having to type less when checking out a branch: git config -global --add alias.co checkout. Although to be honest I mostly edit the .gitconfig file in an editor en add/edit/remote it directly.</p><p>Now I only have to type git co -b foo to checkout the branch foo.</p><p>git co master<br> git co -b ZS-12345-some_useful_description<br> git set-upstream-to=upstream/master</p><p>But even this is too much typing, we can shorten this to:git co -b ZS-12345-some_useful_description -t upstream/master.</p><p><em>Be advised that the -t (tracking) is just a boolean value. You only tell git that your starting point is the current reference of upstream/master (you may want to fetch upstream first with </em><em>git fetch upstream).</em></p><p>Redditor <a href="https://www.reddit.com/user/ugh_you_swine">/u/ugh_you_swine</a> pointed out that this enables you to “muddy”<br>with the upstream branches. While this is true when your branch name is<br>the same as the remote branch, in this case you will get an error. The<br>man page of git push tells us:</p><pre>When neither the command-line nor the configuration specify what to<br>push, the default behavior is used, which corresponds to the simple<br>value for push.default: the current branch is pushed to the<br>corresponding upstream branch, but as a safety measure, the push is<br>aborted if the upstream branch does not have the same name as the local one.</pre><p>You will get an error from git when you push without any options. You<br>can change the behaviour by changing your configuration to git config --global remote.pushDefault origin. Or omit the --global if you want<br>this on a per repository basis. This configuration option overrides branch.&lt;name&gt;.remote for all branches and is overriden by branch.&lt;name&gt;.pushRemote.</p><p>But I soon got tired of having to type all the underscores, so I created a git script that deals with this: git nb ZS-12345 some useful description. This custom command creates the branch for me and pushes it to my remote (origin). There are several things that I’ve done. <br>First I created a script called <a href="https://gitlab.com/waterkip/bum/blob/master/bin/git-new-branch">git-new-branch</a> that deals with all the logic of fetching upstream, checks if I added an issue number and then deals with my very useful description. Then I created the alias nb which helps me type even less (and you can go further by setting an alias to nb in your shell — yes I did that as well): nb ZS-12345 some useful description. The script also deals with actually pushing the newly created branch to my remote, something I like to do often for all work-related projects.</p><p>So now we can add a new feature or fix a bug. Let’s say I was working on a feature and suddenly someone says, “Drop whatever you are doing, you need to fix this bug immediately!”. Git has a very nice feature called “stash”, every uncommitted change is pushed in the stack. While useful, I also have a love hate relationship with it. Because sometimes I just forget that I stashed some changes. Therefore I created a small script called git-wip which commits all changes with a simple commit message: “WIP: You want to edit this commit message”. It also accepts arguments, git wip working on X when Y interrupted me, which renders my commit message to (you can guess): “WIP: working on X when Y interrupted me”. I can now switch to fixing the bug: nb ZS-23456 urgent bug which needs fixing asap.</p><p>When I have my fix I commit the changes and want to commit my fix. Having a good commit message is crucial, both my future self and others need to know what has changes and more important why I made the change. In search of a good commit message I came across the following template:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/16e1bd512366dd07418c4f1274d5fc3e/href">https://medium.com/media/16e1bd512366dd07418c4f1274d5fc3e/href</a></iframe><p>I have this file in both my homedir and in case of the project at work, a <a href="https://gitlab.com/waterkip/zaaksysteem/blob/master/.default-commit-message">modified version of it in the repository</a>. You can add it to your repository gitconfig git config commit.template .default-commit-message or in your global gitconfiggit config --global commit.template .default-commit-message. Now when I hit git commit it fires up my editor and I have a clear and visual guide on what needs to be in my commit message. I’ve added two hooks to make sure my commit message always starts with the Jira issue number. First is the prepare-commit-msg which adds issue number. Because my branches always have an issue number in their name, this is the easy part. Then I have a second hook which actually deals with making sure I don’t hit exit without saving by accident and the commit messages stays: “ZS-12345: ”. To circumvent this I save the md5 checksum of the modified message in the prepare-commit-msg and check it in thecommit-msg hook.</p><p>Because having to remember what the changes were I’ve aliased commit to c but with some options added: git config --global alias.c &#39;commit -uno -v’. The -u ( --untracked-files) option tells git what it should do with untracked files. And in this case, I don’t want to show untracked files ( no): -uno. See the man page for the other options. The -v option shows a unified diff at the bottom of you editor, so you can easily see what the changes are that you are about to commit. I’ve also added the alias ca for git commit --amend and I make sure that it uses the c alias: git config --global --add alias.ca &#39;!git c --amend’. Now also my amend makes use of the template and allows me to edit my commit message (or forces that, because of the hooks on the commit message). If you don’t want to edit the commit message, just use git ca --no-edit which is aliased to git amend, will now skip the editor bit and just amend the commit. I now just need to push this to my origin, git push origin (aliased to git po) and for that I use git po HEAD and then merge it into our development branch: git po HEAD &amp;&amp; git co development &amp;&amp; git fetch upstream &amp;&amp; git reset --hard upstream/development &amp;&amp; git merge --no-edit - &amp;&amp; git push upstream HEAD &amp;&amp; git co -. This is pushes HEAD to my remote, checks out development, fetches and resets my local version of development and then merges it without having to edit the commit message and pushes it upstream and checks out the branch as if nothing happened. This is also scripted to make life easier. Maybe I should call thisgit-merge-dev. I now move the issue on the board to testing and let our tester do its job. I will probably submit a pull request (or merge request if you’re using Gitlab) to get someone to approve the changes and merge it after testing has completed.</p><p><em>Gitlab has this really nice feature where you can tell that a PR/MR is “Work in progress”, it just adds “WIP: ” in front of your commit message. And prevents Gitlab from merging it into the code base.</em></p><p>Back to my feature! The next git-script: <a href="https://gitlab.com/waterkip/bum/blob/master/bin/git-go-branch">git-go-branch</a> (aliased to git gb) which deals with not having to run git branch | grep ZS-12345 (git branch is aliased to git br), copy the branch name and check that branch out: git gb ZS-12345. I don’t always know the issue number by heart, but go-branch will find any part of the branch name that matches: git gb useful goes to the same branch. If the word useful is found in multiple branches go-branch will just print out which branches were found and I must copy/paste the branch name for the checkout command.</p><p>Now I have two options, I either continue where I left off or I undo the commit. When you choose the former you can use git commit --amend when done and edit the commit message. The latter has the alias undo-commit and does the following: git reset --soft HEAD^. It keeps the files staged for the commit, if you don’t want that, you can run git reset and it is not staged for committing anymore (or run git reset HEAD^ if you want it in one go). We can continue to work on the feature.</p><p>When submitting your PR/MR you will probably get some feedback of your peers and you’ll find yourself wanting to update your modifications accordingly. For years I’ve fixed those issues and made new commits which said things like “Processed feedback of reviewer”. But I came across this neat feature called “fixup”. Which allows you to fix a commit. When making a change to a commit I lookup the commit ID of the commit that introduced the thing where I get feedback on and then I hit git commit --fixup &lt;commit-ish&gt; (aliased to git fixup). This leaves a commit message which looks a bit like “fixup! ZS-12345: Adds new method to ZS::Foo::Bar”. I do this for all the feedback I got in the MR/PR and then push it to my remote (and the development branch). When the reviewer is happy with my changes and approves the MR/PR I do the following git pull --rebase -i --autosquash (aliased to git autosquash). This will pull all the changes from the upstream master branch (remember the tracking bit with my branch creation?) and will squash the commits and show what will be rebased. It will also place all the fixup commits directly under the commit it fixes and merges the commit with the fixup. They become one.</p><p>Now I merge the code into the code base and don’t clobber my history with meaningless commit messages about the review procedure.<br>I also use fixup when the tester reports an issue while testing development and I don’t want to push a rebased version of my work into the development branch. Or if I work on a feature with multiple commits and find out that something isn’t working. Because I use autosquash before actually merging into master my history stays pretty and readable. I found fixup to be really helpful to stay in the commit early and often mantra while keeping the commit history clean.</p><p>If you work on a project long enough you’ll want to read the commit messages because sometimes you’ll use git blame(I’ve aliased this to git praise because sometimes you just need to praise who ever made a change) to find out who made a particular change in the code base and why they did that. Being verbose in a commit message helps. The number of times someone has asked me about a code change and I or my co-worker go.. “There were issues, but.. nope, I forgot why I made that particular change in that way” is plentiful in my five years at Mintlab. It is an acceptable answer but having a good commit message prevents one from asking and having to answer such questions. We use a .mailmap file in our repository and I want to have all my logs show one particular author, no several because they have a different mail address at Gitlab, on their development machine or at their laptop at home. So I use git log --use-mailmap for the alias git l. This alias is then used in several other aliases, such as git lg (git log --graph) so I can have a graph display of all the commits, and git sl(git log --format=&#39;%C(yellow)%h%Creset%aI%Cgreen%s%Creset%a) that makes grepping on particular commit messages easier.</p><p>In addition to this I’ve created git-log-commitwhich shows me the diff for just one commit, or when a merge commit is given, shows me the diff of all the commits of that merge commit.</p><p>As I was explaining all of this to my colleague he said that I should put my scripts somewhere. Since they were in a private repository with all my dotfiles, which some of them I don’t want to share with the world I’ve created “<a href="https://gitlab.com/waterkip/bum.git">Bum</a>”. You can have a look at it at <a href="https://gitlab.com/waterkip/bum.git,">https://gitlab.com/waterkip/bum.git,</a> or clone it directly if you already have a Gitlab account git clone git@gitlab.com:waterkip/bum.git.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7f2c145c9d6d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>