<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 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">
    <channel>
        <title>Ruben's Blog Posts | RSS Feed</title>
        <link>https://callmeruben.com</link>
        <description>My personal blog where I post about my experiences in software development, software architecture, hardware and life.</description>
        <lastBuildDate>Sat, 15 Jul 2017 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Ruben's Personal Website</generator>
        <language>en</language>
        <image>
            <title>Ruben's Blog Posts | RSS Feed</title>
            <url>https://callmeruben.com/icons/icon.png</url>
            <link>https://callmeruben.com</link>
        </image>
        <copyright>All rights reserved 2024, Ruben Harutyunyan</copyright>
        <atom:link href="https://callmeruben.com/blog/rss.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Making Git usage more comfortable with aliases]]></title>
            <link>https://callmeruben.com/blog/making-git-usage-more-comfortable-with-aliases</link>
            <guid>making-git-usage-more-comfortable-with-aliases</guid>
            <pubDate>Sat, 15 Jul 2017 00:00:00 GMT</pubDate>
            <description><![CDATA[Ever been tired of typing the same long Git command? These tips can help you.]]></description>
            <content:encoded><![CDATA[<p>Typing the same long command everyday gets old way too fast. Especially if it&#x27;s a macro that takes 3 lines on terminal screen to type. We are going to use Git aliases to fix that.</p><h2 id="how-to-set-aliases">How to set aliases</h2><p>First of all, to actually set an alias you have 2 options.</p><ol><li><p><code>$ git config --global alias.co checkout</code></p><p>The command line way of setting aliases is pretty straightforward. In this case <code>co</code> is an alias for <code>checkout</code>.</p></li><li><p>Editing <code>.gitconfig</code></p><p>Your <code>.gitconfig</code> might look like this:</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">alias</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="2"><span class="token attr-name key">co</span> <span class="token punctuation">=</span> <span class="token attr-value value">checkout</span>
</span></code></pre><p>You can also use <code>&quot;!&quot;</code> to execute the aliased command in a shell.</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token attr-name key">di</span><span class="token punctuation">=</span><span class="token attr-value value">!git status --porcelain --short --ignored | sed -n &#x27;&quot;s/^!! //p&quot;&#x27;</span>
</span></code></pre></li></ol><h2 id="inspiration-for-aliases">Inspiration for aliases</h2><p>What are the most used commands in your case? Commit? Checkout? Then set them as aliases. In most cases that&#x27;s the best way of improving your workflow.</p><p>If you want something &quot;future proof&quot; there are a couple of great resources for that.</p><ul><li><p><a href="https://github.com/sorin-ionescu/prezto/blob/master/modules/git/alias.zsh">Prezto&#x27;s Git aliases</a></p><p>Probably the best organization of aliases. They are actually supposed to be used from the shell, not as a git subcommand, but more on that later.</p></li><li><p><a href="https://www.youtube.com/watch?v=3IIaOj1Lhb0">Git Aliases of the Gods! - Git Merge 2017</a></p><p>This is a talk by a BitBucket developer Tim Pettersen. Some of his aliases didn&#x27;t make into my list for complexity reasons, but if you are interested in serving your local Git repository to the internet, you might want to check out the video at <a href="https://youtu.be/3IIaOj1Lhb0?t=11m32s">11:32</a>.</p></li><li><p>Your imagination</p><p>You need something more? Add it yourself!</p></li></ul><h2 id="warnings">Warnings</h2><p>While creating your aliases you must consider multiple things:</p><ol><li><p>Git aliases are case insensitive</p><pre class="language-sh"><code class="code-highlight language-sh"><span class="code-line line-number" line="1"><span class="token function">git</span> gco <span class="token comment"># Lowercase</span>
</span><span class="code-line line-number" line="2"><span class="token function">git</span> gCo <span class="token comment"># is actually the same</span>
</span></code></pre></li><li><p>Creating way too many shell aliases will probably result in command name clashes.</p><p>For example <code>gs</code>, which I use for <code>git status</code> will clash with GhostScript&#x27;s executable. Fortunately for me I pretty much never use GhostScript, so that&#x27;s a loss I can live with.</p><p>You can also source an executable by typing its whole path to skip aliases, like <code>/usr/bin/gs</code>.</p></li></ol><h2 id="actual-aliases">Actual aliases</h2><p>First of all, we are going to &quot;translate&quot; Prezto&#x27;s aliases for the shell to their respective Git versions.</p><p>During the conversion we will lose <code>git submodule</code> and <code>git flow</code> aliases since they are using uppercase letters to differentiate from <code>git stash</code> and <code>git fetch</code>.</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">alias</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="2">  <span class="token comment"># Branch (b)</span>
</span><span class="code-line line-number" line="3">  <span class="token attr-name key">b</span><span class="token punctuation">=</span><span class="token attr-value value">branch</span>
</span><span class="code-line line-number" line="4">  <span class="token attr-name key">ba</span><span class="token punctuation">=</span><span class="token attr-value value">branch --all --verbose --verbose</span>
</span><span class="code-line line-number" line="5">  <span class="token attr-name key">bl</span><span class="token punctuation">=</span><span class="token attr-value value">branch --verbose --verbose</span>
</span><span class="code-line line-number" line="6">  <span class="token attr-name key">bc</span><span class="token punctuation">=</span><span class="token attr-value value">checkout -b</span>
</span><span class="code-line line-number" line="7">  <span class="token attr-name key">bx</span><span class="token punctuation">=</span><span class="token attr-value value">branch --delete</span>
</span><span class="code-line line-number" line="8">  <span class="token attr-name key">bm</span><span class="token punctuation">=</span><span class="token attr-value value">branch --move</span>
</span><span class="code-line line-number" line="9">  <span class="token attr-name key">bs</span><span class="token punctuation">=</span><span class="token attr-value value">show-branch</span>
</span><span class="code-line line-number" line="10">
</span><span class="code-line line-number" line="11">  <span class="token comment"># Commit (c)</span>
</span><span class="code-line line-number" line="12">  <span class="token attr-name key">c</span><span class="token punctuation">=</span><span class="token attr-value value">commit</span>
</span><span class="code-line line-number" line="13">  <span class="token attr-name key">ca</span><span class="token punctuation">=</span><span class="token attr-value value">commit --all</span>
</span><span class="code-line line-number" line="14">  <span class="token attr-name key">cm</span><span class="token punctuation">=</span><span class="token attr-value value">commit --message</span>
</span><span class="code-line line-number" line="15">  <span class="token attr-name key">cam</span><span class="token punctuation">=</span><span class="token attr-value value">commit --all --message</span>
</span><span class="code-line line-number" line="16">  <span class="token attr-name key">co</span><span class="token punctuation">=</span><span class="token attr-value value">checkout</span>
</span><span class="code-line line-number" line="17">  <span class="token attr-name key">cop</span><span class="token punctuation">=</span><span class="token attr-value value">checkout --patch</span>
</span><span class="code-line line-number" line="18">  <span class="token attr-name key">cf</span><span class="token punctuation">=</span><span class="token attr-value value">commit --amend --reuse-message HEAD</span>
</span><span class="code-line line-number" line="19">  <span class="token attr-name key">chp</span><span class="token punctuation">=</span><span class="token attr-value value">cherry-pick</span>
</span><span class="code-line line-number" line="20">  <span class="token attr-name key">cr</span><span class="token punctuation">=</span><span class="token attr-value value">revert</span>
</span><span class="code-line line-number" line="21">  <span class="token attr-name key">cres</span><span class="token punctuation">=</span><span class="token attr-value value">reset &quot;HEAD^&quot;</span>
</span><span class="code-line line-number" line="22">  <span class="token attr-name key">cs</span><span class="token punctuation">=</span><span class="token attr-value value">show</span>
</span><span class="code-line line-number" line="23">
</span><span class="code-line line-number" line="24">  <span class="token comment"># Data (d)</span>
</span><span class="code-line line-number" line="25">  <span class="token attr-name key">d</span><span class="token punctuation">=</span><span class="token attr-value value">ls-files</span>
</span><span class="code-line line-number" line="26">  <span class="token attr-name key">dc</span><span class="token punctuation">=</span><span class="token attr-value value">ls-files --cached</span>
</span><span class="code-line line-number" line="27">  <span class="token attr-name key">dd</span><span class="token punctuation">=</span><span class="token attr-value value">ls-files --deleted</span>
</span><span class="code-line line-number" line="28">  <span class="token attr-name key">dx</span><span class="token punctuation">=</span><span class="token attr-value value">ls-files --deleted</span>
</span><span class="code-line line-number" line="29">  <span class="token attr-name key">dm</span><span class="token punctuation">=</span><span class="token attr-value value">ls-files --modified</span>
</span><span class="code-line line-number" line="30">  <span class="token attr-name key">do</span><span class="token punctuation">=</span><span class="token attr-value value">ls-files --other --exclude-standard</span>
</span><span class="code-line line-number" line="31">  <span class="token attr-name key">dk</span><span class="token punctuation">=</span><span class="token attr-value value">ls-files --killed</span>
</span><span class="code-line line-number" line="32">  <span class="token attr-name key">di</span><span class="token punctuation">=</span><span class="token attr-value value">!git status --porcelain --short --ignored | sed -n &#x27;&quot;s/^!! //p&quot;&#x27;</span>
</span><span class="code-line line-number" line="33">
</span><span class="code-line line-number" line="34">  <span class="token comment"># Fetch (f)</span>
</span><span class="code-line line-number" line="35">  <span class="token attr-name key">f</span><span class="token punctuation">=</span><span class="token attr-value value">fetch</span>
</span><span class="code-line line-number" line="36">  <span class="token attr-name key">fa</span><span class="token punctuation">=</span><span class="token attr-value value">fetch --all</span>
</span><span class="code-line line-number" line="37">  <span class="token attr-name key">fc</span><span class="token punctuation">=</span><span class="token attr-value value">clone</span>
</span><span class="code-line line-number" line="38">  <span class="token attr-name key">fp</span><span class="token punctuation">=</span><span class="token attr-value value">pull</span>
</span><span class="code-line line-number" line="39">  <span class="token attr-name key">fr</span><span class="token punctuation">=</span><span class="token attr-value value">pull --rebase</span>
</span><span class="code-line line-number" line="40">  <span class="token attr-name key">fpr</span><span class="token punctuation">=</span><span class="token attr-value value">pull --rebase</span>
</span><span class="code-line line-number" line="41">
</span><span class="code-line line-number" line="42">  <span class="token comment"># Grep (g)</span>
</span><span class="code-line line-number" line="43">  <span class="token attr-name key">g</span><span class="token punctuation">=</span><span class="token attr-value value">grep</span>
</span><span class="code-line line-number" line="44">  <span class="token attr-name key">gi</span><span class="token punctuation">=</span><span class="token attr-value value">grep --ignore-case</span>
</span><span class="code-line line-number" line="45">  <span class="token attr-name key">gl</span><span class="token punctuation">=</span><span class="token attr-value value">grep --files-with-matches</span>
</span><span class="code-line line-number" line="46">  <span class="token attr-name key">gL</span><span class="token punctuation">=</span><span class="token attr-value value">grep --files-without-matches</span>
</span><span class="code-line line-number" line="47">  <span class="token attr-name key">gv</span><span class="token punctuation">=</span><span class="token attr-value value">grep --invert-match</span>
</span><span class="code-line line-number" line="48">  <span class="token attr-name key">gw</span><span class="token punctuation">=</span><span class="token attr-value value">grep --word-regexp</span>
</span><span class="code-line line-number" line="49">
</span><span class="code-line line-number" line="50">  <span class="token comment"># Index (i)</span>
</span><span class="code-line line-number" line="51">  <span class="token attr-name key">ia</span><span class="token punctuation">=</span><span class="token attr-value value">add</span>
</span><span class="code-line line-number" line="52">  <span class="token attr-name key">iap</span><span class="token punctuation">=</span><span class="token attr-value value">add --patch</span>
</span><span class="code-line line-number" line="53">  <span class="token attr-name key">iu</span><span class="token punctuation">=</span><span class="token attr-value value">add --update</span>
</span><span class="code-line line-number" line="54">  <span class="token attr-name key">id</span><span class="token punctuation">=</span><span class="token attr-value value">diff --no-ext-diff --cached</span>
</span><span class="code-line line-number" line="55">  <span class="token attr-name key">idw</span><span class="token punctuation">=</span><span class="token attr-value value">diff --no-ext-diff --cached --word-diff</span>
</span><span class="code-line line-number" line="56">  <span class="token attr-name key">ir</span><span class="token punctuation">=</span><span class="token attr-value value">reset</span>
</span><span class="code-line line-number" line="57">  <span class="token attr-name key">irp</span><span class="token punctuation">=</span><span class="token attr-value value">reset --patch</span>
</span><span class="code-line line-number" line="58">  <span class="token attr-name key">ix</span><span class="token punctuation">=</span><span class="token attr-value value">rm -r --cached</span>
</span><span class="code-line line-number" line="59">
</span><span class="code-line line-number" line="60">  <span class="token comment"># Merge (m)</span>
</span><span class="code-line line-number" line="61">  <span class="token attr-name key">m</span><span class="token punctuation">=</span><span class="token attr-value value">merge</span>
</span><span class="code-line line-number" line="62">  <span class="token attr-name key">mnc</span><span class="token punctuation">=</span><span class="token attr-value value">merge --no-commit</span>
</span><span class="code-line line-number" line="63">  <span class="token attr-name key">mf</span><span class="token punctuation">=</span><span class="token attr-value value">merge --ff</span>
</span><span class="code-line line-number" line="64">  <span class="token attr-name key">mnf</span><span class="token punctuation">=</span><span class="token attr-value value">merge --no-ff</span>
</span><span class="code-line line-number" line="65">  <span class="token attr-name key">ma</span><span class="token punctuation">=</span><span class="token attr-value value">merge --abort</span>
</span><span class="code-line line-number" line="66">  <span class="token attr-name key">mt</span><span class="token punctuation">=</span><span class="token attr-value value">mergetool</span>
</span><span class="code-line line-number" line="67">
</span><span class="code-line line-number" line="68">  <span class="token comment"># Push (p)</span>
</span><span class="code-line line-number" line="69">  <span class="token attr-name key">p</span><span class="token punctuation">=</span><span class="token attr-value value">push</span>
</span><span class="code-line line-number" line="70">  <span class="token attr-name key">pf</span><span class="token punctuation">=</span><span class="token attr-value value">push --force-with-lease</span>
</span><span class="code-line line-number" line="71">  <span class="token attr-name key">pa</span><span class="token punctuation">=</span><span class="token attr-value value">push --all</span>
</span><span class="code-line line-number" line="72">  <span class="token attr-name key">pt</span><span class="token punctuation">=</span><span class="token attr-value value">push --tags</span>
</span><span class="code-line line-number" line="73">  <span class="token attr-name key">pat</span><span class="token punctuation">=</span><span class="token attr-value value">push --all &amp;&amp; git push --tags</span>
</span><span class="code-line line-number" line="74">  <span class="token attr-name key">pc</span><span class="token punctuation">=</span><span class="token attr-value value">!git push --set-upstream origin &quot;$(git-branch-current 2&gt; /dev/null)&quot;</span>
</span><span class="code-line line-number" line="75">  <span class="token attr-name key">pp</span><span class="token punctuation">=</span><span class="token attr-value value">!git pull origin &quot;$(git-branch-current 2&gt; /dev/null)&quot; &amp;&amp; git push origin &quot;$(git-branch-current 2&gt; /dev/null)&quot;</span>
</span><span class="code-line line-number" line="76">
</span><span class="code-line line-number" line="77">  <span class="token comment"># Rebase (r)</span>
</span><span class="code-line line-number" line="78">  <span class="token attr-name key">r</span><span class="token punctuation">=</span><span class="token attr-value value">rebase</span>
</span><span class="code-line line-number" line="79">  <span class="token attr-name key">ra</span><span class="token punctuation">=</span><span class="token attr-value value">rebase --abort</span>
</span><span class="code-line line-number" line="80">  <span class="token attr-name key">rc</span><span class="token punctuation">=</span><span class="token attr-value value">rebase --continue</span>
</span><span class="code-line line-number" line="81">  <span class="token attr-name key">ri</span><span class="token punctuation">=</span><span class="token attr-value value">rebase --interactive</span>
</span><span class="code-line line-number" line="82">  <span class="token attr-name key">rs</span><span class="token punctuation">=</span><span class="token attr-value value">rebase --skip</span>
</span><span class="code-line line-number" line="83">
</span><span class="code-line line-number" line="84">  <span class="token comment"># Stash (s)</span>
</span><span class="code-line line-number" line="85">  <span class="token attr-name key">stsh</span> <span class="token punctuation">=</span> <span class="token attr-value value">stash --keep-index</span>
</span><span class="code-line line-number" line="86">  <span class="token attr-name key">staash</span> <span class="token punctuation">=</span> <span class="token attr-value value">stash --include-untracked</span>
</span><span class="code-line line-number" line="87">  <span class="token attr-name key">staaash</span> <span class="token punctuation">=</span> <span class="token attr-value value">stash --all</span>
</span><span class="code-line line-number" line="88">  <span class="token attr-name key">s</span><span class="token punctuation">=</span><span class="token attr-value value">stash</span>
</span><span class="code-line line-number" line="89">  <span class="token attr-name key">sa</span><span class="token punctuation">=</span><span class="token attr-value value">stash apply</span>
</span><span class="code-line line-number" line="90">  <span class="token attr-name key">sx</span><span class="token punctuation">=</span><span class="token attr-value value">stash drop</span>
</span><span class="code-line line-number" line="91">  <span class="token attr-name key">sl</span><span class="token punctuation">=</span><span class="token attr-value value">stash list</span>
</span><span class="code-line line-number" line="92">  <span class="token attr-name key">sd</span><span class="token punctuation">=</span><span class="token attr-value value">stash show --patch --stat</span>
</span><span class="code-line line-number" line="93">  <span class="token attr-name key">sp</span><span class="token punctuation">=</span><span class="token attr-value value">stash pop</span>
</span><span class="code-line line-number" line="94">  <span class="token attr-name key">ss</span><span class="token punctuation">=</span><span class="token attr-value value">stash save</span>
</span><span class="code-line line-number" line="95">  <span class="token attr-name key">ssu</span><span class="token punctuation">=</span><span class="token attr-value value">stash save --include-untracked</span>
</span><span class="code-line line-number" line="96">
</span><span class="code-line line-number" line="97">  <span class="token comment"># Working Copy (w)</span>
</span><span class="code-line line-number" line="98">  <span class="token attr-name key">ws</span><span class="token punctuation">=</span><span class="token attr-value value">status</span>
</span><span class="code-line line-number" line="99">  <span class="token attr-name key">wd</span><span class="token punctuation">=</span><span class="token attr-value value">diff --no-ext-diff</span>
</span><span class="code-line line-number" line="100">  <span class="token attr-name key">wdw</span><span class="token punctuation">=</span><span class="token attr-value value">diff --no-ext-diff --word-diff</span>
</span><span class="code-line line-number" line="101">  <span class="token attr-name key">wrs</span><span class="token punctuation">=</span><span class="token attr-value value">reset --soft</span>
</span><span class="code-line line-number" line="102">  <span class="token attr-name key">wrh</span><span class="token punctuation">=</span><span class="token attr-value value">reset --hard</span>
</span><span class="code-line line-number" line="103">  <span class="token attr-name key">wc</span><span class="token punctuation">=</span><span class="token attr-value value">clean -n</span>
</span><span class="code-line line-number" line="104">  <span class="token attr-name key">wcf</span><span class="token punctuation">=</span><span class="token attr-value value">clean -f</span>
</span><span class="code-line line-number" line="105">  <span class="token attr-name key">wcfd</span><span class="token punctuation">=</span><span class="token attr-value value">clean -df</span>
</span><span class="code-line line-number" line="106">  <span class="token attr-name key">wx</span><span class="token punctuation">=</span><span class="token attr-value value">rm -r</span>
</span><span class="code-line line-number" line="107">  <span class="token attr-name key">wxf</span><span class="token punctuation">=</span><span class="token attr-value value">rm -rf</span>
</span></code></pre><p>Then, we are going to add pretty looking logs based on <a href="https://stackoverflow.com/questions/1057564/pretty-git-branch-graphs/9074343#9074343">this StackOverflow answer</a>.</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1">  <span class="token comment"># Log (l)</span>
</span><span class="code-line line-number" line="2">  <span class="token attr-name key">l1</span> <span class="token punctuation">=</span> <span class="token attr-value value">log --graph --abbrev-commit --decorate --date=relative --format=format:&#x27;%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)&#x27; --all</span>
</span><span class="code-line line-number" line="3">  <span class="token attr-name key">l2</span> <span class="token punctuation">=</span> <span class="token attr-value value">log --graph --abbrev-commit --decorate --format=format:&#x27;%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n&#x27;&#x27;          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)&#x27; --all</span>
</span><span class="code-line line-number" line="4">  <span class="token attr-name key">l</span> <span class="token punctuation">=</span> <span class="token attr-value value">!git l1</span>
</span></code></pre><p>Would be cool to save a snapshot of changes without committing changes, right?</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1">  <span class="token attr-name key">snapshot</span> <span class="token punctuation">=</span> <span class="token attr-value value">!git stash save &quot;snapshot: $(date)&quot; &amp;&amp; git stash apply &quot;stash@{0}&quot;</span>
</span><span class="code-line line-number" line="2">  <span class="token attr-name key">ls-snapshots</span> <span class="token punctuation">=</span> <span class="token attr-value value">!git stash list --grep snapshot</span>
</span></code></pre><p>Listing recent branches is sometimes useful.</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1">  <span class="token attr-name key">recent-branches</span> <span class="token punctuation">=</span> <span class="token attr-value value">!git for-each-ref --count=15 --sort=-committerdate refs/heads/ --format=&#x27;%(refname:short)&#x27;</span>
</span></code></pre><p>In the end we&#x27;ll add URL shorthands to popular websites.</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">url &quot;https://aur.archlinux.org/&quot;</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="2">  <span class="token attr-name key">insteadOf</span> <span class="token punctuation">=</span> <span class="token attr-value value">&quot;<span class="token inner-value">aur:</span>&quot;</span>
</span><span class="code-line line-number" line="3"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">url &quot;ssh+git://aur4.archlinux.org/&quot;</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="4">  <span class="token attr-name key">pushInsteadOf</span> <span class="token punctuation">=</span> <span class="token attr-value value">&quot;<span class="token inner-value">aur:</span>&quot;</span>
</span><span class="code-line line-number" line="5"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">url &quot;https://github.com/&quot;</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="6">  <span class="token attr-name key">insteadOf</span> <span class="token punctuation">=</span> <span class="token attr-value value">&quot;<span class="token inner-value">gh:</span>&quot;</span>
</span><span class="code-line line-number" line="7"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">url &quot;git@github.com:&quot;</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="8">  <span class="token attr-name key">pushInsteadOf</span> <span class="token punctuation">=</span> <span class="token attr-value value">&quot;<span class="token inner-value">gh:</span>&quot;</span>
</span></code></pre><h2 id="general-tips">General tips</h2><p>These aren&#x27;t really aliases but they are still useful.</p><ul><li><p>Verbose commit messages by default (includes commented out diff)</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">commit</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="2">    <span class="token attr-name key">verbose</span> <span class="token punctuation">=</span> <span class="token attr-value value">true</span>
</span></code></pre></li><li><p>Default push location</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">push</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="2">    <span class="token attr-name key">default</span> <span class="token punctuation">=</span> <span class="token attr-value value">upstream</span>
</span></code></pre></li><li><p>Enable <a href="https://git-scm.com/blog/2010/03/08/rerere.html">rerere</a></p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">rerere</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="2">    <span class="token attr-name key">enabled</span> <span class="token punctuation">=</span> <span class="token attr-value value">true</span>
</span></code></pre></li><li><p>Enable colored output</p><pre class="language-ini"><code class="language-ini code-highlight"><span class="code-line line-number" line="1"><span class="token section"><span class="token punctuation">[</span><span class="token section-name selector">color</span><span class="token punctuation">]</span></span>
</span><span class="code-line line-number" line="2">    <span class="token attr-name key">ui</span> <span class="token punctuation">=</span> <span class="token attr-value value">true</span>
</span></code></pre></li></ul><h2 id="but-in-the-end">But in the end...</h2><p>Those aliases might be an overkill for your use case. If you ever feel like you are wasting more time on &quot;upgrading&quot; your workflow instead of getting actual work done, consider stopping.</p><p>That being said, you can check out the full <code>.gitconfig</code> in <a href="https://github.com/Vagr9K/dotfiles/blob/master/git/gitconfig">my dotfiles repo</a>.</p>]]></content:encoded>
            <author>vagr9k@gmail.com (Ruben Harutyunyan)</author>
            <category>dotfiles</category>
            <enclosure url="https://callmeruben.com/blog/making-git-usage-more-comfortable-with-aliases/git-settings.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Tmux tips and tricks]]></title>
            <link>https://callmeruben.com/blog/tmux-tips-and-tricks</link>
            <guid>tmux-tips-and-tricks</guid>
            <pubDate>Mon, 17 Jul 2017 00:00:00 GMT</pubDate>
            <description><![CDATA[Improving Tmux workflows using configuration and plugins.]]></description>
            <content:encoded><![CDATA[<p>I&#x27;m a heavy tmux user. Mostly because of the way I launch and monitor my dev servers/compilers/transpilers while coding.</p><p>Configuring Tmux in a right way for your needs is really important if you want to have a distraction free workflow.</p><h2 id="better-tmux-prefix">Better Tmux prefix</h2><p>Prefix key is going to be the most used one. Setting it right is crucial. For me <code>Meta-z</code> combination works really well since both keys are easy to press and I don&#x27;t use any other keybinds that can interfere with it.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment"># Better prefix</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> prefix M-z
</span><span class="code-line line-number" line="3">unbind C-b
</span><span class="code-line line-number" line="4"><span class="token builtin class-name">bind</span> M-z send-prefix
</span></code></pre><p>At some point you&#x27;ll also face a situation when you need to use the prefix key in nested sessions, so let&#x27;s handle that too by setting <code>Meta-a</code> as a keybind.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment"># Nested Tmux keybinds</span>
</span><span class="code-line line-number" line="2">bind-key <span class="token parameter variable">-n</span> M-a send-prefix
</span></code></pre><h2 id="pane-navigation">Pane navigation</h2><p>Use Meta+arrow keys without prefix key to switch panes.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> M-Left select-pane <span class="token parameter variable">-L</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> M-Right select-pane <span class="token parameter variable">-R</span>
</span><span class="code-line line-number" line="3"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> M-Up select-pane <span class="token parameter variable">-U</span>
</span><span class="code-line line-number" line="4"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> M-Down select-pane <span class="token parameter variable">-D</span>
</span></code></pre><h2 id="window-navigation">Window navigation</h2><p>Use shift+arrow keys to switch windows.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> S-Left  previous-window
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> S-Right next-window
</span></code></pre><h2 id="vim-user-specific-keybinds">Vim user specific keybinds</h2><p>You can skip this part if you don&#x27;t plan on using Vi/Vim or their keybinds. Otherwise comments are pretty self-explainatory.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment"># Instant vim-mode change</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-s</span> escape-time <span class="token number">0</span>
</span><span class="code-line line-number" line="3">
</span><span class="code-line line-number" line="4"><span class="token comment"># Enable modifier keys in vim</span>
</span><span class="code-line line-number" line="5">set-option <span class="token parameter variable">-g</span> xterm-keys on
</span><span class="code-line line-number" line="6">
</span><span class="code-line line-number" line="7"><span class="token comment"># Use vi keybinds</span>
</span><span class="code-line line-number" line="8">setw <span class="token parameter variable">-g</span> mode-keys <span class="token function">vi</span>
</span><span class="code-line line-number" line="9"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-keys <span class="token function">vi</span>
</span></code></pre><h2 id="mouse-support">Mouse support</h2><p>Generally you shouldn&#x27;t be using your mouse if you want to be <em>extra</em> productive but there are exceptions, so we&#x27;ll set it up just in case:</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment"># Mouse support</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> mouse on
</span><span class="code-line line-number" line="3"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> WheelUpPane   select-pane <span class="token parameter variable">-t</span><span class="token operator">=</span> <span class="token punctuation">\</span><span class="token punctuation">;</span> copy-mode <span class="token parameter variable">-e</span> <span class="token punctuation">\</span><span class="token punctuation">;</span> send-keys <span class="token parameter variable">-M</span>
</span><span class="code-line line-number" line="4"><span class="token builtin class-name">bind</span> <span class="token parameter variable">-n</span> WheelDownPane select-pane <span class="token parameter variable">-t</span><span class="token operator">=</span> <span class="token punctuation">\</span><span class="token punctuation">;</span>                 send-keys <span class="token parameter variable">-M</span>
</span></code></pre><h2 id="copy-mode-keybinds">Copy mode keybinds</h2><p>This snippet creates a vi-like experience in copy mode.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment"># Copy mode settings</span>
</span><span class="code-line line-number" line="2">unbind p
</span><span class="code-line line-number" line="3"><span class="token builtin class-name">bind</span> p paste-buffer
</span><span class="code-line line-number" line="4"><span class="token builtin class-name">bind</span> -Tcopy-mode <span class="token function">v</span> send <span class="token parameter variable">-X</span> begin-selection
</span><span class="code-line line-number" line="5"><span class="token builtin class-name">bind</span> -Tcopy-mode y send <span class="token parameter variable">-X</span> copy-selection
</span></code></pre><h2 id="increase-the-history-size">Increase the history size</h2><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment"># Bigger history</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> history-limit <span class="token number">10000</span>
</span></code></pre><h2 id="change-panewindow-counting-index">Change pane/window counting index</h2><p>If you prefer keeping 0-index pane/window for special purposes, this might come in handy.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment"># Start counting windows/panes from 1</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> base-index <span class="token number">1</span>
</span><span class="code-line line-number" line="3">setw <span class="token parameter variable">-g</span> pane-base-index <span class="token number">1</span>
</span></code></pre><h2 id="turn-on-the-aggressive-resize">Turn on the aggressive resize</h2><p>When using the same session on multiple screens, you&#x27;ll be limited by the smallest screen resolution on <em>all</em> screens. This can be mostly fixed by aggressive resize.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1">setw <span class="token parameter variable">-g</span> aggressive-resize on
</span></code></pre><h2 id="use-a-plugin-manager">Use a plugin manager</h2><p><a href="https://github.com/tmux-plugins/tpm">TPM</a> is a really great way of managing Tmux plugins.</p><p>I&#x27;m personally using the following ones:</p><ul><li><a href="https://github.com/tmux-plugins/tmux-resurrect">Tmux Resurrect</a> to restore the session after rebooting</li><li><a href="https://github.com/tmux-plugins/tmux-copycat">Tmux Copycat</a> for regex based searches in copymode</li><li><a href="https://github.com/tmux-plugins/tmux-net-speed">Tmux net-speed</a> to monitor network usage</li></ul><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment">##Plugins</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> @plugin <span class="token string">&#x27;tmux-plugins/tpm&#x27;</span>
</span><span class="code-line line-number" line="3"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> @plugin <span class="token string">&#x27;tmux-plugins/tmux-resurrect&#x27;</span>
</span><span class="code-line line-number" line="4"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> @plugin <span class="token string">&#x27;tmux-plugins/tmux-copycat&#x27;</span>
</span><span class="code-line line-number" line="5"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> @plugin <span class="token string">&#x27;tmux-plugins/tmux-net-speed&#x27;</span>
</span><span class="code-line line-number" line="6">
</span><span class="code-line line-number" line="7"><span class="token comment">##Plugin location</span>
</span><span class="code-line line-number" line="8">set-environment <span class="token parameter variable">-g</span> TMUX_PLUGIN_MANAGER_PATH <span class="token string">&#x27;~/.dotfiles/tmux/plugins/&#x27;</span>
</span><span class="code-line line-number" line="9"><span class="token comment">##Init</span>
</span><span class="code-line line-number" line="10">run <span class="token string">&#x27;~/.dotfiles/tmux/plugins/tpm/tpm&#x27;</span>
</span></code></pre><h2 id="themes">Themes</h2><p>I&#x27;m using a custom Powerline-like Tmux theme I&#x27;ve made myself.</p><div><img alt="Tmux theme screenshot" src="/blog/tmux-tips-and-tricks/theme-screenshot.png" width="1354" height="647" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAA2wAAANsAF9ZVn6AAAAIklEQVR4nGPY8uB9bMtkBhUjBiF5Bnm3IAYlPRWvyKLZKwFz7ggDN1BCPAAAAABJRU5ErkJggg=="/></div><p>You can read more about that in <a href="/blog/creating-a-native-powerline-theme-for-tmux">Creating a native Powerline theme for Tmux</a>.</p><h2 id="is-it-worth-investing-time-into-this">Is it worth investing time into this?</h2><p>If you are using SSH to manage multiple computers/servers, or would like to emulate <a href="https://launchpad.net/terminator">Terminator</a> like features in normal terminals, then yes.</p><p>Tmux also can be a good terminal workspace manager.</p><p>My full Tmux configuration is in <a href="https://github.com/Vagr9K/dotfiles/tree/master/tmux">my dotfiles repo</a>.</p>]]></content:encoded>
            <author>vagr9k@gmail.com (Ruben Harutyunyan)</author>
            <category>dotfiles</category>
            <enclosure url="https://callmeruben.com/blog/tmux-tips-and-tricks/tmux-settings.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Creating a native Powerline theme for Tmux]]></title>
            <link>https://callmeruben.com/blog/creating-a-native-powerline-theme-for-tmux</link>
            <guid>creating-a-native-powerline-theme-for-tmux</guid>
            <pubDate>Thu, 20 Jul 2017 00:00:00 GMT</pubDate>
            <description><![CDATA[Adding a bit of color to the terminal DX.]]></description>
            <content:encoded><![CDATA[<p>Many of you might have liked the appearance of <a href="https://github.com/powerline/powerline">the Powerline</a> tmux theme, but the hassle of installing Powerline and suffering possible performance degradation was not worth it.</p><p>In this article I&#x27;m going to talk about theming Tmux to look like Powerline.</p><p>For those of you interested in the end result <a href="https://github.com/Vagr9K/dotfiles/blob/master/tmux/powerline.tmuxtheme">here is the theme file</a>.</p><p>Source it in your <code>tmux.conf</code>.</p><p>Inspired by <a href="https://github.com/jimeh/tmux-themepack/blob/master/powerline/default/cyan.tmuxtheme">Tmux themepack</a>.</p><h2 id="determining-required-features">Determining required features</h2><p>We are mostly interested in:</p><ul><li>Indication of the prefix activation</li><li>Powerline like window list</li><li>Network usage indication</li><li>Time</li><li>Number of tmux clients</li><li>Hostname</li><li>Username</li></ul><h2 id="tmux-theming-101">Tmux theming 101</h2><p>Update interval for the statusline is being set with <code>set -g status-interval SECONDS</code>.</p><p>Colors in Tmux config, are set using Xterm color codes. You can use <a href="https://jonasjacek.github.io/colors/">this cheatsheet</a> to get the right color.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-fg colour240
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-bg colour233
</span></code></pre><p>To theme the left status bar we are going to use variable like <code>client_prefix</code> and <code>#S</code>. Full list is available in <code>man tmux</code>.</p><p>Note the usage of Powerline characters. As long as your terminal font supports them, you can just copy and paste them into your configuration file.</p><p>To set colors, use <code>#[bg=COLORCODE,fg=COLORCODE]</code> before the characters you want to recolor.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-left-bg colour233
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-left-fg colour243
</span><span class="code-line line-number" line="3"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-left-length <span class="token number">40</span>
</span><span class="code-line line-number" line="4"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-left <span class="token string">&quot;#{?client_prefix,#[fg=colour255]#[bg=colour31]#[bold] #S #[fg=colour31]#[bg=colour233],#[fg=colour232]#[bg=colour255]#[bold] #S #[fg=colour255]#[bg=colour233]}&quot;</span>
</span></code></pre><p>Same thing for the right side of the status bar.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-right-bg colour233
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-right-fg colour243
</span><span class="code-line line-number" line="3"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-right-length <span class="token number">100</span>
</span><span class="code-line line-number" line="4"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> status-right <span class="token string">&quot;#{net_speed} #[fg=colour236,bg=colour233]#[fg=colour255,bg=colour236] %H:%M #[fg=colour233,bg=colour236]#[fg=colour255,bg=colour233,bold] #{session_attached} #[fg=colour255,bg=colour233]#[fg=colour233,bg=colour255]  #(whoami)@#[fg=colour232,bg=colour255,bold]#H &quot;</span>
</span></code></pre><p>Window status (middle part) contains the list of your active windows. Set the display format using <code>set -g window-status-format &quot; #I#F  #W &quot;</code>.</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> window-status-format <span class="token string">&quot;  #I#F  #W  &quot;</span>
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> window-status-current-format <span class="token string">&quot;#[fg=colour233,bg=colour31]#[fg=colour255,bg=colour31] #I#F  #W #[fg=colour31,bg=colour233,nobold]&quot;</span>
</span></code></pre><p>And for the currently active window:</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> window-status-current-bg colour39
</span><span class="code-line line-number" line="2"><span class="token builtin class-name">set</span> <span class="token parameter variable">-g</span> window-status-current-fg colour255
</span></code></pre><h2 id="result">Result</h2><div><img alt="Screenshot of the theme" src="/blog/creating-a-native-powerline-theme-for-tmux/theme-screenshot.png" width="1354" height="647" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAA2wAAANsAF9ZVn6AAAAIklEQVR4nGPY8uB9bMtkBhUjBiF5Bnm3IAYlPRWvyKLZKwFz7ggDN1BCPAAAAABJRU5ErkJggg=="/></div><p><a href="https://github.com/Vagr9K/dotfiles/blob/master/tmux/powerline.tmuxtheme">Full theme file</a>.</p><p>Feel free to check out my <a href="/blog/tmux-tips-and-tricks">Tmux tips and tricks</a>!</p>]]></content:encoded>
            <author>vagr9k@gmail.com (Ruben Harutyunyan)</author>
            <category>dotfiles</category>
            <enclosure url="https://callmeruben.com/blog/creating-a-native-powerline-theme-for-tmux/theme-screenshot.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[You need a fancy async prompt, and here's why]]></title>
            <link>https://callmeruben.com/blog/you-need-a-fancy-async-prompt-and-heres-why</link>
            <guid>you-need-a-fancy-async-prompt-and-heres-why</guid>
            <pubDate>Mon, 24 Jul 2017 00:00:00 GMT</pubDate>
            <description><![CDATA[Improving terminal DX using custom ZSH prompts.]]></description>
            <content:encoded><![CDATA[<p>Modern programming involves an abusive amount of terminal usage.</p><ul><li>Test changes? Terminal.</li><li>Launch server? Terminal.</li><li>Commit changes? Terminal.</li><li>Docker? Terminal.</li><li>Anything else? Probably terminal...</li><li>Using VIM? Always terminal.</li></ul><p>Now imagine the core tool of your workflow looking like this:</p><div><img alt="Default prompt" src="/blog/you-need-a-fancy-async-prompt-and-heres-why/prompt-nocolor.png" width="932" height="411" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAA2wAAANsAF9ZVn6AAAAEUlEQVR4nGNgYmJihAEGZAAAAUwAEA6AkrgAAAAASUVORK5CYII="/></div><p>Doesn&#x27;t seem very pleasant, does it?</p><h2 id="its-all-about-the-eye-candy">It&#x27;s all about the eye candy</h2><p>Communities like <a href="https://reddit.com/r/unixporn">/r/unixporn</a> (it&#x27;s SFW for the record) aim to solve that issue, by providing eye candy for our trusty terminals:</p><div><img alt="Prompt with eyecandy" src="/blog/you-need-a-fancy-async-prompt-and-heres-why/terminal-eyecandy.png" width="932" height="452" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAA2wAAANsAF9ZVn6AAAAIklEQVR4nGPo3HtCzjOUgV+OQUyZ4cT//8bx6QwCCuxmtgBuDQeaE5IWsAAAAABJRU5ErkJggg=="/></div><p>Looks much better, right? But we can&#x27;t stop now...</p><video src="/videos/more-eyecandy.mp4" controls="" loop="" preload="auto"></video><p>We need more statuses!</p><div><img alt="Even more eye candy" src="/blog/you-need-a-fancy-async-prompt-and-heres-why/even-more-eyecandy.png" width="1664" height="628" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAI0lEQVR4nGM48u1/686dbgX1Uw5fYCiYu5ZBXNk0Lju2cyoAu8AMesid/ogAAAAASUVORK5CYII="/></div><p>NOTE: Screenshots are from <a href="https://github.com/bhilburn/powerlevel9k/wiki/Show-Off-Your-Config">Powerlevel9K&#x27;s config showoff</a>.</p><p>Overall a much better experience, except for all the overhead you get from running <code>git status</code> and <code>nvm version</code> every single time you press <code>ENTER</code>.</p><p><a href="https://github.com/bhilburn/powerlevel9k/issues/232">Slow</a> <a href="https://github.com/bhilburn/powerlevel9k/issues/374">terminal</a> <a href="https://github.com/bhilburn/powerlevel9k/issues/132">response</a> <a href="https://github.com/bhilburn/powerlevel9k/issues/314">issues</a> <a href="https://github.com/bhilburn/powerlevel9k/issues/244">are</a> <a href="https://github.com/bhilburn/powerlevel9k/issues/287">really</a> <a href="https://github.com/robbyrussell/oh-my-zsh/issues/4116">common</a>, <a href="https://github.com/powerline/powerline/issues/1280">even</a> <a href="https://github.com/powerline/powerline/issues/1543">outside</a> <a href="https://github.com/powerline/powerline/issues/104">of</a> <a href="https://groups.google.com/forum/#!topic/powerline-support/7WCkP1HgPTc">Powerlevel9K</a>.</p><p>It&#x27;s not even about relatively fancy <code>nvm</code>/<code>rbenv</code> prompt segments. VCS segments add <em>a lot</em> of overhead in big repos.</p><p>This is how it looks like opening a Linux Kernel repo with most of its history deleted (only 14 commits):</p><video src="/videos/sync-prompt.mp4" controls="" loop="" preload="auto"></video><p>Granted, filesystem cache makes subsequent prompt redraws much faster, but there is still a 200-300ms delay that makes typing really annoying.</p><h2 id="async-will-help-us">Async will help us</h2><p>Here is the list of async prompts I managed to find (feel free to add yours in the comments):</p><ul><li><a href="https://github.com/sindresorhus/pure">Pure</a></li><li><a href="https://github.com/el1t/statusline">Statusline</a></li><li><a href="https://github.com/bhilburn/powerlevel9k/pull/344">Powerlevel9K&#x27;s WIP async branch</a></li></ul><p>NOTE: I&#x27;m going to use Powerlevel9K in this example purely because of aesthetics.</p><p>Instead of this in <code>antigernrc</code> (you can use whatever ZSH package manager you prefer):</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token variable assign-left">POWERLEVEL9K_INSTALLATION_PATH</span><span class="token operator">=</span><span class="token variable">$ANTIGEN_BUNDLES</span>/bhilburn/powerlevel9k
</span><span class="code-line line-number" line="2">antigen theme bhilburn/powerlevel9k powerlevel9k
</span></code></pre><p>We are going to use the WIP branch with zsh-async:</p><pre class="language-sh"><code class="language-sh code-highlight"><span class="code-line line-number" line="1"><span class="token comment">#Async library</span>
</span><span class="code-line line-number" line="2">antigen bundle mafredri/zsh-async
</span><span class="code-line line-number" line="3">
</span><span class="code-line line-number" line="4"><span class="token comment">#Theme</span>
</span><span class="code-line line-number" line="5"><span class="token variable assign-left">POWERLEVEL9K_INSTALLATION_PATH</span><span class="token operator">=</span><span class="token variable">$ANTIGEN_BUNDLES</span>/dritter/powerlevel9k-async_all_the_segments/powerlevel9k.zsh-theme
</span><span class="code-line line-number" line="6">antigen theme dritter/powerlevel9k powerlevel9k <span class="token variable parameter">--branch</span><span class="token operator">=</span>async_all_the_segments
</span></code></pre><p>Much better results!</p><video src="/videos/async-prompt.mp4" controls="" loop="" preload="auto"></video><p>As you might have noticed, VCS info isn&#x27;t blocking us from typing into the terminal and executing commands. That&#x27;s a huge usability improvement.</p><p>You can find my full zsh configuration <a href="https://github.com/Vagr9K/dotfiles/tree/master/zsh">here</a>.</p><p>Happy coding!</p>]]></content:encoded>
            <author>vagr9k@gmail.com (Ruben Harutyunyan)</author>
            <category>dotfiles</category>
            <enclosure url="https://callmeruben.com/blog/you-need-a-fancy-async-prompt-and-heres-why/zsh-prompt.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Using N-API for high performance voice command detection in Discord]]></title>
            <link>https://callmeruben.com/blog/using-n-api-for-high-performance-voice-command-detection-in-discord</link>
            <guid>using-n-api-for-high-performance-voice-command-detection-in-discord</guid>
            <pubDate>Sun, 28 Apr 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Have you ever wanted to just pronounce the song's name and let a Discord bot play it? Today we will make our own "Alexa" for Discord.]]></description>
            <content:encoded><![CDATA[<p>Have you ever wanted to just pronounce the song&#x27;s name and let a Discord bot play it?<br/>Today we will make our own &quot;Alexa&quot; for Discord.</p><h2 id="demo">Demo</h2><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameBorder="0" height="518" src="https://www.youtube.com/embed/vRwp--RoJdo?feature=oembed" title="VoiceBot Demo (with native-voice-command-detector)." width="920"></iframe><p>Why <code>Terminator</code>? It seems to be the most consistently detected hotword.<br/>Why <code>Mozart</code>? It&#x27;s not copyrighted.</p><h2 id="code">Code</h2><p>You can find the native module code in <a href="https://github.com/Vagr9K/native-voice-command-detector">Vagr9K/native-voice-command-detector</a>.<br/>The actual discord bot that utilizes this is located in <a href="https://github.com/Vagr9K/VoiceBot">Vagr9K/VoiceBot</a>.</p><h2 id="general-implementation-plan">General implementation plan</h2><p>First of all, let&#x27;s break down the idea into smaller pieces.</p><p>There are effectively two main parts of our application:</p><ul><li>The main bot code that will be responsible for:<ul><li>handling the Discord API</li><li>monitoring user actions for commands</li><li>responding to commands</li></ul></li><li>The voice command detection code</li></ul><p>This might initially seem strange since the voice command detection code is technically a submodule of the main code, but due to its complexity, it&#x27;s worth it to split the problem into these pieces.</p><h2 id="implementing-the-main-code">Implementing the main code</h2><p>The main code gets easily handled by a NodeJS based application.</p><p>Libraries like <a href="https://discord.js.org">discord.js</a> and <a href="https://github.com/fent/node-ytdl">ytdl</a> provide easy to use tooling to solve most of our problems.<br/>Add <a href="https://www.typescriptlang.org/">TypeScript</a> on top to get static typing, compile time checks and very convenient autocomplete based on the definitions of the aforementioned modules and you&#x27;re set.</p><p>The npm ecosystem greatly decreases the development time while also lowering the entry bar for people with small programming experience, making it perfect for the audience of Discord.</p><p>From the performance perspective these tasks aren&#x27;t CPU intensive and mostly wait for the network, meaning that the <a href="https://nodejs.org/de/docs/guides/event-loop-timers-and-nexttick/">NodeJS event loop</a> can handle them effectively without any extra optimizations being needed.</p><h2 id="implementing-the-voice-command-detection">Implementing the voice command detection</h2><p>The naive implementation would consist of us piping the entire audio stream into a speech to text service and parsing the text.<br/>Due to privacy concerns and pricing of these services that&#x27;s not ethically/economically practical.</p><p>It&#x27;s reasonable to expect that voice commands will be less than 1% of the audio stream data, making the so called &quot;hotword detection&quot; a huge improvement in both regards.<br/>After we&#x27;ve detected the hotword we only need to send a chunk of audio data for speech to text and later command extraction.</p><p>Keep in mind that the data that we will be receiving is a stream, meaning that after a hotword is detected we will have to redirect the next X seconds of data into a buffer that will be later sent to a speech recognition service.</p><p>It&#x27;s also worth noting that if a user pronounces another hotword while the previous one was being buffered, we need to create a new buffer for the new command and submit the old one for processing.</p><p>The next issue is that the hotword detector is buffering the data on its end too and we&#x27;ll have to submit the leftover data after the hotword detection to the command speech buffer to make sure that no command speech segments are lost.</p><p>This gives us the general logic of the application (data flow is from top to bottom):</p><div><img alt="Application logic schema" src="/blog/using-n-api-for-high-performance-voice-command-detection-in-discord/main-logic.png" width="768" height="626" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAL0lEQVR4nGNgAIMvn8/+f3WC4f+Lg3effP3/4ez/fycZ/n84+P/zlf/PTv97fA0Auy0bIxHP+voAAAAASUVORK5CYII="/></div><h3 id="picking-the-correct-libraries">Picking the correct libraries</h3><p>Let&#x27;s start filling the blanks.</p><p>For Hotword detection <a href="https://github.com/Picovoice/Porcupine">Porcupine</a> seems to be the best bet.<br/>This is due to:</p><ul><li>Relatively permissive license</li><li>Multitude of available ports</li><li>High performance</li><li>Hotword models can be generated on the fly (but can&#x27;t be used commercially)</li></ul><p>Ideally, <a href="https://github.com/MycroftAI/mycroft-precise">Precise</a> would&#x27;ve been used, but it&#x27;s Python only making things complicated, since it can&#x27;t be as easily ported to NodeJS.</p><p>It&#x27;s really hard to compare the speech to text services, but judging by the pricing and general feedback the <a href="https://cloud.google.com/speech-to-text/">GCloud Speech To Text</a> is a safe choice.</p><h3 id="details-details-even-more-details">Details, details, even more details</h3><p>Here things get extra complicated.</p><p>First of all, we receive the audio data from Discord as RAW <a href="https://opus-codec.org/">Opus</a> frames. Opus is a highly optimized audio codec that manages to provide high quality audio even at the lowest bitrates, making it optimal for network based voice communication.</p><div><img alt="Codec quality comparison graph" src="/blog/using-n-api-for-high-performance-voice-command-detection-in-discord/opus-quality.svg" width="1010" height="750" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAI0lEQVR4nGNgAIPFOf//H/8PYv1f9//adBhHTTxLnl8ByAIAIKcNpi98GAoAAAAASUVORK5CYII="/></div><p>Since the supported format for Porcupine is the signed 16 bit low endian PCM (single channel, 16K frequency), we need to first decode the data.<br/>GCloud in its own turn supports S16LE, but due to network payload size concerns, it makes sense to first compress the data via encoding. As we&#x27;ve established before Opus provides the best size/quality and is supported as <a href="https://cloud.google.com/speech-to-text/docs/reference/rest/v1/RecognitionConfig#AudioEncoding">OggOpus on GCloud</a>. The encoded audio data will also need to be <code>Base64</code> encoded and sent as a <code>JSON</code> payload by a <code>POST</code> request.</p><p>The updated diagram looks like this:</p><div><img alt="Full logic diagram" src="/blog/using-n-api-for-high-performance-voice-command-detection-in-discord/full-logic.png" width="768" height="716" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAP0lEQVR4nAE0AMv/AAAAANLi3dDi0M3NrAAAAABzo//y1sLv1rEA/9uv39bW9OHD7//kAP/vz/zqveK/puGuqS6tIsz7rbdSAAAAAElFTkSuQmCC"/></div><h3 id="designing-the-correct-architecture">Designing the correct architecture</h3><p>Since the logical segment is done, we need to dive into the architecture.</p><ul><li><p>Option #1: Just write NodeJS code.</p><p>This won&#x27;t scale reliably due to the fact that encoding/decoding/hotword detection are CPU intensive tasks. Once we start blocking the event loop <a href="https://nodejs.org/de/docs/guides/dont-block-the-event-loop/">the performance of NodeJS will degrade very quickly</a>. We won&#x27;t be able to support multiple people speaking in real-time and massive delays will be ensured. Combine this with the fact that NodeJS is single threaded and the processing load can&#x27;t be distributed between multiple logical CPU cores either.</p><p>Unfortunately, in their current state the <a href="https://nodejs.org/api/worker_threads.html">worker threads</a> are limited due to object serialization/deserialization restrictions. Namely when it comes to passing functions to worker threads.</p></li><li><p>Option #2: Rely on native encoding/decoding libraries.</p><p>Although the encoding/decoding gets sped up massively by using native modules like <a href="https://github.com/Rantanen/node-opus">node-opus</a>, we face the issue of <code>N-API -&gt; JS</code> and <code>JS -&gt; N-API</code> function call and argument copying overhead. This will mostly become a problem with the huge buffers. A couple seconds of speech is around 500+ 16bit Opus frames which decode into 160K+ 16 bit PCM frames (~312KB of data). Combine it with each frame having to be submitted by invoking a native function (extra overhead for each call) things don&#x27;t look as pretty. On top of that we&#x27;re still limited to a single thread.</p></li><li><p>Option N3: Submit all data to a native module that will process the rest.</p><p>This way we don&#x27;t deal with the overhead and have a full control over how and in which thread our code executes. Upon finishing the processing we will invoke a callback with the full text to keep the N-API overhead at a minimum.</p></li></ul><h3 id="designing-the-parallelization">Designing the parallelization</h3><p>First of all, let&#x27;s break down our app to <em>concurrent</em> tasks to see which actions can be parallelized (notice the green rectangles):</p><div><img alt="Parallelization opportunities" src="/blog/using-n-api-for-high-performance-voice-command-detection-in-discord/workers.png" width="768" height="771" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAP0lEQVR4nAE0AMv/AAAAAJTHgYa/XX29JwC6lHAvgtfEyI7W0ZcA9Nyj4tTR4teypNF6AP//Fv/Zr7+yb62ycsAsHh6tYqALAAAAAElFTkSuQmCC"/></div><p>This gives us a good idea over which tasks can be assigned to worker threads. The issue is that we&#x27;re dealing with streams. Processing the buffers only when a new frame enters the module is problematic since a user may pronounce his command and stay silent for the rest of the command&#x27;s time limit, effectively hanging the system.</p><p>This means that our logic processing needs to be handled on periodic intervals by a separate lightweight thread, while worker threads execute the CPU intensive code.</p><p>This brings us to the final design:</p><div><img alt="Full threading design" src="/blog/using-n-api-for-high-performance-voice-command-detection-in-discord/full-implementation.png" width="1061" height="1371" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAECAIAAADETxJQAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAMUlEQVR4nGNYXzU5Lji9sCCU4ff5x4VtaQziDAz/P/yZtKWbQYaB4dXxi80LKswzBAFtVBHR25OyLAAAAABJRU5ErkJggg=="/></div><p>The <code>VoiceProcessor</code> instance holds all the buffers and their states. Each audio stream (Discord user) gets his own instance.</p><p>The main NodeJS thread which originally invokes the native code is only used for submitting the Opus frames to the VoiceProcessor instance buffer. No processing is done during the submission which keeps the event loop blocked for the minimal amount of time.</p><p>The periodic timer thread periodically calls the <code>Sync</code> function on each <code>VoiceProcessor</code> instance to perform the state processing on them. This ensures consistent latency irrelevant of the Opus frame submission rate. The main NodeJS thread is also unloaded and doesn&#x27;t have to perform these actions.</p><p>Based on the timers and buffer states of the <code>VoiceProcessor</code> Opus decoding, hotword detection and speech recognition are being invoked in separate worker threads to ensure maximum possible parallelization of work on all the available logical CPU cores.</p><h2 id="benchmark">Benchmark</h2><p>The benchmark was executed as follows:</p><ul><li>Average command text callback delay is measured</li><li>Prerecorded data is submitted</li><li>It&#x27;s submitted instantly and assigned different IDs each time to simulate multiple concurrent users</li><li>Silence TTL is 500ms, buffer TTL is 200ms, thus making the lowest possible callback time ~900ms</li><li>GCloud network requests are skipped due to inconsistency they introduce</li><li>Concurrent user count configurations are 1/10/50/100/150/200/250/300/350/400/450/500</li><li>Worker thread configurations are 2th/4th/8th/16th</li><li>The benchmark was run on a Ryzen 1800x (8c/16th, stock, 3200Mhz CL14 RAM)</li></ul><p>Results:</p><table><thead><tr><th>Time/Count</th><th>32th</th><th>16th</th><th>8th</th><th>4th</th><th>2th</th></tr></thead><tbody><tr><td>1</td><td>915</td><td>925.3333333</td><td>889.6666667</td><td>924</td><td>915.6666667</td></tr><tr><td>10</td><td>927.9</td><td>922</td><td>950.5333333</td><td>904.5666667</td><td>958.5666667</td></tr><tr><td>50</td><td>983.96</td><td>994</td><td>961.0266667</td><td>1140.52</td><td>1334.426667</td></tr><tr><td>100</td><td>1035.42</td><td>984.8533333</td><td>1052.04</td><td>1381.716667</td><td>2151.346667</td></tr><tr><td>150</td><td>1129.208889</td><td>1097.468889</td><td>1185.746667</td><td>1744.268889</td><td>3270.86</td></tr><tr><td>200</td><td>1246.273333</td><td>1231.196667</td><td>1340.586667</td><td>2249.508333</td><td>4223.388333</td></tr><tr><td>250</td><td>1370.210667</td><td>1205.458667</td><td>1608.093333</td><td>2750.345333</td><td>5197.790667</td></tr><tr><td>300</td><td>1596.752222</td><td>1365.345556</td><td>1772.262222</td><td>3313.592222</td><td>6191.073333</td></tr><tr><td>350</td><td>1475.487619</td><td>1566.992381</td><td>1944.973333</td><td>3838.128571</td><td>7174.192381</td></tr><tr><td>400</td><td>1502.828333</td><td>1592.363333</td><td>2224.576667</td><td>4330.573333</td><td>8285.690833</td></tr><tr><td>450</td><td>1745.642963</td><td>1816.846667</td><td>2516.979259</td><td>4854.128148</td><td>9094.977037</td></tr><tr><td>500</td><td>1987.492667</td><td>2028.574667</td><td>2585.128</td><td>5258.944667</td><td>10008.06133</td></tr></tbody></table><p>Visualized (callback response time is pictured in relation to concurrent users, lower is better):</p><div><img alt="Performance scaling with CPU core count" src="/blog/using-n-api-for-high-performance-voice-command-detection-in-discord/threads.png" width="1085" height="649" blurDataUrl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAI0lEQVR4nGM4d+7cl69fDxw4cPr0KYbcnCxlZUUNdXU3N1cA13AM2vcZ13QAAAAASUVORK5CYII="/></div><p>As expected, 32 worker threads on a 16 logical core system perform the same as 16 workers.</p><p>There is a linear scaling with the worker count from 2 to 4, and from 4 to 8 workers.</p><p>Jumping from 8 workers to 16 provides ~23% performance improvement which is in line with the general expectations from AMD&#x27;s SMT implementation.</p><p>Overall, the nature of the task allowed proper parallelization to be implemented. This is close to a best case scenario when it comes to multithreading.</p><p>It&#x27;s also worth noting that Discord enforces sharding after your bot connects to 2500 servers. From my observations it&#x27;s highly unlikely that more than 100 people will be concurrently talking on those 2500 servers. As you can see from the &lt;100 concurrent user tests, we have enough performance to run each shard&#x27;s instance on a dual core CPU without any issues (ignoring NodeJS&#x27;s own processing needs).</p><h2 id="conclusion">Conclusion</h2><p>Voice command detection even in its simplest form is far from being easy. There are a lot of edge cases to consider and performance metrics to keep track of.</p><p>As of right now, this is still an alpha stage project, but it shows that using N-API to handle the performance critical segments while keeping the simplicity of NodeJS for the rest of the project, effectively combines the best of both worlds.</p><p>This has been my &quot;weekend project&quot; for quite a while. If you think I&#x27;ve missed something, feel free to point it out.</p>]]></content:encoded>
            <author>vagr9k@gmail.com (Ruben Harutyunyan)</author>
            <category>programming</category>
            <enclosure url="https://callmeruben.com/blog/using-n-api-for-high-performance-voice-command-detection-in-discord/napi-detector.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>