<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Peter Whittaker</title>
 <link href="http://peter-whittaker.comatom.xml" rel="self"/>
 <link href="http://peter-whittaker.com"/>
 <updated>2026-04-08T13:12:47+00:00</updated>
 <id>http://peter-whittaker.com</id>
 <author>
   <name>Peter Whittaker</name>
   <email></email>
 </author>

 
 <entry>
   <title>About time: Setting up ctags for vim and git</title>
   <link href="http://peter-whittaker.com/getting-ctags-working-with-vim-and-git"/>
   <updated>2026-01-08T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Setting-up-ctags-for-vim-and-git</id>
   <content type="html">&lt;p&gt;Professionally, I work pretty close to a bleeding edge (not &lt;em&gt;the&lt;/em&gt; bleeding edge, but it’s still hairy here):
Most of my job involves cross-domain work of one kind or another, and we prize reliability
and performance in equal measure - one dropped packet in 10 billion is too many, and
single-digit Gbps on 25Gbps interfaces is too slow, even with a complete protocol break
and physically guaranteed one-way transmission. Getting all those ducks lined up can be both
exhilarating and exhausting.&lt;/p&gt;

&lt;p&gt;Personally, though, I am both a creature of habit and slow to change. Most of the rest of the team
uses VSCode, and here I am happily trundling along with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt;. Why? Because it is ubiquitous and I
know it more than well enough to get my job done. Given how much I have to master for the job itself,
I am reluctant to change my tools, since they work. But I am jealous of my colleagues’ ability to
jump around their source files, from the use of a function, method, or structure to its definition&lt;/p&gt;

&lt;p&gt;So I have kept a tab group about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctags&lt;/code&gt; hanging around for &lt;em&gt;years&lt;/em&gt;. Among other things, this contained the following, all of which are referred to herein, listed in rough order of importance:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/universal-ctags/ctags&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt; GitHub project&lt;/a&gt; &lt;em&gt;tl;dr I installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctags&lt;/code&gt; from my clone of this repo&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://tbaggery.com/2011/08/08/effortless-ctags-with-git.html&quot;&gt;tpope’s excellent &lt;em&gt;but slightly out of date&lt;/em&gt; git hooks for ctags&lt;/a&gt; &lt;em&gt;I used these as the basis for my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitHooks&lt;/code&gt; script, below&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.ctags.io/en/latest/man/ctags.1.html&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt; man page&lt;/a&gt; &lt;em&gt;with working references to related man pages&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ctags.sourceforge.net&quot;&gt;the extraordinarily out-of-date &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exuberant-ctags&lt;/code&gt; project page&lt;/a&gt; &lt;em&gt;IMHO, not worth it, but YMMV&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://geekdude.github.io/tech/ctags/&quot;&gt;one I found while writing this post that aims but IMHO falls just short of covering ctags+git+vim at a “just get started” level&lt;/a&gt;, which I find lacking for a few reasons that make it a good example of what’s missing or out of date about a lot of available info:
    &lt;ul&gt;
      &lt;li&gt;it uses tpope’s work, extending it for a few cases, but overlooks a hook for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;it uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exuberant-ctags&lt;/code&gt; for reasons unclear to me&lt;/li&gt;
      &lt;li&gt;the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctags&lt;/code&gt; seems to blindly adopt options widely published elsewhere without justifying or explaining them&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, New Year, and all that, and yesterday I decided it was time. I dusted off those tabs, tried to make sense of them,
and ran into a wall.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exuberant&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal&lt;/code&gt;? (&lt;em&gt;tl;dr&lt;/em&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Which does my main development box have, and which version? (&lt;em&gt;best guess&lt;/em&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal&lt;/code&gt; 6.0.0)&lt;/li&gt;
  &lt;li&gt;No direct Rust support, eh? (&lt;em&gt;tl;dr&lt;/em&gt;: true for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exuberant&lt;/code&gt;, false for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal&lt;/code&gt;, the documentation is &lt;em&gt;waaaaay&lt;/em&gt; out of date)&lt;/li&gt;
  &lt;li&gt;How do I get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; to find tags file, given there are several places they could be? (&lt;em&gt;no spoilers&lt;/em&gt;, it turns out to be super easy)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically, I ran into the perfect intersection of several hacker problems:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Many maintainers and contributors do really, really well at writing code, &lt;em&gt;but write documentation for people who already know what they know&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Many users accept on authority what they have found elsewhere, and share it, sometimes verbatim, without really understanding the choices and motivation behind the original, and often very old, posts/documents/etm.&lt;/li&gt;
  &lt;li&gt;Some maintainers don’t understand why anyone would need anything spelled out, after all, &lt;em&gt;it’s all right there in the README!&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;The documentation in the main branch of some repos can be way behind what is actually on web sites, making &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RTFM&lt;/code&gt; - and building &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TFM&lt;/code&gt; in the first place - essentially pointless (so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip uninstall sphinx &amp;amp;&amp;amp; sudo dnf remove python3-pip&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, there was plenty of information that assumed you already knew what you were doing,
very little for the person trying to find their feet, and none of it &lt;em&gt;that&lt;/em&gt; accurate.&lt;/p&gt;

&lt;p&gt;Findings as I worked through this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exuberant-ctags&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;hasn’t been updated in over a decade, and&lt;/li&gt;
      &lt;li&gt;doesn’t support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rust&lt;/code&gt; directly, but only via extensive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regex&lt;/code&gt; patterns, which can be very, very slow (and good luck finding curated and up-to-date patterns)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt; is up-to-date and supports Rust and many other languages
    &lt;ul&gt;
      &lt;li&gt;the documentation included in the repo is, uh, poor, and, if you want to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RTFM&lt;/code&gt;, you will need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sphinx&lt;/code&gt;, but they don’t add much value&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exuberant-ctags&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt; have many options, with defaults settables in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctags&lt;/code&gt; file that can live in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ctags&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.config/ctags&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ctags.d/*&lt;/code&gt;, depending on which you are using and what you prefer, but…
    &lt;ul&gt;
      &lt;li&gt;I found that the default behaviour of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt;, without any such file, was sufficient for my main use-cases.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty-tags&lt;/code&gt; &lt;em&gt;may&lt;/em&gt; be useful, but it depends on your use-case:
    &lt;ul&gt;
      &lt;li&gt;if you have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt; and only need tags on &lt;em&gt;your&lt;/em&gt; code, that is, your project itself, &lt;em&gt;you probably don’t need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty-tags&lt;/code&gt;&lt;/em&gt;&lt;/li&gt;
      &lt;li&gt;if you have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exuberant-ctags&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty-tags&lt;/code&gt; provides the regexs to tag Rust source, but this &lt;em&gt;can&lt;/em&gt; be slow&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty-tags&lt;/code&gt; shines when you want tagging into your project’s dependencies, and into the Rust source itself
        &lt;ul&gt;
          &lt;li&gt;the maintainer chose to generate one tag file per project and per dependency, which can generate a lot of tag files, which, personally, I find cluttering and messy, but it does reduce the likelihood of naming conflicts&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set tags=.git/tags;,tags;&lt;/code&gt; to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.vimrc&lt;/code&gt; was enough to satisfy my two primary use cases:
    &lt;ul&gt;
      &lt;li&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;&lt;/code&gt; tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; to start in current directory and recurse upwards until it finds a matching file, e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git/tags&lt;/code&gt; at the top of your repo&lt;/li&gt;
      &lt;li&gt;I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; hooks, à la tpope, to generate and maintain tags&lt;/li&gt;
      &lt;li&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tags;&lt;/code&gt; pattern will match tags I generate &lt;em&gt;ad hoc&lt;/em&gt; outside of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; (provided I specify -ftags when generating tags; cf “existing tags file”, below)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;tpope’s well-known &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; hooks for tagging seem a little out of date: there was no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post-switch&lt;/code&gt; hook, e.g.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided to&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;build and install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt; from source,&lt;/li&gt;
  &lt;li&gt;skip its documentation,&lt;/li&gt;
  &lt;li&gt;adapt tpope’s git hooks, with some updates, creating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitHooks&lt;/code&gt; script I can run on any development machine to set things up for me,&lt;/li&gt;
  &lt;li&gt;keep my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.vimrc&lt;/code&gt; simple,&lt;/li&gt;
  &lt;li&gt;skip &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty-tags&lt;/code&gt; for now, as it doesn’t add value for me, at least not yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;building-universal-ctags&quot;&gt;Building &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;universal-ctags&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/universal-ctags/ctags.git
cd ctags
./autogen.sh
./configure
make &amp;amp;&amp;amp; sudo make install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin/ctags&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin&lt;/code&gt; was already in my $PATH&lt;/p&gt;

&lt;h3 id=&quot;my-vimrc&quot;&gt;My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.vimrc&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;As noted above, getting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; to find tag files was, for me, as simple as&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo &apos;set tags=.git/tags;,tags;&apos; &amp;gt;&amp;gt; ~/.vimrc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; to search for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ctags&lt;/code&gt; file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git/tags&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tags&lt;/code&gt; starting in the current directory and searching all parent directories until it finds one. If you change the name of the ctags file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; hooks in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitHooks&lt;/code&gt; (cf “existing tags file”, below), change or add it here or in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./vimrc&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;If you run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitHooks&lt;/code&gt; script and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git ic&lt;/code&gt; in your repository, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git/tags&lt;/code&gt; file will exist in your repository and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; when find it whenever you open a repo file with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;;&lt;/code&gt; syntax is explained by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:help file-searching&lt;/code&gt; info.&lt;/p&gt;

&lt;h3 id=&quot;adopting-tpopes-git-hooks&quot;&gt;Adopting tpope’s git hooks&lt;/h3&gt;
&lt;p&gt;For this, I wrote a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitHooks&lt;/code&gt; script:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

if ! command -v ctags 2&amp;gt;&amp;amp;1 /dev/null; then
    &amp;gt;&amp;amp;2 echo &quot;No ctags, exiting; consider installing&quot;
    exit 1
else
    &amp;gt;&amp;amp;2 echo &quot;Proceeding with ctags setup&quot;
fi

# idempotent
# shellcheck disable=SC2088 # we want the exact string
git config --global init.templatedir &apos;~/.git_template&apos;
git config --global alias.tags &apos;!git hook run ctags&apos;
git config --global alias.ic &apos;!git init &amp;amp;&amp;amp; git tags&apos;

HOOKDIR=~/.git_template/hooks
mkdir -p &quot;$HOOKDIR&quot;
CTAGS=&quot;$HOOKDIR/ctags&quot;
POST=&quot;$HOOKDIR/post-&quot;

cat &amp;lt;&amp;lt; &apos;EOF&apos; &amp;gt; &quot;$CTAGS&quot;
#!/usr/bin/env bash
set -e
if ! command -v ctags &amp;amp;&amp;gt; /dev/null; then
    &amp;gt;&amp;amp;2 echo &quot;No ctags, exiting; consider installing&quot;
    exit 0 # exit clean so that git commands reflect proper status (maybe)
fi
dir=&quot;`git rev-parse --git-dir`&quot;
trap &apos;rm -f &quot;$dir/tags.$$&quot;&apos; EXIT INT TERM
# process all tracked files in the repository (git ls-files),
# reading tracked file names from stdin (-L -), creating a
# temporary tags file in the repo&apos;s .git dir (-f$dir/tags.$$),
# with tags relative to the .git dir (--tag-relative=yes makes
# the tags relative to the directory containing the tags file).
git ls-files|ctags --tag-relative=yes -L - -f$dir/tags.$$
# rename the temporary file to &quot;tags&quot;, replacing any existing tags file
mv $dir/tags.$$ $dir/tags
EOF
chmod +x &quot;$CTAGS&quot;

# create hooks for the following git actions
declare -a hookLinks=()
hookLinks+=(applypatch)
hookLinks+=(checkout)
hookLinks+=(commit)
hookLinks+=(merge)
hookLinks+=(rewrite)
hookLinks+=(switch)

for hookLink in &quot;${hookLinks[@]}&quot;; do
    hookFile=&quot;$POST${hookLink}&quot;
    # run all commands in sub-statement in order to redirect just once
    {
        echo &apos;#!/usr/bin/env bash&apos;
        if [[ $hookLink == rewrite ]]; then
            # special case
            # shellcheck disable=SC2016
            echo &apos;[[ $1 == rebase ]] &amp;amp;&amp;amp; exec .git/hooks/post-merge&apos;
        else
            # general case
            echo &apos;.git/hooks/ctags &amp;amp;&amp;gt; /dev/null &amp;amp;&apos;
        fi
    } &amp;gt; &quot;${hookFile}&quot;
    chmod +x &quot;${hookFile}&quot;
done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gitHooks&lt;/code&gt; in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/bin&lt;/code&gt; directory, setting up and maintaining tags in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; repository was simple:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# set up the hooks and aliases I wanted
gitHooks
cd aGitRepo
# use the `ic` alias to run git init, which picks up the templates
# defined by `gitHooks`, and git tags, which generates tags in .git
git ic
cd ../anotherRepo
git ic   # ditto
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That was enough to satisfy my major use-case, using tags when working in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; repositories with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Obligatory "What I wish I knew about git" post</title>
   <link href="http://peter-whittaker.com/obligatory-grokking-git-post"/>
   <updated>2020-12-30T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/obligatory-grokking-git-post</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; &lt;em&gt;If you think of&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch&lt;/code&gt;&lt;em&gt;as a verb meaning&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diverge from this path, split my tree&lt;/code&gt;&lt;em&gt;and think of the branch name as a label that tracks the most recent commit on this new path, a lot of stuff about git becomes clearer. You might already understand what&lt;/em&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;detached HEAD&lt;/code&gt;&lt;em&gt;means&lt;/em&gt;.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Koan1: If I am on a branch but not at a branch, I am detached.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Koan2: I can always get where I need to go, if I am committed.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;“Aha!”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Epiphanies. Eureka moments. Call them what you will, they change our world: We perceive and understand things differently. Some come after difficult, tortured paths, others from seemingly nowhere. Some dawn slowly, some disorientingly suddenly. I especially love the slow dawns, when I can feel the red pill soaking in, feel the change of perspective, feel the new world.&lt;/p&gt;

&lt;p&gt;The challenge comes when you want to explain the new perspective to those who aren’t there yet – especially when you took the difficult, tortured path: it can be tempting to recite your journey, in the hope that others will be able to follow your footsteps.&lt;/p&gt;

&lt;p&gt;It’s not doomed to failure, just verbose, not to mention potentially inaccessible or irrelevant to those starting from different places: the paths of others need diverge only slightly from our own for the intersection of shared experiences to be very small indeed.&lt;/p&gt;

&lt;p&gt;Besides, the one thing that you cannot explain is the experience of the red pill itself: Unless and until someone else takes it, they won’t get what that part of the trip was like. (I didn’t intend the pun, but I’m happy with it :-&amp;gt;)&lt;/p&gt;

&lt;p&gt;So rather than explain how I came to git, or how I used git badly (as I now perceive it), or reference websites and tutorials and blogs and practices, all in an attempt to share pre-flash headspace, allow me one – or maybe two or three – noble lies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lie #1&lt;/strong&gt;: &lt;em&gt;You don’t work&lt;/em&gt; ON &lt;em&gt;a branch, you work&lt;/em&gt; AT &lt;em&gt;a branch&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Once you understand what I mean by that, you may think, as I do now, that ON Vs AT is mostly irrelevant, that ON is perhaps slightly more conceptually accurate from a code management perspective, but that AT is more useful when thinking about references in general, about what various commands do and how, and about how to solve various problems.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aside: As soon as you understand Lie #1, you will be able to infer what a&lt;/em&gt; detached head &lt;em&gt;is and you will know that you do not need to use branches or the stash to work with them. Both (the nature of detachment, the commit-only solution) will literally just come to you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lie #2&lt;/strong&gt;: The only difference between a branch and a tag is persistence, or perhaps more accurately, mobility: Branches are &lt;em&gt;mobile&lt;/em&gt;, tags are &lt;em&gt;sessile&lt;/em&gt;. Git itself moves branches for you, and git itself keeps tags in place, unless you force it to move them.&lt;/p&gt;

&lt;p&gt;This one is definitely more of a lie, but once you realize why you can pretty much use a tag anywhere you can a branch, and vice versa, you’ll understand how accurate it is. At least from a perspective very much related to &lt;strong&gt;Lie #1&lt;/strong&gt;. (And&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit-ish&lt;/code&gt;will start to make more sense, too.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lie #3&lt;/strong&gt;: We could just as well call&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;&lt;em&gt;fred&lt;/em&gt; or &lt;em&gt;default&lt;/em&gt; or &lt;em&gt;bootStrap&lt;/em&gt; or &lt;em&gt;justGetStarted&lt;/em&gt; and it would mean exactly the same thing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;bootStrap&lt;/em&gt; and &lt;em&gt;justGetStarted&lt;/em&gt; are actually pretty good names, when you consider the &lt;em&gt;first&lt;/em&gt; problem that the branch concept solves. More on this below.&lt;/p&gt;

&lt;p&gt;Usually at this point – or far earlier – most articles have brought up&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directed graphs&lt;/code&gt;and other matters mathematical, which I avoid herein not because I do not understand them but because they aren’t strictly necessary to starting up the ladder or taking the first step on or  &lt;em&gt;insertFavouriteMetaphorHere&lt;/em&gt;  and all I really hope to do is get you started.&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;Words of the inner pedant&lt;/em&gt;: git mastery requires understanding the nature of git’s&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directed acyclic graph&lt;/code&gt;and how simply signing the root commit can mark an entire tree as “trusted”. This article is not about mastery.)&lt;/p&gt;

&lt;p&gt;Think of source code management at one its most fundamental levels: A way to checkpoint your work, easily and cleanly, so that you revert to your last known good state when something goes wrong. Start with just that.&lt;/p&gt;

&lt;p&gt;The&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit&lt;/code&gt;action creates that checkpoint. You’ve probably seen in logs and man pages and blogs, etc., that each commit has a unique ID – and you’ve probably seen that these IDs are lengthy apparently random strings (they actually are pretty close to random, that’s one of their cryptographic properties, but that is well beyond the scope of this post).&lt;/p&gt;

&lt;p&gt;Your next commit creates another checkpoint, with its own long random ID. That first commit is essentially an ancestor of the second one, since the second one built on your work in the first one. Just a bit of foreshadowing.&lt;/p&gt;

&lt;p&gt;If you had to keep track of where you were, of the relationship between these commits, git would lose a lot of its value – so it keeps track for you – and it does this in two distinct ways.&lt;/p&gt;

&lt;p&gt;The first is with the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;concept.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;is simply where you are working right now. If you only ever go forward, commit after commit after commit,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;will always refer to your last commit. You will never have to “solve” the “problem” of working on a&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;detached HEAD.&lt;/code&gt;In fact, you will never have to know about&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;or&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEADs...&lt;/code&gt;but after reading this, hopefully it will just make sense to you.&lt;/p&gt;

&lt;p&gt;The second way git helps you keep track of where you are is with the branch concept. Chances are you are familiar with it, but just in case….&lt;/p&gt;

&lt;p&gt;Let’s say you have been working for a while, commit after commit, and are happy with your work: it works as you hoped, it is stable, it is secure, etc. Your next step is a problem you aren’t sure how to solve, but you have a few ideas and want to experiment with them – git helps you with branches.&lt;/p&gt;

&lt;p&gt;In essence, a branch gives you a “place away”, a sandbox, where you can experiment based on your good work without breaking it.&lt;/p&gt;

&lt;p&gt;You create a branch for each approach –&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch approach01&lt;/code&gt;or&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git checkout -b approach01&lt;/code&gt;or&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git switch...&lt;/code&gt;– start coding, make some commits, compare, contrast, decide on the approach that works best. Merging that best approach and carrying on is the next step.&lt;/p&gt;

&lt;p&gt;But before we take that step, let’s come back to&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;and to how git helps you keep track of where you are.&lt;/p&gt;

&lt;p&gt;Now that you have multiple branches, you have multiple&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEADs:&lt;/code&gt;There is a&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;on each branch, and each branch’s&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;is the most recent commit on that branch. Think of a tree, coming up from the ground, tall and strong, and at some point splitting, growing two or more branches from the trunk. Each branch ends in its own&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD.&lt;/code&gt;It’s easy to refer to the tree or to the trunk, it’s the bulk of stuff that happened before you made your first branch. The branches start where things split from the trunk.&lt;/p&gt;

&lt;p&gt;Which is the main part of the tree now? Well. That’s actually a deeply philosophical question, but for most of us the answer is&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master:&lt;/code&gt;git knows you are likely to branch off at some point, so it simply makes sure that your work, even if perfectly linear, commit after commit without branching, has a branch name, and that name is master.&lt;/p&gt;

&lt;p&gt;Whether&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;is a master in any semantic sense is entirely up to you. This is just the name that git gives the shoot that grows as you commit. Like I wrote above, it could just as easily be called &lt;em&gt;bootStrap&lt;/em&gt; or &lt;em&gt;justGetStarted&lt;/em&gt; or &lt;em&gt;myStuff&lt;/em&gt; or &lt;em&gt;shoot&lt;/em&gt;: Unless you need to come back to it, you never need to know what it was called and you only need to come back to it if you create a branch. There are git workflows based on the idea of making master “special”, making it mean something (we’ve just started doing this in my main job: master is our &lt;em&gt;generally available public release&lt;/em&gt; branch, with other branches being for feature development, penetration testing, etc.).&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;Lie #3&lt;/strong&gt;: Master is only as special as you make it, and, if you never branch, you never need it. Git just made sure it was there because chances are that you would need it, at some point. We’ll come back to &lt;strong&gt;Lie #3&lt;/strong&gt;, because &lt;strong&gt;Lie #1&lt;/strong&gt; also applies.&lt;/p&gt;

&lt;p&gt;Getting back to our tree: if you look at the tree, and consider your solid, secure, satisfying work to be the trunk, right up to where you started branching for experimental purposes, where does master end and the branches begin?&lt;/p&gt;

&lt;p&gt;Time to consider&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;branch&lt;/code&gt;not as a &lt;em&gt;noun&lt;/em&gt;, but as a &lt;em&gt;verb&lt;/em&gt;. Some git commands are pretty verb-like, e.g.,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;, while others are pretty noun-like, e.g.,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git log&lt;/code&gt;. Until very recently, I always thought of&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch&lt;/code&gt;as a noun-like command, as in &lt;em&gt;make a branch&lt;/em&gt;, but in a lot of ways, it is far better to think of it as a verb: branch to this new place. Diverge. Blaze new path.&lt;/p&gt;

&lt;p&gt;You started with a shoot that grew straight and tall then got to a point where you needed to experiment, so you created a branch, to “work off to the side”. Then you created another one, because you weren’t sure about that path. Repeat ad infinitum, or at least ad bonum or ad satium, i.e., until you have a good enough branch.&lt;/p&gt;

&lt;p&gt;Right now, you’ve got&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;, which refers to the shoot, and master’s&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD,&lt;/code&gt;which is the last commit on master, and several branches, each diverging from&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master,&lt;/code&gt;and each of their&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEADs,&lt;/code&gt;which are the leaves, if you will, on each branch.&lt;/p&gt;

&lt;p&gt;Right?&lt;/p&gt;

&lt;p&gt;Well, yeah, OK, that’s how a lot of people view these branch things.&lt;/p&gt;

&lt;p&gt;For the sake of this article, for the sake of pedagogy, please try this: Think of master as a human friendly, human-consumable label for the last commit you made before branching. In other words, on master,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;and&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;refer to the same commit.&lt;/p&gt;

&lt;p&gt;Now do the same for each branch: Consider the branch name, e.g.,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;approach01,&lt;/code&gt;not to apply to the entire branch, but to refer to the last, most recent commit on that branch. In other words, on any branch, including master,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;and the &lt;em&gt;branch name&lt;/em&gt; refer to exactly the same thing, the most recent commit.&lt;/p&gt;

&lt;p&gt;Every time you commit, git moves&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;to point to the new commit. And every time you commit, git moves &lt;em&gt;branch name&lt;/em&gt; to point to that same commit.&lt;/p&gt;

&lt;p&gt;Why is this so important? When you checkout a branch, even master, git doesn’t place you just anywhere on that branch: It places you at the end of that branch, where &lt;em&gt;end&lt;/em&gt; means the last commit made on that branch.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;Lie #1&lt;/strong&gt;: When you start working on a branch, you start at a very specific place – the end of the branch. And the end keeps moving: each time you make a commit, the branch grows, and git updates location references for you so that you are always at the end, which is almost always where you want to be.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;Lie #1&lt;/strong&gt; says a branch is a moving reference, a label that refers to the most recent commit of a related set of commits.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;We might as well deal with &lt;strong&gt;Lie #2&lt;/strong&gt; right here. Tags are clearly labels. That is their whole function: To label a commit as having some semantic meaning, some importance to you or your team. The only idea of a tag is to mark a commit that you should always be able to come back to, easily.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;A tag is persistent, static, sessile reference, a label that never moves, a marker for an important spot. (Under the covers, full tags are one of the four types of git objects, along with blobs, trees, and commits; I use “full” to contrast with “lightweight” tags, which are just references.)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Of course, you don’t really need tags. You could capture the same information in a commit message, then read through the log, looking for it, get the commit ID, and git checkout that CommitID and there you’d be! But who has time for that?&lt;/p&gt;

&lt;p&gt;Git tag saves you that work, by allowing you to mark specific commits as special and to list those special commits with&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git tag -l.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To complete the lie, consider tags as labels that move you back in time, to previous somehow important commits, and branches as labels that move you forward in time, to the most recent commit. Git will never move a tag on you, because it has no way of knowing why you made that label: it just isn’t smart enough, it assumes the label meant something to you, and will only move it if you explicitly force it to. But git moves the end-of-branch label with every commit, because most of the time most of us want to be right there, at the end of the branch, at our most recent work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lie #2&lt;/strong&gt; is important because you can pretty much use a tag anywhere you can use a branch name, in&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff,&lt;/code&gt;in&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkout,&lt;/code&gt;in&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch,&lt;/code&gt;in&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge,&lt;/code&gt;in&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rebase,&lt;/code&gt;etc., etc., and vice versa. Branch and tag refer to specific commits in a tree of commits. A directed graph that starts with the first commit, the seed, and shoots upward until those first branches, and then along each one.&lt;/p&gt;

&lt;p&gt;If you like the tree analogy, we are about to blow it up. If you dislike it, this next bit is for you.&lt;/p&gt;

&lt;p&gt;So what’s a&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;detached HEAD?&lt;/code&gt;Consider that pretty much anywhere you can use a tag you can use a branch name and vice versa, and pretty much anywhere you can use either you can use a commit ID.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git checkout commitThatIsNeitherTaggedNorTheEndOfTheBranch&lt;/code&gt;will take you to a&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;detached HEAD:&lt;/code&gt;You reach a&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;detached HEAD&lt;/code&gt;when your current working commit is not otherwise labeled, that is, it has not been tagged and is not the last commit in/on a branch. It doesn’t matter how many descendants it has, it just isn’t the last commit on ANY branch and it isn’t tagged.&lt;/p&gt;

&lt;p&gt;If you change something there and make commit it, you will have “created a branch”, in that you will have diverged your tree, but you won’t really have created a branch, because you never labelled it as such. You’ve &lt;em&gt;diverged&lt;/em&gt; in the tree, in the directed graph, without &lt;em&gt;branching&lt;/em&gt;, as the idea is most often expressed.&lt;/p&gt;

&lt;p&gt;In other words, a detached&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;is git telling you that you didn’t follow the expected rules. But there aren’t really any rules, just conventions, and, if you understand how these commits and labels (these references) work, you can always get there from here or here from there. So call this &lt;strong&gt;Lie #4&lt;/strong&gt;: a&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;detached HEAD&lt;/code&gt;is git telling you to follow the rules. That don’t exist. Paradox if true. But false, so logically consistent. Right? Right.&lt;/p&gt;

&lt;p&gt;When you decide to go back in time or when you end up back in time and decide that you where you are is where you need to be, you make a change. Now you need to integrate that change into your later work. How?&lt;/p&gt;

&lt;p&gt;The traditional advice is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Commit.&lt;/li&gt;
  &lt;li&gt;Make a branch at that commit.&lt;/li&gt;
  &lt;li&gt;Jump to where you want that work (usually by checking out another branch and thereby moving to its end)&lt;/li&gt;
  &lt;li&gt;Merge the branch from step 2 into your current location.&lt;/li&gt;
  &lt;li&gt;Sometime later, delete the branch from #2, since you do not need it anymore.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But you don’t need to make a branch, which means you don’t need to clean it up, so steps 2 and 5 are irrelevant. You just need to:&lt;/p&gt;

&lt;p&gt;A. Commit – and note the commit ID&lt;br /&gt;
B. Jump to where you want that work&lt;br /&gt;
C. Merge that commit ID into your current location.&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;Huh. What does our tree look like now? The tree analogy just died because we reached into its trunk (the detached HEAD, that commit from the distant past) and we pulled it forward in time and married it to the end of one of the branches – or to the top of the trunk, if we merged it into master, and hadn’t yet merged anything else into master.&lt;/p&gt;

&lt;p&gt;Well, mostly died. Because we may have used&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rebase.&lt;/code&gt;If we used&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rebase,&lt;/code&gt;we just morphed the tree into a bigger tree. Insert favourite SciFi/SpaceOpera metaphor here.&lt;/p&gt;

&lt;p&gt;Think about starting our shoot and branching off into&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;approach01&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;approach02,&lt;/code&gt;so that we have two distinct branches heading to the sky from the trunk we are calling master, then realizing that we need both of these approaches, that they solve different problems. We need to combine them somehow. Most commonly, we would use merge, but we could also use rebase. What’s the diff?&lt;/p&gt;

&lt;p&gt;If we&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git checkout approach01
git merge approach02
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;we join our two branches so that we have a trunk and two limbs that split off (that diverge from master) only to rejoin, to converge, somewhere above the trunk. If we then&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git checkout master
git merge approach1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;suddenly&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;is at the top of the tree. The fact that we took two distinct paths to get to this new master, path&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;approach01&lt;/code&gt;and path&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;approach02,&lt;/code&gt;is preserved forever, reflected in our commit history. In fact, because&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt;gets pulled along to the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;approach1&lt;/code&gt;commit, you can picture the three paths as a trunk that separates into three branches with empty space between them only to have the three branches converge and start growing as a single branch again.&lt;/p&gt;

&lt;p&gt;But what if instead we had done&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git checkout approach01
git rebase approach02
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git checkout master
git rebase approach2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;???&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In this case&lt;/em&gt;,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase&lt;/code&gt;basically says &lt;em&gt;do not preserve the distinctiveness of these paths, make it seem as if there was but one path, which we mean to walk all along&lt;/em&gt;: The result of doing these rebase operations is that despite the fact that we branched – diverged – and wandered around design space for a time, so that our tree started to get twisty and possibly gnarled, it is now a single shoot, straight and true, from ground to sky (or whatever altitude master happens to be).&lt;/p&gt;

&lt;p&gt;So, yeah, the tree analogy is weak. But all analogies are.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;git commits are arranged as a directed graph, one that starts from the first commit and continues linearly, unless and until one branches, at which point the graph diverges. Unless and until one branches, each later commit has as its ancestors all earlier commits (each commit has one and only one ancestor commit, except the first one, which has none).&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Ancestry diverges when a branch occurs – but each commit still has only one ancestor commit, unless and until there is a merge.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git merge&lt;/code&gt;joins commits on two separate branches into a new commit, one with multiple ancestral paths: That commit has two or more ancestor commits, and, most of the time, those commits will have one or more common ancestors (not always, we won’t go there, but git does allow merging of completely unrelated trees).&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase&lt;/code&gt;joins commits on two separate branches into a new commit with but a single ancestral path, returning us to linearity, so each commit has but one ancestor commit, all the way back to the beginning of the branch (which might be the beginning of the tree).&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;so-pete-you-dont-use-branches-then&quot;&gt;So, Pete, you don’t use branches, then?&lt;/h4&gt;

&lt;p&gt;I absolutely do. There are excellent articles and man pages on git workflows, e.g., the kernel workflow, and those workflows are enabled by and depend upon the branch concept. I don’t think I could work with others – and even some of my weirder&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hmm, will this work&lt;/code&gt;ideas – without branches.&lt;/p&gt;

&lt;p&gt;It’s just that thinking about the branch as its HEAD and not as the whole limb helps me understand what git is doing for me and how we can help each other. At a branch, I am at the labelled end of series of a related commits. But I don’t need to be there, I can be anywhere &lt;em&gt;on&lt;/em&gt; the branch – as long as I am careful.&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;If I am on a branch but not at a branch, I am detached.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;(Another git koan.)&lt;/p&gt;

&lt;h4 id=&quot;what-about-remotes-then&quot;&gt;What about remotes, then?&lt;/h4&gt;

&lt;p&gt;All of these same concepts apply when working with remote branches, whether those are on your own on server or they are those of others, members of your team or others with whom you are collaborating. Remotes are simply pointers to other commit trees, other directed graphs, out in the world somewhere.&lt;/p&gt;

&lt;p&gt;What happens when you do a pull?&lt;/p&gt;

&lt;p&gt;Like the man page says,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;is basically&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git fetch&lt;/code&gt;followed by&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git merge&lt;/code&gt;FETCH_HEAD: Your&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;is your recent commit on your branch,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FETCH_HEAD&lt;/code&gt;is the most recent commit on the copy of the branch you just fetched, and&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;merges that commit and its ancestors onto your&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD,&lt;/code&gt;creating a new commit and moving&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEAD&lt;/code&gt;and the branch name to that commit.&lt;/p&gt;

&lt;p&gt;Note an important part buried in the previous paragraph: &lt;em&gt;the copy of the branch you just fetched&lt;/em&gt;. You aren’t working at or on the remote, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git fetch&lt;/code&gt; created a local copy of that other branch, and you work with that local copy. As is noted elsewhere, ultimately, all git is local, git “just” provides various commands for moving copies around so that we can work with them, together.&lt;/p&gt;

&lt;p&gt;Of course, as soon as you have remotes, you have the possibility of merge conflicts, since changes can be made to the same code in different places. Fortunately, git has tools for resolving merge conflicts: git developers recognize merge conflict resolution as a key “value add” for git, one of the main ways git makes distributed development so much easier, and they spend a lot of time and effort making remote access and fetching and merging work really well. Subjects for another session, perhaps.&lt;/p&gt;

&lt;p&gt;Since&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;is basically&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git fetch&lt;/code&gt;and&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git merge FETCH_HEAD,&lt;/code&gt;what if you did&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git fetch&lt;/code&gt;and&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase FETCH_HEAD&lt;/code&gt;instead?&lt;/p&gt;

&lt;p&gt;You know how I said there weren’t any rules, just conventions? Yeah. Conventions can be very important. If it’s your sandbox, you may do as you please, set the rules for others, if you want them to play with you. If your rule is rebase not merge, well, it’s your sandbox.&lt;/p&gt;

&lt;p&gt;Most sandboxes don’t work that way, though: Merge and rebase preserve all commits, which means that both preserve a history, it’s just rebase doesn’t preserve how the history happened, only the events, in sort of the right order but not really. Merge does.&lt;/p&gt;

&lt;p&gt;For most projects, the actually history of the history is important because understanding how we got here from there can often help us better understand the nature of the work, the nature of the problems we are trying to solve, the nature of solution space. Rebase hides or obscures potentially important exploration.&lt;/p&gt;

&lt;p&gt;You are free to rebase. Especially your own code, in your sandbox. But don’t be surprised if others are inclined to a dim view of those who would rebase the work of others and discard potentially important history, or at least obscure it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My preferred take on&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rebase&lt;/code&gt;Vs&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Rebase&lt;/td&gt;
      &lt;td&gt;Use&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase someOtherCode&lt;/code&gt;to bring that other code into your &lt;strong&gt;unshared branch&lt;/strong&gt;, e.g., a feature you are developing, to make sure that your code integrates well with what others have done since you started.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Merge&lt;/td&gt;
      &lt;td&gt;Use&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git merge ...&lt;/code&gt;to integrate two branches, especially &lt;strong&gt;shared&lt;/strong&gt; branches&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Or, to put it more simply:&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Private, never-before-shared code.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git merge&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Anything published, shared, or otherwise in two or more places, all of which are &lt;strong&gt;not&lt;/strong&gt; under your &lt;strong&gt;direct&lt;/strong&gt; control&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Or, even more simply:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;What has been seen should never be rebased.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Violate that only when you and your collaborators truly understand why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With all that in mind, is a remote a branch, is the same way that a branch is a branch?&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;No. While they can be listed with&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch -[ar],&lt;/code&gt;and while you can use&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch someName&lt;/code&gt;to make a local branch that tracks a remote called&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;someName&lt;/code&gt;&lt;em&gt;just appear&lt;/em&gt;, they are not branches.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Branches are places you can go. At least from the perspective of this article.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;You may work with remotes, but you can never get to them. You fetch or pull from them, you push to them, but you can never be there. You can never be on them or at them.&lt;/p&gt;

&lt;p&gt;This is very different from tags, branches, and even commitIDs, which represent places in commit space you can actually get to. Think of&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git checkout&lt;/code&gt;and&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git switch&lt;/code&gt;as a bit like the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mv&lt;/code&gt;command, which takes you from where you are in the filesystem to a new place in the filesystem: checkout and switch take you to named places in the same way that mv does.&lt;/p&gt;

&lt;p&gt;Being the master of the bad analogy (have you read my tree work?), git remotes are not like remote filesystems that can be mounted via NFS or SMB and navigated directly as if they were local. They are more like archives, tar or cpio files, that you download, then extract over your local files, but the extraction program is extra clever, and cleanly integrates your local content with the archive content, pausing to let you resolve conflicts it cannot figure out.&lt;/p&gt;

&lt;p&gt;At least that’s the analogy for&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull.&lt;/code&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt;is sort of like the reverse, but not really (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; is much closer to being the opposite of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git fetch&lt;/code&gt;: if you think of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt; as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy from remote to local&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy from local to remote&lt;/code&gt;… …of course, the remote need not be remote, it can be another local folder or filesystem, but’s that for another time… …unless you grok that right away… :-&amp;gt;).&lt;/p&gt;

&lt;p&gt;The cool thing is that a&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bundle&lt;/code&gt;is an archive and can be treated exactly like a remote, pulling from it and pushing to it, for those times when you need to share or move code but cannot do it directly in a connected fashion. Another thought for another time.&lt;/p&gt;

&lt;p&gt;So, no: A remote is not a branch, even if&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch&lt;/code&gt;will list available/known remotes. My take is that the git developers know that remotes aren’t branches but that they value usability and friendliness and sensible conventions over slavish devotion to intellectual and/or design purity. The more I use git, the more I respect their work.&lt;/p&gt;

&lt;p&gt;But if you start thinking of branches as places, not paths, and stop thinking of remotes as branches, a lot becomes clear. Er. Clearer. Yeah, that’s one.&lt;/p&gt;

&lt;p&gt;One big step (at least for me) on the road to git clarity….&lt;/p&gt;

&lt;h3 id=&quot;references-and-acknowledgements&quot;&gt;References and acknowledgements&lt;/h3&gt;

&lt;p&gt;This article and the perspective behind it owe a great deal to the following. I especially need to reread the second one a few more times….&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The thoroughly enjoyable &lt;a href=&quot;http://think-like-a-git.net&quot;&gt;Think Like (a) Git&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;The &lt;a href=&quot;http://think-like-a-git.net/resources.html&quot;&gt;resources section&lt;/a&gt; is especially good. Some of my favourites are &lt;a href=&quot;https://github.com/pluralsight/git-internals-pdf&quot;&gt;Scott Chacon’s &lt;em&gt;Git Internals&lt;/em&gt;&lt;/a&gt; and &lt;a href=&quot;https://tomayko.com/blog/2008/the-thing-about-git&quot;&gt;Ryan Tomayko’s &lt;em&gt;The Thing About Git&lt;/em&gt;&lt;/a&gt; – I particularly like Ryan’s take on&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add --patch&lt;/code&gt;and how&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add&lt;/code&gt;is, in general, a command for manipulating the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index,&lt;/code&gt;aka&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;staging,&lt;/code&gt;aka, the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache.&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;A thoroughly underrated &lt;a href=&quot;https://stackoverflow.com/a/61575566&quot;&gt;stackoverflow answer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Paradox-free Time Travel! Yay! Wait, not so fast....</title>
   <link href="http://peter-whittaker.com/time-travel-yay-wait-maybe-not"/>
   <updated>2020-09-29T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/time-travel-yay-wait-maybe-not</id>
   <content type="html">&lt;p&gt;A &lt;a href=&quot;https://iopscience.iop.org/article/10.1088/1361-6382/aba4bc&quot;&gt;recent paper on Closed Time-like Curves&lt;/a&gt; has editors the world over asserting that paradox-free time-travel is now possible! Readers have gotten pretty excited too….&lt;/p&gt;

&lt;p&gt;Fantastic! If we travel back in time and cause an accident, we’ll still exist when we get back!&lt;/p&gt;

&lt;p&gt;Well, no. Or at least, not quite. These headlines are inaccurate: a more accurate headline might be “Paradox-Free Time Travel Possible, Provided Closed Time-like Curves (CTCs) Exist (in our universe)”.&lt;/p&gt;

&lt;p&gt;It’s a bit wordy, and loses some of the buzz, so one can understand why editors have opted for click-bait.&lt;/p&gt;

&lt;p&gt;Notice the conditional in there, the proviso? If CTCs exist - or can exist - in our universe, then we get safe time travel. But that is a huge “if”….&lt;/p&gt;

&lt;p&gt;CTCs are a set of possible solutions to the equations of General Relativity (GR). The equations of GR are non-linear and it is not always possible to produce analytic (complete) solutions, so physicists often use approximations to get close to a solution. If a solution is interesting, e.g., T=0, it is studied further (oh, the Big Bang (literally: T=0, everything starts)). One can also make assumptions about some elements of the equations (if we set this to P…) and see what pops out (oh, Big Bang! oh, CTCs!).&lt;/p&gt;

&lt;p&gt;The equations of GR are studied as much for how interesting is the math as for their potential physical value. CTCs have been studied for over a century, in part as an intellectual exercise (oh, these are weird, could things really work this way), in part to learn if their study can inform open problems in physics.&lt;/p&gt;

&lt;p&gt;One such open problem is that GR and Quantum Mechanics (QM) are inconsistent with each other. For example, GR assumes continuous functions and continuous values (it is a classical theory, in that limited respect) while QM assumes that observations can take on only discrete values and, as a result, functions used in QM are those amenable to treating discontinuous ranges of values. This is where the term “quantum leap” comes from: An observable leaps from one value to another without having any of the values in between; in classical mechanics and GR, this just isn’t allowed. (Fundamentally, the maths of GR and QM don’t even share much commonality; I saw a great graphic about this years ago, and I regret not saving it.)&lt;/p&gt;

&lt;p&gt;Cosmology is informed by both QM and GR: GR tells us more or less how to interpret the structure of the universe at a large scale, over large time frames (was there a Big Bang; is the universe expanding; is there dark matter; how do stars evolve and black holes form; etc.) while QM gets us the smallest scales and tells how information behaves in the universe, e.g., is information lost at the event horizon of a black hole, can physical processes be irreversible, etc.&lt;/p&gt;

&lt;p&gt;One of the most important aspects of this is that of the four fundamental forces (the electromagnetic, the strong and weak nuclear forces, and gravity), three are described by QM (the first three) and form part of the Standard Model (in which every force is transmitted by a “particle”, e.g., the photon for the electromagnetic force, and from which many incredible predictions have been made, e.g., the existence of the Higgs boson, recently confirmed at CERN). The fourth fundamental force, gravity, the most important to the overall structure of the universe, comes from GR. In particular, there is no “graviton” that carries it (or at least not yet).&lt;/p&gt;

&lt;p&gt;When physicists talk about a Grand Unified Theory, they are hoping to reconcile GR and QM, e.g., to make quantum gravity. Or more accurately, to quantize gravity.&lt;/p&gt;

&lt;p&gt;Since CTCs are a pure-GR thing, whether or not they can exist should depend on whether or not they are consistent with or violate QM. For example, the paper cited above does include as one of its references a paper arguing that CTCs would violate the Heisenberg Uncertainty Principle, which is pretty much as fundamental to QM as the constancy of the speed of light is to GR.&lt;/p&gt;

&lt;p&gt;Where things get really interesting is when one combines CTCs with computational complexity….&lt;/p&gt;

&lt;p&gt;If CTCs exist, &lt;a href=&quot;https://www.scottaaronson.com/papers/ctc.pdf&quot;&gt;Aaronson and Watrous have shown&lt;/a&gt; based on work by Deutsch that quantum and classical computers have exactly the same power - provided that one could engineer a classical computer to operate in a CTC (I’m going out on a limb stating that, a lot of Aaronson’s work makes my head hurt). Deutsch’s work isn’t universally accepted either.&lt;/p&gt;

&lt;p&gt;So. What is the article saying?&lt;/p&gt;

&lt;p&gt;If CTCs can exist in our universe (if they are consistent with our most-successful-ever theory, our most tremendous intellectual artifact, QM), then paradox-free time-travel can exist.&lt;/p&gt;

&lt;p&gt;But that is a very, very, very large IF. Like 96 point bold underlined flashing neon.&lt;/p&gt;

&lt;p&gt;What’s the point, then?&lt;/p&gt;

&lt;p&gt;The more we learn about GR and what it allows and forbids, and the more we learn about QM and what it allows and forbids, the more clues we obtain to what is wrong with either and what a future synthesis or replacement theory might look like.&lt;/p&gt;

&lt;p&gt;Studying paradox-free time-travel via CTC may provide theoretical insights or it may actually predict observables, either confirmatory (if we see it, CTCs are real) or contradictory/falsifiable (if we see it, CTCs cannot be).&lt;/p&gt;

&lt;p&gt;So we don’t know anything more for sure, yet. But we have a better idea of how things might be, provided other things are. We just don’t know what those other things are yet.&lt;/p&gt;

&lt;p&gt;Stay tuned….&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Python Best Practice re methods Vs external functions</title>
   <link href="http://peter-whittaker.com/python-best-practice-Q01"/>
   <updated>2020-09-24T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/python-best-practice-question</id>
   <content type="html">&lt;p&gt;Q for my Pythonista friends….&lt;/p&gt;

&lt;p&gt;I’ve several utility functions for processing complex data in a file, each function being pithy: read from the file, validate the data, display it if in debug mode, process it, etc. Each function exists in its own module. None have output, they either return valid data or raise appropriate exceptions.&lt;/p&gt;

&lt;p&gt;Just this morning I created a Class to represent the structure found in the file and moved the calls to the utility functions from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__main__&lt;/code&gt; to the class constructor, and rewrote &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__main__&lt;/code&gt;, shortening it quite a bit.&lt;/p&gt;

&lt;p&gt;Everything works. The executable imports the Class, the Class module imports the utility functions, all is well. All of my tests pass. Yay me.&lt;/p&gt;

&lt;p&gt;I like “all is well” and I am generally a “if it ain’t broke don’t fix it” kind of person, but I am stalled on wondering whether I should make those utility functions class methods: They are used exclusively for processing this very specific data, and don’t and won’t have a life of their own. Do I leave them as-is (everything works) or do I bring them into the class module? (I did this as a test, all works just as well. But I want to decide on a direction before making the initial commit to this branch.)&lt;/p&gt;

&lt;p&gt;My argument for doing nothing is a mix of “it ain’t broke” and aesthetics: The class module is short and pretty; bringing them in increases module line count by over 200.&lt;/p&gt;

&lt;p&gt;My argument for bringing them is less clear: It will reduce clutter in the folder that holds everything, and they don’t have a separate life, so having them be “private” methods within the class seems sensible, and I think it will make import and module packaging simpler, but I don’t know, hmm, fret….&lt;/p&gt;

&lt;p&gt;I know I am over thinking this, but….&lt;/p&gt;

&lt;p&gt;What’s the best practice guidance?&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>I am nothing ⊃ ¬Patient</title>
   <link href="http://peter-whittaker.com/nothing-if-not-patient"/>
   <updated>2020-09-01T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/nothing-if-not-patient</id>
   <content type="html">&lt;p&gt;Once, and I don’t recall why, I said, probably to someone else, “I am nothing if not patient”.&lt;/p&gt;

&lt;p&gt;Later, with grudging head-bobbing, self noted that I wasn’t (always) (particulary) patient.&lt;/p&gt;

&lt;p&gt;Did I mention that my degree’s in physics (meaning I studied more math than anything else) and that I got most of the way to a degree in philosophy?&lt;/p&gt;

&lt;p&gt;So, yeah, there is a strong inner logician. He sleeps a lot, but this woke him.&lt;/p&gt;

&lt;p&gt;He eeked out “if we are not patient, are we therefore nothing?”&lt;/p&gt;

&lt;p&gt;That was a &amp;lt;insert face-slap/cold-water metaphor here&amp;gt; moment.&lt;/p&gt;

&lt;p&gt;It worked, though. Many/most/at-least-some moments of impatience were followed by moments of reflection. I am not patient. What am I? I don’t feel like nothing, but I don’t feel really good about this.&lt;/p&gt;

&lt;p&gt;It took a long time.&lt;/p&gt;

&lt;p&gt;I feel it. And I think most people consider me pretty chill these days, even thems that know me really well and see private me. It’s a good feeling.&lt;/p&gt;

&lt;p&gt;FWIW. YMMV. But one never knows, eh?&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Doing Python development under Mac OS</title>
   <link href="http://peter-whittaker.com/install-python-MacOS"/>
   <updated>2020-08-19T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Installing-Python-et-al-under-MacOS</id>
   <content type="html">&lt;p&gt;tl;dr? &lt;a href=&quot;#just-the-recipe&quot;&gt;Jump right to the recipe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For reasons lost in the mists of time, I’ve been a Mac guy since 2013 - but I’ve been a command line (CLI) guy since 1990 (or earlier, hard to remember), and still am. A few years ago, I discovered &lt;a href=&quot;https://brew.sh&quot;&gt;brew, aka homebrew&lt;/a&gt; after finding out how out-of-date were so many of the standard UNIX/Linux tools included with Mac OS, e.g., bash.&lt;/p&gt;

&lt;p&gt;Generalizing, the hardest part of working with the CLI on a Mac is having the same experience on all the platforms I use, which range from Windows 10 with WSL (some flavour of Ubuntu, cannot remember which), RHEL 7.6, Fedora 32, Mac OS 10.15, and Cygwin - and that problem persists across those other platforms, which is why &lt;a href=&quot;https://github.com/PeterWhittaker/myDotFiles&quot;&gt;I created a GitHub project to keep my dot files in snc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well. Until the last few days.&lt;/p&gt;

&lt;p&gt;Since last week I’ve been using the &lt;a href=&quot;https://github.com/23andMe/Yamale&quot;&gt;most excellent Yamale&lt;/a&gt; to validate YAML schema I’ve defined for my &lt;a href=&quot;https://github.com/PeterWhittaker/LinearAssuredPipeline&quot;&gt;SELinux Linear Assured Pipeline&lt;/a&gt;. I’ve even made some contributions to it - or, at least, am in the process of so doing…&lt;/p&gt;

&lt;p&gt;…and that’s where things get weird.&lt;/p&gt;

&lt;p&gt;Yamale depends upon Python and &lt;a href=&quot;https://pypi.org/project/pip/&quot;&gt;pip&lt;/a&gt;, the Python package manager, and upon &lt;a href=&quot;https://tox.readthedocs.io/en/latest/#&quot;&gt;tox&lt;/a&gt;, a test automation framework for Python  - but &lt;strong&gt;standard Mac OS has neither &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; nor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox&lt;/code&gt;&lt;/strong&gt;. OK, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install pip&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install tox&lt;/code&gt;, right? Well, almost.&lt;/p&gt;

&lt;p&gt;This is where things can get a little weird. Yamale’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox.ini&lt;/code&gt;, for example, depends upon two different versions of Python, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.6&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;How does get multiple versions of Python? The short answer is &lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;pyenv, a python-independent shell-script based system for managing multiple Python environments&lt;/a&gt;. But that’s not the whole story.&lt;/p&gt;

&lt;p&gt;This &lt;a href=&quot;https://opensource.com/article/20/4/pyenv&quot;&gt;page intends itself as the correct answer&lt;/a&gt; - install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; and go! - but it is lacking a step, as I discovered - and I discovered that page &lt;a href=&quot;https://opensource.com/article/19/5/python-3-default-mac&quot;&gt;from this page&lt;/a&gt;, which tells us what we might do wrong, then what we should do to get things right - but a bootstrap problem remains….&lt;/p&gt;

&lt;p&gt;The first page wants us to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install pyenv&lt;/code&gt; - but Mac OS lacks &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;. The second page has a few brew-based recipes, but suggests that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; is the wrong way to go, overall, and that we really want &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt;. This &lt;a href=&quot;https://opensource.com/article/19/6/python-virtual-environments-mac&quot;&gt;third page&lt;/a&gt; tries to put things succinctly, but if you’re running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox&lt;/code&gt;, there is &lt;strong&gt;YAS&lt;/strong&gt; (Yet Another Step): By default, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox&lt;/code&gt; works only with your default Python environment, whether that is standard or something installed via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox&lt;/code&gt; to work with multiple Pythons out of the box, you need &lt;a href=&quot;https://pypi.org/project/tox-pyenv/&quot;&gt;tox-pyenv&lt;/a&gt; - but &lt;strong&gt;that&lt;/strong&gt; page mentions a lot of irrelevant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Circle CI&lt;/code&gt; stuff.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Before we go any further, I should note that whatever you do
with brew - or with gem, if you get there - do NOT override
system defaults - Mac OS and many utilities produced for the
Mac depend upon them. Let brew manage things and install them
cellar-only. You then need to find them via appropriate
manipulation of PATH, CPPFLAGS, LDFLAGS, etc. Cf my dot file
project for one way of doing this.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At any rate, none of those pages provide the complete answer. You know that you need&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Python (and possibly multiple versions of Python)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; (not included with Mac OS)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox&lt;/code&gt;, which depends upon &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt;, which you can get a number of ways….&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox-pyenv&lt;/code&gt;, which depends upon &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;….&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what is one to do? Install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; and use it to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox-pyenv&lt;/code&gt; several times over? That seems so….&lt;/p&gt;

&lt;p&gt;UGH.&lt;/p&gt;

&lt;p&gt;I repeat.&lt;/p&gt;

&lt;h1 id=&quot;ugh&quot;&gt;UGH.&lt;/h1&gt;

&lt;p&gt;I don’t know that what follows is a universally-accepted approach, but it is the shortest and cleanest path to where I needed to go (as verified by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make clean; make&lt;/code&gt; in my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yamale&lt;/code&gt; top-level folder and having all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox&lt;/code&gt; tests work out-of-the-box with both Python &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.6&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.8&lt;/code&gt;, no modifications to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yamale&lt;/code&gt; files required…).&lt;/p&gt;

&lt;p&gt;So, &lt;a href=&quot;http://onlineslangdictionary.com/meaning-definition-of/ymmv&quot;&gt;YMMV&lt;/a&gt; and &lt;a href=&quot;http://onlineslangdictionary.com/meaning-definition-of/fwiw&quot;&gt;FWIW&lt;/a&gt;, here is….&lt;/p&gt;

&lt;h3 id=&quot;just-the-recipe&quot;&gt;Just The Recipe&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# install brew
$ /bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)&quot;
# use brew to install python3, which comes with pip3
$ brew install python3          # let brew install this cask-only
# figure out your path to pip3, installed with python3
$ &amp;lt;path to pip3&amp;gt; install tox
# install py-env using brew
$ brew install py-env
$ &amp;lt;path to pip3&amp;gt; install tox-pyenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you’ve done that, figure out what versions of Python your particular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tox.ini&lt;/code&gt; specifies; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yamale&lt;/code&gt;, these are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.6&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3.8&lt;/code&gt; which I’ve mentioned a few times already…. Assuming these are what you need, the rest of your recipe is as follows:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ brew install pyenv
$ pyenv install 3.6
# lists multiple versions, choose appropriate minor #, e.g.,
$ pyenv install 3.6.11
$ pyenv install 3.8
# lists multiple versions, choose appropriate minor #, e.g.,
$ pyenv install 3.8.5
# almost there, now we want to use what we just installed
$ cd &amp;lt;where Yamale lives&amp;gt;
# have pyenv set the versions of python tox should use
$ pyenv local 3.6.11 3.8.5
# that command creates a ./python-version file, which
# is already in ./gitignore! Now confirm those....
$ pyenv version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In my case, the latter command shows&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;3.6.11 (set by /Users/pww/pipelineWork/yamale/.python-version)
3.8.5 (set by /Users/pww/pipelineWork/yamale/.python-version)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv which&lt;/code&gt; shows me what I expected:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ pyenv which python3.6
/Users/pww/.pyenv/versions/3.6.11/bin/python3.6
$ pyenv which python3.8
/Users/pww/.pyenv/versions/3.8.5/bin/python3.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now. Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make clean; make&lt;/code&gt; and all should be well.&lt;/p&gt;

&lt;p&gt;It was for me, at least.&lt;/p&gt;

&lt;p&gt;Easy-peasy. Right?&lt;/p&gt;

&lt;p&gt;Right.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Buying a used Wrangler JK/JKU: Rubi or Sport?</title>
   <link href="http://peter-whittaker.com/rubi-or-sport"/>
   <updated>2020-08-17T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Rubi-or-Sport</id>
   <content type="html">&lt;p&gt;Someone posted in an FB group indicating they were going to buy a 2012-or-later Wrangler and asking whether they should get the Rubicon or the Sport. Well, my comment was longish (surprise, surprise), so I thought I would reproduce it here.&lt;/p&gt;

&lt;p&gt;I bought a Rubi new in 2014. It was my daily for five years, and was wheeled hard. Now it is a trail queen, and will, eventually, be a trailer queen (my truck is now my daily).&lt;/p&gt;

&lt;p&gt;Pros of the Rubi:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Electronically controlled lockers and swaybar disconnect.&lt;/li&gt;
  &lt;li&gt;Beefier rear axle and tube; beefier front axle (the front tube is sort of a 37, it’s a shaved 44, not as tough as the rear, tougher than a 30).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons of the Rubi:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Price.&lt;/li&gt;
  &lt;li&gt;The wires for the lockers can be ripped off by branches if one is cavalier about them and replacing the sensor and actuator is a huge PITA (I am less cavalier now :-&amp;gt;).&lt;/li&gt;
  &lt;li&gt;The swaybar disconnect needs regular maintenance (remove, disassemble, clean, grease but not too much) if ever exposed to water, dust, etc., which it will be if you wheel it all.&lt;/li&gt;
  &lt;li&gt;The SmartBar component that controls the swaybar has NO protection against water or dust intrusion and the harder you wheel the more likely it will fail, eventually, taking your canbus with it - the Christmas Tree display is, uh, surprising. It’s $2500 to replace. I replaced the entire thing with a Heilweig heavy duty and have been disconnected since June, no problems anywhere.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How to choose? It comes down to two factors:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;how mechanically inclined you are, and&lt;/li&gt;
  &lt;li&gt;how soon you want to get to hard wheeling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Less of the former and more of the latter == Rubicon.&lt;/p&gt;

&lt;p&gt;More of the former and less of the latter == Sport (save money now, upgrade as you discover your wheeling style).&lt;/p&gt;

&lt;p&gt;Final note: If you are serious about buying a used Rubi, take it for a test drive, get it onto gravel or dirt, put it in 4Hi, drive around a bit, shift back to 2WD, then back to 4Hi, make sure that is smooth and easy. Stop, put it in neutral, shift into 4Lo, drive some more, and try the lockers (one click is rear, two is front, three is both (? not sure, I always go by the indicator lights) and the disconnect (the lockers and disconnect only work in 4Lo at less than 40kph).&lt;/p&gt;

&lt;p&gt;Make sure everything works, including all the indicators. (Try using any of the lockers or swaybar in 4Hi or in 2WD: The indicators should be flash, showing that the component is unavailable.)&lt;/p&gt;

&lt;p&gt;If any of these tests fail, run away. Or ask for a deep, deep discount.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Well, I'm back</title>
   <link href="http://peter-whittaker.com/well-im-back"/>
   <updated>2020-08-07T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/well-im-back</id>
   <content type="html">&lt;h2 id=&quot;five-years-is-a-long-time-why-stop-why-start-again-and-why-was-restarting-so-technically-painful&quot;&gt;Five years is a long time. Why stop? Why start again? And why was restarting so (technically) painful?&lt;/h2&gt;

&lt;p class=&quot;lead&quot;&gt;tl;dr It was 2014; I thought I should learn git and GitHub - didn&apos;t use it, just thought it would be cool. The blog was a practical exercise in both. I&apos;m not sure why I kept it going (not that it went very long). Work changed. Life changed. Now I use git on work&apos;s self-hosted GitLab sometimes weekly, sometimes hourly: I needed to step up my git game. Looking up a very specific how-to dropped me into a four day rabbit hole....&lt;/p&gt;

&lt;p class=&quot;lead&quot;&gt;Also tl;dr In this &lt;strong&gt;very long&lt;/strong&gt; post, you&apos;ll learn (at least part of) how to maintain a Jekyll-based GitHub Pages blog under Mac OS. There is an awful long in here, take your time.... :-&amp;gt;&lt;/p&gt;
&lt;p&gt;For most of the last 20 years, I’ve been a consultant. Work inputs were meetings, site inspections, emails, phone calls, interviews both formal and informal, and reams of policy and procedure documents. Deliverables were documents and presentations and emails. Mostly.&lt;/p&gt;

&lt;h2 id=&quot;the-backstory---getting-started-back-then-and-stopping-and-starting-again&quot;&gt;The backstory - getting started, back then, and stopping, and starting again….&lt;/h2&gt;

&lt;p&gt;2014 began the same way, but looked to become more technical. I’d been curious about git for some time, but had neither work need nor leisure time (foreshadowing: life changes) to make any effort. With the 2014 work becoming more technical, that looked to change. I’d also &lt;em&gt;always meant&lt;/em&gt; to start a blog, so, brace of birds, single rock, and all that. Yay, the blog was born. There was much rejoicing.&lt;/p&gt;

&lt;p&gt;Actually, there were pretty much crickets. I didn’t feel badly about that, many blogs are ignored. Many blogs also peter out over time, so I didn’t feel too badly about that, either (well, sort of kind of? More &lt;em&gt;life change&lt;/em&gt; foreshadowing, duh duh daaaaah).&lt;/p&gt;

&lt;p&gt;Fast forward to earlier this year: A consulting gig has me coding. February and March find me in the basement typing away for 10 to 12 hours a day, 6 or 7 seven days a week. I was in heaven - the beginning of lockdown went largely - but not entirely - unnoticed for me. Yes, I have the most patient girlfriend in the world, she is sweet and adorable and understanding (queue &lt;em&gt;life change&lt;/em&gt; foreshadowing minor key).&lt;/p&gt;

&lt;p&gt;That coding involved git. Just basic stuff, trying to get my head around git pull, git add, git commit, and git push. Branches were a step too far, as was using SSH.&lt;/p&gt;

&lt;p&gt;That was then (has it really only been five months?). That consulting gig morphed into full-time employment, the first time I have been an employee since 2001 (2013-2014 kind-of/sort-of had employment, but not really, that was a strange period… …maybe one day I’ll share an askari story (there’s a pun there; I quite like it; grokking it requires knowing the story; maybe one day; I digress…)).&lt;/p&gt;

&lt;p&gt;That &lt;a href=&quot;https://www.sphyrnasecurity.com&quot;&gt;full time job&lt;/a&gt; is a mix of marketing, marketing management (mostly product/roadmap), client liaison, prospect management, business development, and hands-on. Instead of writing this, I should probably be working on the compliance platform User Acceptance Plan for one customer; or SELinux linear channel controls for two customers; or the cloud-based compliance platform demonstrator; or a few other things… (I’ve got GitLab issues aplenty assigned to me…).&lt;/p&gt;

&lt;p&gt;A few days ago, I thought it would be a good idea to upload my SSH public key to the company GitLab and use that instead of passwords. My git/GitHub/GitLab isn’t yet second nature, so a lot of comparatively simple things require research and reminders - and I dislike breaking things through my own ignorance, so off to GitHub I went to remind myself how to upload SSH keys, etc.&lt;/p&gt;

&lt;p&gt;In noodling through my account settings, I noticed that all of the security and analysis features were disabled and I said to myself, “Self, why are these off? Surely they are a good idea!”. Self thought this was very reasonable, did a bit of poking about, satisfied itself that CHECKING ALL THE BOXES!!! was a good idea, and did so….&lt;/p&gt;

&lt;p&gt;The emails began to arrive seconds later.&lt;/p&gt;

&lt;p&gt;I don’t have a lot of code in GitHub, mostly personal stuff, very few forks. Some of that code failed one Dependabot test or another….&lt;/p&gt;

&lt;p&gt;This blog is hosted on GitHub. It hadn’t been updated in six years. There were, uh, vulnerabilities and out-of-date code aplenty.&lt;/p&gt;

&lt;p&gt;And the blog broke. Pagination broke. Application of CSS broke. A lot of things broke.&lt;/p&gt;

&lt;p&gt;It was embarassing, really. I had to fix it. I just &lt;em&gt;had to!!!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There were a few basic issues:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Out of date code, mostly Jekyll&lt;/li&gt;
  &lt;li&gt;GitHub pages had stopped supporting
    &lt;ul&gt;
      &lt;li&gt;relative links&lt;/li&gt;
      &lt;li&gt;the markdown engine I’d been using&lt;/li&gt;
      &lt;li&gt;URL specifications such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{ site.baseurl }}&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The IP addresses for custom domains had changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last of those generated build warnings; the first few build failures. I’m not a Ruby guy, so it took a while to find everything and figure things out… …but I couldn’t even start figuring things out until I updated my Mac OS development environment… …which I didn’t realize I had to do for a while.&lt;/p&gt;

&lt;h2 id=&quot;a-digression---how-to-manage-dot-files-across-multiple-machines&quot;&gt;A digression - how to manage &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot files&lt;/code&gt; across multiple machines&lt;/h2&gt;

&lt;p&gt;On any given day, I might work on any or all of two Macs (my ancient Air, my work Mini); one or more Linux VMs on the Mini (my Fedora SELinux VM; CentOS or Fedora VMs for the compliance automation platform); one or more customer RedHat VMs for one of my contracts; and WSL, the Windows Subsystem for Linux.&lt;/p&gt;

&lt;h3 id=&quot;a-digression-within-a-digression---wsl-is-awesome&quot;&gt;A digression within a digression - WSL is awesome&lt;/h3&gt;

&lt;p&gt;Until PowerShell, Windows shells sucked. PWSH changed that. It’s powerful, well-designed, and can be a lot of fun to use. But if one is mostly using Linux and bash, it can be a pain to make the mental switch from bash to PWSH and back again.&lt;/p&gt;

&lt;p&gt;So I installed CygWin on a few Windows machines and it was OK. Pretty good. Not great. Somewhere along the way, WSL popped onto my radar as I was intrigued. “Huh. Real Linux? On Windows? Directly? &lt;em&gt;Can’t be!!!&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;A little poking about, a little research, installation of WSL, installation of Ubuntu under WSL, &lt;em&gt;WHAM!&lt;/em&gt;, real bash. Heaven!&lt;/p&gt;

&lt;h3 id=&quot;we-now-return-to-our-regularly-scheduled-inception-er-digression&quot;&gt;We now return to our regularly scheduled inception, er, digression&lt;/h3&gt;

&lt;p&gt;When I switch machines, I like to have the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vimrc&lt;/code&gt;, the same bash prompt, the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; defaults, etc.&lt;/p&gt;

&lt;p&gt;At first, I was mucking about with Google Drive and uploading and downloading &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rc&lt;/code&gt; files, but that was a real pain. I took a while to realize that I could a) reduce/eliminate that pain and b) learn more git, if I created a GitHub project &lt;a href=&quot;https://github.com/PeterWhittaker/myDotFiles&quot;&gt;for my dot files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the challenges of getting there was merging various &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.rc&lt;/code&gt; files. On Air, my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; had a bunch of entries relating to things like Ruby, ICU4C, etc. I had no idea what they were, no idea why there were, so I commented them out: I didn’t want in my environment things I didn’t understand and didn’t know why they were there. Pretty reasonable, I think.&lt;/p&gt;

&lt;h2 id=&quot;we-will-now-pause-the-first-digression&quot;&gt;We will now pause the first digression&lt;/h2&gt;

&lt;p&gt;There was a lot of Googling to rediscover how to manage a Jekyll-based site on the Mac OS command line. IM(NS)HO, the Jekyll documentation runs at two levels, too basic and too complex. Much forensic guess work was involved.&lt;/p&gt;

&lt;p&gt;Long story short, I started running various gem and jekyll commands and nothing was working.&lt;/p&gt;

&lt;p&gt;Most notably, I could neither figure out nor remember what this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle&lt;/code&gt; thing was nor how to find it.&lt;/p&gt;

&lt;h2 id=&quot;another-digression---keeping-a-mac-os-development-environment-up-to-date&quot;&gt;Another digression - keeping a Mac OS development environment up-to-date&lt;/h2&gt;

&lt;p&gt;I’d installed brew and a few other things in 2014 as part of getting the blog going in the first place. Sometime after that, for reasons lost in the mists of time, I installed node. Later, doing advisory and content development for &lt;a href=&quot;https://www.clickarmor.com&quot;&gt;ClickArmor&lt;/a&gt;, Node was a great engine for testing some JS code I was hacking about. After a few false starts, I wrote a script to simplify my life:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat ~/bin/update
#!/bin/bash

brew update
brew upgrade
read -p &quot;Ready to continue with npm and node (ctrl-c to skip)? &quot; answer
npm-check -u -g
npm-check -u
node -v
npm -v
$
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It’s basically an encapsulation of just-in-time reseach I knew I was likely to forget: Run the brew update and upgrade commands, let me decide whether or not to update node, which can take a while, run the necessary npm-check and node commands….&lt;/p&gt;

&lt;p&gt;So far, so good.&lt;/p&gt;

&lt;h2 id=&quot;we-will-now-pause-the-second-digression&quot;&gt;We will now pause the second digression&lt;/h2&gt;

&lt;p&gt;In puzzling out restarting blog management, I realized I needed a few gems - and that my gems were out of date. My first thought was to update my script…&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
brew update
brew upgrade
gem update
read -p &quot;Ready to continue with npm and node (ctrl-c to skip)? &quot; answer
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This didn’t work. At least not well. I kept getting this error:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ERROR:  While executing gem ... (Gem::FilePermissionError)
    You don&apos;t have write permissions for the /Library/Ruby/Gems/2.6.0 directory.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;More Googling revealed little/nothing of value, so I naively tried:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
brew update
brew upgrade
gem update || sudo gem update
read -p &quot;Ready to continue with npm and node (ctrl-c to skip)? &quot; answer
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Yay! No more errors! Great, right?!?!&lt;/p&gt;

&lt;p&gt;Yeah, no, not so much.&lt;/p&gt;

&lt;p&gt;I didn’t have any errors, but the site still wasn’t building.&lt;/p&gt;

&lt;p&gt;I was lost, so I did what I probably should have done to begin with: careful inspection of all update/upgrade messages, wherein I noticed various brew indications about things being installed &lt;em&gt;cask-only&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Distant bells began to ring, echoes from the past. Right. Mac OS includes a few things that one can also get from brew, notably herein, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem&lt;/code&gt;, but neither &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll&lt;/code&gt; nor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;More puzzling. Finally, &lt;strong&gt;EUREKA!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I needed to&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install Ruby via brew&lt;/li&gt;
  &lt;li&gt;Update my bash environment so that brew versions of various things appeared first in various paths&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, I was using the Mac OS versions of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem&lt;/code&gt;, etc., which are older, and wasn’t picking up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle&lt;/code&gt;, et al, at all.&lt;/p&gt;

&lt;p&gt;Step the first was easy enough. Step the second got me looking through my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt;….&lt;/p&gt;

&lt;h2 id=&quot;merging-digressions-the-first-and-second&quot;&gt;Merging digressions the first and second&lt;/h2&gt;

&lt;p&gt;I wanted to manage my bash environment conservatively, only adding to it things that made sense on the machine in question. I’d made a start at that some time ago:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
function isMacOS {
    false
}

function isLinux {
    false
}
...

case $OSTYPE in
    darwin*)
        function isMacOS {
            true
        }
        ;;
    cygwin*)
        function isCygwin {
            true
        }
        ;;
    linux-gnu*)
        function isLinux {
            true
        }
        case $(uname -r) in
            *Microsoft)
                function isWSL {
                    true
                }
                ;;
            *)
                :
                ;;
        esac
        ;;
    *)
        echo; echo &quot;What OS is this?&quot;; echo
        ;;
esac

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Nothing terribly complicated, a few utility functions to help manage conditional inclusion of environment variables, paths, etc., based on platform.&lt;/p&gt;

&lt;p&gt;Among other things, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isMacOS&lt;/code&gt; function appeared in that commented-out block of settings related to ICU4C, et al, mentioned above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lightbulb flash&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Right, that’s what those were for. All of that code was first added to my Air &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; to make sure I was using the brew versions of various utilities, so that I could build and test the blog locally before pushing it to GitHub. Riiiight!&lt;/p&gt;

&lt;p&gt;Progress.&lt;/p&gt;

&lt;p&gt;Did I enable all that code? No. I was tempted to. But conservative in code application, remember? I realized I wanted it to be conditional, based at least on whether or not various components were present.&lt;/p&gt;

&lt;p&gt;This is what replaced the commented-out code (the path-bit bookends are included for completeness):&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if [ -z &quot;${PATH}&quot; ]; then
    # there should be a default path, but in some weird cases, perhaps not
    # set a reasonable default
    PATH=/usr/bin:/bin:/usr/sbin:/sbin
    export PATH
fi

# Take full advantage of BREW, if on Mac OS and if it installed
if isMacOS; then

    # linked brew formula
    checkFor PATH /usr/local/bin
    
    # ic4uc items
    checkFor PATH &quot;/usr/local/opt/icu4c/bin&quot;
    checkFor PATH &quot;/usr/local/opt/icu4c/sbin&quot;
    checkFor LDFLAGS /usr/local/opt/icu4c/lib&quot;
    checkFor CPPFLAGS /usr/local/opt/icu4c/include&quot;

    # ruby items
    checkFor PATH &quot;/usr/local/opt/ruby/bin&quot;
    checkFor LDFLAGS &quot;/usr/local/opt/ruby/lib&quot;
    checkFor CPPFLAGS &quot;/usr/local/opt/ruby/include&quot;

    # python items
    checkFor PATH &quot;/usr/local/opt/python@3.8/bin&quot;
    checkFor LDFLAGS &quot;/usr/local/opt/python@3.8/lib&quot;
    checkFor PKG_CONFIG_PATH &quot;/usr/local/opt/python@3.8/lib/pkgconfig&quot;

    # openssl items
    checkFor PATH &quot;/usr/local/opt/openssl@1.1/bin&quot;
    checkFor LDFLAGS &quot;/usr/local/opt/openssl@1.1/lib&quot;
    checkFor CPPFLAGS &quot;/usr/local/opt/openssl@1.1/include&quot;
    checkFor PKG_CONFIG_PATH &quot;/usr/local/opt/openssl@1.1/lib/pkgconfig&quot;

fi

# always prepend my bin, if it exists and is not already there
checkFor PATH ~/bin

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The interesting bits:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set a reasonable default PATH, if PATH is unset&lt;/li&gt;
  &lt;li&gt;If running on Mac OS, update various paths iff (if and only if) certain packages are present&lt;/li&gt;
  &lt;li&gt;If it isn’t already there, prepend &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/bin&lt;/code&gt; to my PATH&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This works really, really well.&lt;/p&gt;

&lt;p&gt;But what’s this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkfor&lt;/code&gt; thing? I’m really pleased with this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;checkFor () {
    path=&quot;$1&quot;
    want=&quot;$2&quot;
    extras=$3

    if [[ -z &quot;$path&quot; || -z &quot;$want&quot; || -n &quot;$extras&quot; ]]; then
        echo &quot;$FUNCNAME requires PATH WANT; called with &apos;$path&apos; &apos;$want&apos; $3; doing nothing&quot;
        return 1
    fi

    # if the desired folder doesn&apos;t exist, return - nothing to add
    [[ -d &quot;$want&quot; ]] || return

    # if the wanted folder is in the path, return - nothing to add
    isInPath &quot;$path&quot; &quot;$want&quot; &amp;amp;&amp;amp; return

    # we only get here if we need to add to the path
    # now we need to know what type of path it is
    case $path in
        PATH)
            export PATH=&quot;${want}:${!path}&quot;
            ;;
        PKG_CONFIG_PATH)
            export PKG_CONFIG_PATH=&quot;${want}:${!path}&quot;
            ;;
        LDFLAGS)
            export LDFLAGS=&quot;-L${want} ${!path}&quot;
            ;;
        CPPFLAGS)
            export CPPFLAGS=&quot;-I${want} ${!path}&quot;
            ;;
    esac
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Fairly simple logic:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Check for the right number of arguments (two):
    &lt;ol&gt;
      &lt;li&gt;The type of “path” to update (PATH, LDFLAGS, etc.);&lt;/li&gt;
      &lt;li&gt;The folder to add to that “path”&lt;/li&gt;
      &lt;li&gt;If there are three or more arguments, chances are that
        &lt;ul&gt;
          &lt;li&gt;The name of the folder “want” contains spaces and&lt;/li&gt;
          &lt;li&gt;That name wasn’t quoted when passed to checkFor&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;If the folder “want” doesn’t exist, do nothing (e.g., on a Mac where I don’t run brew)&lt;/li&gt;
  &lt;li&gt;If the folder is already in whatever “path” it is, do nothing
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isInPath&lt;/code&gt; is explained below&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;If
    &lt;ol&gt;
      &lt;li&gt;all arguments are correct,&lt;/li&gt;
      &lt;li&gt;“want” exists, and&lt;/li&gt;
      &lt;li&gt;“want” isn’t already in “path”
 add it to the appropriate path, with appropriate separation and prefix, if any&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkFor&lt;/code&gt; relies on:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;isInPath () {
    path=&quot;$1&quot;
    want=&quot;$2&quot;
    extras=$3

    # we need exactly two arguments; anything else is probably a quoting problem
    if [[ -z &quot;$path&quot; || -z &quot;$want&quot; || -n &quot;$extras&quot; ]]; then
        echo &quot;$FUNCNAME requires PATH WANT; called with &apos;$path&apos; &apos;$want&apos; $3; doing nothing&quot;
        return 1
    fi

    # if $path is empty, $want obviously isn&apos;t present
    [[ -z &quot;${!path}&quot; ]] &amp;amp;&amp;amp; return 1

    # replace any ~ with $HOME, to be on the safe side
    [[ &quot;$want&quot; == *&quot;~&quot;* ]] &amp;amp;&amp;amp; want=&quot;${want/#\~/$HOME}&quot;

    if [[ &quot;${!path}&quot; != *&quot;$want&quot;* ]]; then
        # echo &quot;NEED: no &apos;$want&apos; in &apos;${!path}&apos;&quot; 
        return 1
    else
        # echo &quot;GOOD: &apos;$want&apos; is in &apos;${!path}&apos;&quot;
        return 0
    fi
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Again, fairly simple logic:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Check arguments&lt;/li&gt;
  &lt;li&gt;Return false if “path” is empty - if it’s empty, we need to create it, which we do by assigning to in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkFor&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Note the use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${!path}&lt;/code&gt; to cause “$path” to be replaced with the contents of whatever variable it represents, e.g., if $path==PATH, ${!path}==$PATH&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Replace any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~&lt;/code&gt; in “want” with the contents of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$HOME&lt;/code&gt;, to simplify matching&lt;/li&gt;
  &lt;li&gt;Return
    &lt;ol&gt;
      &lt;li&gt;false if “path” doesn’t contain “want”&lt;/li&gt;
      &lt;li&gt;true if it does&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkFor&lt;/code&gt; updates “path” only if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isInPath&lt;/code&gt; returns false.&lt;/p&gt;

&lt;p&gt;With that in place, I could update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/bin/update&lt;/code&gt; (remember that?). Long story short, it is now:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/bash

echo Updating brew....
brew update
echo Upgrading brew....
brew upgrade
echo Updating gems
gem update
read -p &quot;Ready to continue with npm and node (ctrl-c to skip)? &quot; answer
npm-check -u -g
npm-check -u
node -v
npm -v
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Notice that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt; isn’t there anymore: with various environment variables set correctly, all of the updates take place in &lt;em&gt;the right place&lt;/em&gt;, regardless of whether that’s system-wide or keg-only: Whatever commands I run pick up the correct environment and do this right thing. Notably,&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ which gem
/usr/local/opt/ruby/bin/gem
$ which bundle
/usr/local/opt/ruby/bin/bundle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But these depend a on few more things we need to cover….&lt;/p&gt;

&lt;h2 id=&quot;you-have-reached-the-end-of-the-first-and-second-digressions&quot;&gt;You have reached the end of the first and second digressions&lt;/h2&gt;

&lt;p&gt;Somewhere along the line, I realized or remembered that building the site on my Mac required these two commands:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle install
bundle exec jekyll serve
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Long story short, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt; reads &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;, installs whatever is needed, and creates &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt; with the current list of dependencies. But I was still getting build errors whenever I did &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push githubio&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Again, long story short, I realized a few things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;When resurrecting a six-year-old site, it’s better not to have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt;, as it might confuse &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle&lt;/code&gt;, and&lt;/li&gt;
  &lt;li&gt;I was going to forget this.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;enter-the-makefile&quot;&gt;Enter the Makefile&lt;/h2&gt;

&lt;p&gt;I love &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt;. I really miss the O’Reilly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; book I lent to someone who quit the &lt;em&gt;Big Nerd Ranch&lt;/em&gt; and took it with them a little while later. Anyway. Typing&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;make
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;doesn’t save a lot of keystrokes, but&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat Makefile
all:
	@bundle install
	@bundle exec jekyll serve

clean:
	@rm Gemfile.lock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;reminds me that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; actually means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install; bundle exec jekyll serve&lt;/code&gt;, which means &lt;em&gt;run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;&lt;/em&gt; to update everything then &lt;em&gt;run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec jekyll serve&lt;/code&gt;&lt;/em&gt; to launch Jekyll as a server so that I can find out a) if local builds succeed and b) if I like the look of the local version of the blog.&lt;/p&gt;

&lt;p&gt;It also reminds me that running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make clean&lt;/code&gt; can help resolve odd errors by removing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt; and letting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt; rebuild all dependencies.&lt;/p&gt;

&lt;p&gt;If all goes well, I can then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; to GitHub and the site will be updated for the world to see.&lt;/p&gt;

&lt;p&gt;(It just occurred that I could add a push rule to the Makefile. Cool. Maybe later.)&lt;/p&gt;

&lt;h2 id=&quot;resurrecting-digressions-the-first-and-the-second-temporarily&quot;&gt;Resurrecting digressions the first and the second, temporarily&lt;/h2&gt;

&lt;p&gt;One note: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; expects real tabs in its makefiles, and I use all spaces in vim. This led to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vimrc&lt;/code&gt; update:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
syntax on
set nocompatible
set tabstop=8 softtabstop=0 expandtab shiftwidth=4 smarttab
set ignorecase
set autoindent
set ruler
set showcmd
set visualbell
set nostartofline
set mouse=nvh

highlight Comment ctermfg=DarkGrey guifg=DarkGrey cterm=underline

*autocmd FileType make set noexpandtab shiftwidth=8 softtabstop=0*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last line is key: If editing any kind of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Makefile&lt;/code&gt;, change back to&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;hard tabs with the correct tab stop, and&lt;/li&gt;
  &lt;li&gt;a shiftwidth of 8, and&lt;/li&gt;
  &lt;li&gt;do not expand to tabs to spaces or&lt;/li&gt;
  &lt;li&gt;use soft tab stops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was very happy to find this.&lt;/p&gt;

&lt;h2 id=&quot;the-digressions-are-dead-long-live-the-main-post&quot;&gt;The digressions are dead. Long live the main post.&lt;/h2&gt;

&lt;p&gt;OK, that’s an awful lot. What’s the real &lt;em&gt;tl;dr&lt;/em&gt;?&lt;/p&gt;

&lt;h2 id=&quot;getting-ready-to-build-a-jekyll-site-on-mac-os&quot;&gt;Getting ready to build a Jekyll site on Mac OS&lt;/h2&gt;

&lt;p&gt;This is the shortest possible recipe, as I understand it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install the XCode command line tools if necessary&lt;/li&gt;
  &lt;li&gt;Install Ruby using brew&lt;/li&gt;
  &lt;li&gt;Update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; so that your environment gets the correct paths&lt;/li&gt;
  &lt;li&gt;Update your brew installation and all your gems&lt;/li&gt;
  &lt;li&gt;Remove Gemfile.lock&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Out of an abundance of caution, do step 4 again&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle exec jekyll serve&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;a-note-on-installing-the-xcode-command-line-tools&quot;&gt;A note on installing the XCode command line tools&lt;/h3&gt;

&lt;p&gt;To check if the XCode command line tools are installed, run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ xcode-select -p
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This should return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Developer/CommandLineTools&lt;/code&gt;; if it does, you are probably OK. Move onto installing Ruby via brew (but keep an eye out for odd failures in later commands).&lt;/p&gt;

&lt;p&gt;If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select&lt;/code&gt; doesn’t exit, check your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PATH&lt;/code&gt; and, if necessary, install XCode from the Mac OS App Store.&lt;/p&gt;

&lt;p&gt;If it runs but doesn’t return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Developer/CommandLineTools&lt;/code&gt;, then run&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ xcode-select --install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When that completes, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xcode-select -p&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;If the command returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Developer/CommandLineTools&lt;/code&gt; you are probably OK. But. One can run into problems anyway. If the subsequent site building steps fail with odd errors, try the following&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo rm -rf /Library/Developer/CommandLineTools
$ xcode-select --install
$ xcode-select -p
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If this doesn’t a) result in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Library/Developer/CommandLineTools&lt;/code&gt; and/or b) doesn’t solve weird problems, I don’t know what other steps to take.&lt;/p&gt;

&lt;h3 id=&quot;command-line-précis&quot;&gt;Command-line précis&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ xcode-select -p
$ sudo rm -rf /Library/Developer/CommandLineTools
$ xcode-select --install
$ xcode-select -p
$ brew update
$ brew upgrade
$ brew install ruby
$ # update your paths appropriately
$ which gem
/usr/local/opt/ruby/bin/gem
$ which bundle
/usr/local/opt/ruby/bin/bundle
$ gem update
$ rm Gemfile.lock
$ bundle install
$ brew update
$ brew upgrade
$ gem update
$ bundle install
$ bundle exec jekyll serve
$ git status
$ # git add as necessary
$ git commit -a
$ git push remote
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;finally-we-can-correct-github-pages-build-errors&quot;&gt;Finally! We can correct GitHub Pages build errors!&lt;/h1&gt;

&lt;p&gt;Yeah, that was my last few days. Every time I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push remote&lt;/code&gt;, I would get an email a few minutes later with build errors. Long story short, in the six years (!!!) since I created the blog, GitHub pages has&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Updated Jekyll, etc., several times over&lt;/li&gt;
  &lt;li&gt;Dropped support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redcarpet&lt;/code&gt;, which I replaced with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kramdown&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Dropped support for relative permalinks&lt;/li&gt;
  &lt;li&gt;Moved to relative path names for included content, e.g., CSS&lt;/li&gt;
  &lt;li&gt;Changed the IP addresses used for custom domain names&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fixing the first two was easy, the Mac OS update procedure described above and a quick change to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt;, respectively.&lt;/p&gt;

&lt;p&gt;The next two required searching for all included files, e.g., CSS being pulled in via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_includes/head.html&lt;/code&gt;, and, more confusingly, removing all references to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{ site.url }}&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{ site.baseurl }}&lt;/code&gt;, etc., in all files, then removing them - I literally just deleted them and GitHub Pages figured things out pretty well.&lt;/p&gt;

&lt;p&gt;The command&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ find . -path ./_site -prune -o -type f -exec grep -i -H -e &apos;site.url&apos; -e &apos;site.baseurl&apos; {} \;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;was very useful for this; the grep clause can be extended as necessary with additional ‘-e &lt;pattern&gt;&apos; commands to find other offenders. The rest of the command is basic `find`:&lt;/pattern&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-path ./_site -prune&lt;/code&gt; causes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt; to ignore the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_site&lt;/code&gt; subfolder&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-type f -exec...&lt;/code&gt; tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt; to apply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; only to files, and not to folders&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep -i -H -e...&lt;/code&gt; tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; to
    &lt;ul&gt;
      &lt;li&gt;Ignore case when searching (just in case),&lt;/li&gt;
      &lt;li&gt;Output the name of any matching file (since it is being passed one file at a time, the default is to not output the file name, which means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; prints matches but not where it found them, unless you specify &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-H&lt;/code&gt;, which forces it to output the file name)&lt;/li&gt;
      &lt;li&gt;Match multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e&lt;/code&gt; patterns against the same file&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-o&lt;/code&gt; tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt; to apply either the path-ignore logic or the grep-in-files logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I figured the relative-path-thing out after carefully reading &lt;a href=&quot;https://github.community/t/css-not-being-applied-in-pages/10466/10&quot;&gt;a GitHub Community post&lt;/a&gt; that I’m not even sure how I found. Careful and determined and persistent Googling, I expect.&lt;/p&gt;

&lt;p&gt;The last fix required updating my custom domain’s DNS with the correct IP addresses for GitHub Pages; &lt;a href=&quot;https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site&quot;&gt;this is pretty well described here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Really, the bulk of the work was the elimination of relative links.&lt;/p&gt;

&lt;p&gt;In fact, in validating that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find&lt;/code&gt; command, I realized I am missing a few, notably in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;atom.xml&lt;/code&gt; and in the reference to &lt;em&gt;atom.xml&lt;/em&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_includes/head.html&lt;/code&gt;… I’ll get those later, the site pretty much works as designed, now.&lt;/p&gt;

&lt;h1 id=&quot;was-it-worth-it&quot;&gt;Was it worth it?&lt;/h1&gt;

&lt;p&gt;Yes. While it was frustrating to figure all of this out,&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’m confident that my Mac development environments are correctly configured&lt;/li&gt;
  &lt;li&gt;I got a lot of practical git experience in a very short time, enough to start locking in things like
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push remote&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git stash&lt;/code&gt; (I used this a lot when cycling through some potential fixes)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most notably, I’ve started to lock in the procedure for publishing new posts, which is excellent practical git:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git checkout -b newpost
$ # some editing
$ make # remember my Makefile, above: `bundle install; bundle exec jekyll serve`
$ git commit -a -m &quot;...&quot;
$ git checkout master
$ git merge newpost
$ git branch -d newpost
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It was lot more pleasant and a lot more harmless figuring this out on a site that doesn’t matter (hey, almost six years without updates) than on company code in the company repo: If I messed that up, my colleague would have had to fix my mistakes, and he is as busy as me and doesn’t have time to clean up my messes.&lt;/p&gt;

&lt;p&gt;I feel a lot more confident that I will mostly use git correctly now. That’s a big win. And writing this up solified a lot of points in my head, another win.&lt;/p&gt;

&lt;p&gt;(Heck, I even figured out to write Jekyll posts about Jekyll, so that they include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{% raw %}&lt;/code&gt;, for example!)&lt;/p&gt;

&lt;p&gt;Time to get back to work, I guess….&lt;/p&gt;

&lt;h1 id=&quot;ok-what-about-stopping-six-years-ago-and-all-the-foreshadowing&quot;&gt;OK, what about stopping six years ago? And all the foreshadowing?&lt;/h1&gt;

&lt;p&gt;Long story short, six years ago, if it wasn’t work-related, I really didn’t have time for it. It was, uh, unpopular, to devote leisure time to anything at the keyboard when there were &lt;em&gt;couply&lt;/em&gt; things to do - even if they were of asymmetric interest, if you will.&lt;/p&gt;

&lt;p&gt;We separated in 2017, with the divorce finalized in 2018. Still neither a lot of leisure time nor interest in picking this back up, too many other things to do. Like working my butt off to save for a down-payment on a place of my own and get out of the post-split rental.&lt;/p&gt;

&lt;p&gt;Last year, my girlfriend and I moved into my new place, and much time was spent on things house-related and new relationship-related.&lt;/p&gt;

&lt;p&gt;(FSM bless theatre and movies: My girlfriend and I became involved romantically while working pre-production on a film in mid-to-late 2017, and behind-the-scenes work on a few plays with &lt;a href=&quot;https://tototoo.ca&quot;&gt;Ottawa’s LGBTQ+ theatre company&lt;/a&gt; (she’s on the board, and we have a lot of friends and family (mostly logical, with a nod to Armistead Maupin), some lawful, some biological) in the LGBTQ+ community in Ottawa and elsewhere).)&lt;/p&gt;

&lt;h2 id=&quot;is-that-it&quot;&gt;Is that it?&lt;/h2&gt;

&lt;p&gt;Nah. I have to&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fix the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;atom.xml&lt;/code&gt; problem described above&lt;/li&gt;
  &lt;li&gt;Make sure a few other things about the site are working as they should (once I figure out or remind myself what &lt;em&gt;should&lt;/em&gt; is in this case&lt;/li&gt;
  &lt;li&gt;Update all my issues (&lt;a href=&quot;/How-I-use-GitHub-Issues&quot;&gt;cf my post on how I was using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ghi&lt;/code&gt; and GitHub issues back then&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Remove specific product versions from my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt;, making it more dynamic and robust&lt;/li&gt;
  &lt;li&gt;Add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt; rule to the site &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Makefile&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;And probably a few other things, too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, back to work for real.&lt;/p&gt;

&lt;h1 id=&quot;a-final-note&quot;&gt;A final note&lt;/h1&gt;

&lt;p&gt;When getting your Mac OS environment configured correctly, it’s worth using&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CMD T
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;every now and again to open a new tab: This will launch a fresh instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; and source your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt;, updating your environment to reflect any recent changes, e.g., as a result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install ruby&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;. While writing this post, I had &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; fail because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle&lt;/code&gt; was missing key environment variables: I’m writing this on my Mini and I had done most of the debugging work on my Air; my environment only had &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CPPFLAGS&lt;/code&gt; half-right, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt; failed.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD T&lt;/code&gt;, new instance of the shell, rerun &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt;, success.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Why people think software is so much easier than it really is</title>
   <link href="http://peter-whittaker.com/why-so-many-people-think-software-is-easier-than-it-is"/>
   <updated>2015-10-02T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Why-so-many-people-think-software-is-easier-than-it-is</id>
   <content type="html">&lt;p class=&quot;lead&quot;&gt;New software is &lt;emphasis&gt;net new&lt;/emphasis&gt; but most people work on maintaining or incrementally improving their status quo. Factor in their lack of understanding of what it takes to develop complex systems, and you might suddently understand why their reaction to your shiny new thing is &quot;What took you so long?&quot;&lt;/p&gt;

&lt;p&gt;A colleague works outside IT and seems to have a pretty good grasp of how complex IT really is. I mentioned to this person that sometimes a customer will, upon seeing “what they really wanted”, simply shake their head and say “what took so long”, blithely ignorant of the complexity involved. My colleague was, well, almost aghast that someone could think this way, and said “I’d not have thought people would be that, I don’t know, ridiculous? Why would anyone presume something like ‘oh that, yeah, you can do that!’ if they can’t do it themselves? That seems an awfully strange way to think (using the word very casually there apparently)”. This is my extended answer to that question.&lt;/p&gt;

&lt;p&gt;I think it has something to do with how usable and useful our tools have become: I think we have an implicit appreciation for the complexity “under the hood” (and that can be meant quite literally), inchoate and rarely if ever articulated, but we lack any kind of context for that complexity, any kind of appreciation for the years and in some cases decades of work required to hide that complexity under aesthetically pleasing veneers, be they body panels or airbrushed aluminum cases.&lt;/p&gt;

&lt;p&gt;For example, how complicated was it for you to start your car when last you drove? Was there risk of injury?&lt;/p&gt;

&lt;p&gt;Until Charles Kettering invented the electric car starter, a saga in and of itself, given the complex mechanical and electrical engineering required, car owners were advised to grip the starter crank handle with their fingers and thumb on the same side of the crank and to pull up. Why same side? To reduce the risk of a broken thumb or wrist or worse in case a backfire caused the crank to suddenly reverse. At immensely higher speed and force than the human had applied in the desired direction.&lt;/p&gt;

&lt;p&gt;And this was a commercial product that sold really, really well.&lt;/p&gt;

&lt;p&gt;Likewise, think of your independent suspension, automatic transmission, fuel injection, etc., etc., etc.&lt;/p&gt;

&lt;p&gt;We know it’s all there, we sort of grok that is fantastically complicated, but since we don’t get how hard it was to solve each individual problem, we take it for granted.&lt;/p&gt;

&lt;p&gt;And it doesn’t break! That’s practically a freaking miracle. Ask me why my Jeep has solid axles some time.&lt;/p&gt;

&lt;p&gt;A computer is possibly thousands of times more complex than a car (though far less deadly). Again, we grok the complexity, are ignorant of the effort, and take it from granted.&lt;/p&gt;

&lt;p&gt;Since we take it for granted that some smart person somewhere put all this complexity in a box, we assume it must be easy for one or two more smart people to add even more complicated functions.&lt;/p&gt;

&lt;p&gt;We miss the fact that it wasn’t one smart person that put it all in a box, it was a veritable army of armies, over decades. So we underestimate the effort to improve.&lt;/p&gt;

&lt;p&gt;Did you know that rocket engines - and all of their supporting infrastructure, fuel tanks, fuel pumps, etc., etc. - are all engineered to resist the destructive effects of fuel? Rocket fuel is so chemically reactive than it destroys the very vehicles it is powering, so part of the engineering is figuring out how to get fine propulsion control while making the parts as close to indestructible as they can reasonably be and still be light enough and cheap enough to put into the vehicle in the first place. Part of maintaining any shuttle was deciding which parts to replace; there were parts that were replaced every single mission, others every few. Very few, perhaps no, elements of the propulsion system would have lasted the lifetime of any specific shuttle.&lt;/p&gt;

&lt;p&gt;And there is another factor, a psychological one.&lt;/p&gt;

&lt;p&gt;Until someone gave you a computer, you had no idea what you could do with one.&lt;/p&gt;

&lt;p&gt;Then you got one, and went, oooh.&lt;/p&gt;

&lt;p&gt;Then they gave you Internet, and you went ooooooooh.&lt;/p&gt;

&lt;p&gt;Then they gave you a mobile phone, and you went oooooooooooooh.&lt;/p&gt;

&lt;p&gt;Each time the next new thing comes along, we make a few leaps of imagination: “Well, if it can do that…”&lt;/p&gt;

&lt;p&gt;Then we find the constraints, and are a little disappointed, but we still have leaps.&lt;/p&gt;

&lt;p&gt;Then another new thing, a leap, some constraints, some disappointment.&lt;/p&gt;

&lt;p&gt;Repeat.&lt;/p&gt;

&lt;p&gt;Finally, after years of effort, the work of an army of teams and of a few outstanding individuals comes together, and we finally get a device that does something close to what we imagined but could not articulate, years before. And our reaction is “FINALLY! What took you guys so long?”&lt;/p&gt;

&lt;p&gt;Now, tie together the two lines of thought, the complexity of engineering and the user psychology, by remembering that the vast majority of humans, especially the vast majority of managers, senior managers, and executives, deal with problems of managing people or machines as cogs: Learn to manage a team, get good at it, learn to manage a team of team leaders, get good at it, learn to manage managers, get good at it, etc., etc.&lt;/p&gt;

&lt;p&gt;There are additional nuances and subtleties at each level, new skills, and new layers of abstraction, but one has to be very high up the chain before one gets to complexity on a scale comparable to what senior technologists deal with. Everyday.&lt;/p&gt;

&lt;p&gt;A colonel or brigadier has a lot more riding on their decisions in terms of immediate consequences when directing their regiment or brigade in battle, but they have trained and drilled for weeks and months and years, and the overall operations are not that complex, compared to what happens inside many of our machines. Especially our networked machines.&lt;/p&gt;

&lt;p&gt;An engineer signing off on the design of a bridge is dealing with something a lot more complicated. If they get it wrong, almost as many people could die as in a battle.&lt;/p&gt;

&lt;p&gt;An engineer signing off on the design of a commercial passenger aircraft is dealing with something even more complicated. If they get it wrong, many, many more people could die than as in a battle.&lt;/p&gt;

&lt;p&gt;But we all see and appreciate the complexity of the senior officer’s job, and the immediacy of the impact of error or poor decisions, so we assume it is the harder one, the more stressful one, the more complex one.&lt;/p&gt;

&lt;p&gt;And we get that wrong.&lt;/p&gt;

&lt;p&gt;Pretty much every time.&lt;/p&gt;

&lt;p&gt;And we assume in our ignorance that the really cool thing that we just had delivered and are now charging couldn’t possibly have been that complicated, was just a matter of time, because we reason about using heuristics and yardsticks that just don’t apply.&lt;/p&gt;

&lt;p&gt;But wait, there’s more.&lt;/p&gt;

&lt;p&gt;Every time an engineer designs a bridge, they are essentially doing what hundreds and thousands of people have done before. There really isn’t anything new in bridge making, it’s incremental change over all previous bridges. Any new tools and techniques are created to enable a specific end, making a better bridge for less money.&lt;/p&gt;

&lt;p&gt;Every time a software developer creates a new program, they are creating something net new that never existed before. Sure, they may use code and libraries written by others, but those were written not to enable a specific end, but any and all ends. Often, the thing they write is similar to what others have done, but software developers are lazy, they try to not duplicate what others have done. Everyone wants to work on net new as much as possible, and the newier the better.&lt;/p&gt;

&lt;p&gt;The more they work at the pointy end of problem space, the more novel and more newily new is the net new they create.&lt;/p&gt;

&lt;p&gt;And that is the crux of the perception problem: Customers and managers without software development experience, whose entire existence is about maintaining or incrementally improving things we’ve been doing for a long time, simply lack the context and understanding to appreciate the novelty and complexity of the net new that comes with every new piece of code. And the newest and shiniest pieces of code are as far from the mundane new as the mundane new is from mundane maintenence-or-increment experience.&lt;/p&gt;

&lt;p&gt;So when we finally get the right pieces of code assembled in the right way, a common reaction is “what took you so long?”&lt;/p&gt;

&lt;p&gt;And now you know why.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Scott Aaronson, expertise, engineering, and the Bell inequality</title>
   <link href="http://peter-whittaker.com/scott-aaronson-bell-inequality-and-expertise"/>
   <updated>2015-09-28T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Scott-Aaronson-Bell-inequality-expertise</id>
   <content type="html">&lt;p class=&quot;lead&quot;&gt;A thought-provoking blog post by Scott Aaronson provokes more thought than I bargained for....&lt;/p&gt;

&lt;p&gt;Scott Aaronson has a rare combination of genius and communication skill: He does first rate research on computational complexity and follows the implications of that work in fields as disparate as drug research and evolutionary theory, AND writes about some of the most complex technical topics in a clear and accessible style, invoking math only when absolutely necessary (and usually you can just close your eyes and skip those parts).&lt;/p&gt;

&lt;p&gt;It so happens that on my birthday (thanks, Scott!) he published
&lt;a href=&quot;http://www.scottaaronson.com/blog/?p=2464&quot;&gt;a great article about Bell inequality violations&lt;/a&gt;. The article is great for several reasons.&lt;/p&gt;

&lt;p&gt;One, it describes the whole problem clearly, describes previous weaknesses with related experiments, and covers exactly why more recent experiments were so very, very clever.&lt;/p&gt;

&lt;p&gt;Two, it suggests that we are in for some wicked advancements in engineering in this space.&lt;/p&gt;

&lt;p&gt;Three, it contains that great line, “as well as I understand them”. This isn’t false humility, this is one of the brightest theorists working today admitting that the subject he writing about, while so close to his areas of expertise, is that far from his expertise.&lt;/p&gt;

&lt;p&gt;Fourth, the article - and point #3 - go a long way to supporting a point I’ve been trying to express well for a long time, and will inevitably fail to so express once again: The distances between each of common knowledge, learned knowledge, advanced knowledge, and truly leading edge knowledge are each so vast that most of us simply cannot comprehend how ignorant we are, despite our best efforts. Even experts in any particular field rapidly reach common levels of ignorance as they move outside their fields.&lt;/p&gt;

&lt;p&gt;Fifth, and last, true scientists recognize that and rarely if ever “speak from authority”, but revert to humility and to applying the basic, repeatable tools of their trade, critical thinking, empirical research, and questioning, questioning, questioning.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The right time to refuse a search is always</title>
   <link href="http://peter-whittaker.com/the-right-time-to-refuse"/>
   <updated>2015-05-08T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/The-right-to-refuse-is-always</id>
   <content type="html">&lt;p class=&quot;lead&quot;&gt;When should you refuse a search? Always. To do otherwise is to erode privacy rights, one concession at a time.&lt;/p&gt;

&lt;p&gt;A friend posted a video of a girl being tased because she refused a search and lipped off to a border agent (this was in the US, the friend is American, the commenters were a mix of Yanks and Canucks).&lt;/p&gt;

&lt;p&gt;Predictably, one comment was to the usual effect, that if she had nothing to hide, she should not have refused.&lt;/p&gt;

&lt;p&gt;Well, that got me riled. After I took a deep breath, this was my reply.&lt;/p&gt;

&lt;p&gt;The only reason to refuse a search is to protect your privacy. The question of whether or not you are hiding something is irrelevant.&lt;/p&gt;

&lt;p&gt;There is a reason you (Americans) have a Fourth Amendment and that we (Americans, Canadians, many others) all have prohibitions against unreasonable search and seizure.&lt;/p&gt;

&lt;p&gt;The starting point is that any and all searches are unreasonable, invasions of privacy. It is then up to legislators and the courts to specifically and reasonably allow searches under specific and reasonable conditions, balancing the privacy right against judicial oversight and probable cause.&lt;/p&gt;

&lt;p&gt;Refuse all searches, because you are American. I will refuse all searches because I am Canadian. This is what it means to defend freedom and liberty: Not with a gun, but with a stand. On principle.&lt;/p&gt;

&lt;p&gt;Always.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My first interview - and it's about Skipper Smith!</title>
   <link href="http://peter-whittaker.com/skipper-smith-interview"/>
   <updated>2015-02-28T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Skipper-Smith-interview</id>
   <content type="html">&lt;p class=&quot;lead&quot;&gt;I got interviewed about our upcoming movie, &lt;a href=&quot;https://www.facebook.com/Skippersmithmovie&quot;&gt;Skipper Smith&lt;/a&gt;! If you want to contribute, please visit our &lt;a href=&quot;https://www.indiegogo.com/projects/skipper-smith-and-the-really-important-thing&quot;&gt;Indiegogo campaign&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Q: Tell us a little about your acting background. What are some of the other films you have been in?&lt;/p&gt;

&lt;p&gt;I started acting about 7 or 8 years ago: My daughter and I auditioned for the local community theatre’s annual Christmas pantomime. It was a great way to spend time together but I wasn’t expecting to get hooked the way I did. I did another play, part of their regular season, and wanted more. But you marry a play, it becomes your life for weeks and months. Tough when you’ve got a job, a spouse, kids, etc.&lt;/p&gt;

&lt;p&gt;My first movie was an action film shot locally. I started with a small part at the end, but got “promoted” when someone didn’t show (I just don’t get that - you say you’re going to be there, you be there, you do what you said you would). I ended up with the first line and the first death. Kinda cool, really. Since then I’ve been eaten by a shark, stabbed to death by a spirit from beyond the grave, shot then hung by a treacherous deputy, puked on by a zombie, left for dead by the man who stole my life and my wife, been a comic goon, and, on smaller screens, I’ve sold cheese! Sometimes the lead, sometimes in the background, more often in between.&lt;/p&gt;

&lt;p&gt;Q: How is being in a comedy like Skipper different from other genres?&lt;/p&gt;

&lt;p&gt;When I first started, I didn’t know how things worked behind the scenes, how much waiting there is, how to manage and maintain my energy all day - to not burn it all off during the wait, to keep the right amount of stoke so that I could be “on” when my turn came - and how, uhm, I’m not sure what the right word is, not forgiving, maybe “flexible”? Yeah, how flexible movies are: You shoot a master, usually wide or half wide, then maybe come closer, maybe do a different angle, then do closeups, turn the camera around to get the other person, usually do several takes. That gives the director and the editor a lot to work with: The actor said his line perfectly, but his eye twitched? Show the other guy listening.&lt;/p&gt;

&lt;p&gt;Comedy ain’t like that. You cannot fix comedy in post the way you often can fix action or even drama. Sometimes the comedy depends upon the audience being able to see the whole scene in one shot, one take. On My Fair Zombie the male lead and I had a pretty important interaction toward the end of the movie, it was only going to work if we could do it in a single shot, and there was one bit of dialog I just could not spit out. We tried several takes and finally the director changed a couple of words. It worked, but it was frustrating for me - especially when I figured out how to accent the line about 5 minutes after we’d moved on.&lt;/p&gt;

&lt;p&gt;You just don’t know sometimes, until you are there under the lights with the camera rolling.&lt;/p&gt;

&lt;p&gt;Q: What is your favorite Indiana Jones movie and why?&lt;/p&gt;

&lt;p&gt;I think my favourite Indiana Jones is the first one. It flows nicely, the comedy is good, the bits that are supposed to be threatening are threatening, the music complements rather than overwhelms, the emotions are real. The second, well, it’s a prequel, and prequels lack threat somehow. I really liked the third, but sometimes they forced the emotion between Ford and Connery, though sometimes it worked really, really well. The fourth? I’d have to see it again….&lt;/p&gt;

&lt;p&gt;Q: What are you most looking forward to about the shoot?&lt;/p&gt;

&lt;p&gt;What am I looking forward to the most? Oh, that’s easy. The script. I’ve only seen about half so far, and there are so many L-O-L moments, so many really good jokes, some subtle, some in your face, some groaners, some witty urbane, some sophomoric. And it all works.&lt;/p&gt;

&lt;p&gt;I have to admit, though, that I am also sometimes totally freaked out. I mean, it’s Indiana-frikkin-Jones! An iconic character of our age, iconically Harrison Ford. When Brett offered me the part, I was just too enthralled to think of that, but as it sunk in I would get really, really nervous from time to time.&lt;/p&gt;

&lt;p&gt;But Brett starting sending me script updates as they became available, he and Trevor working from the beginning, so it gave me chunks to play with, to imagine. I got used to it one scene, sometimes one joke, at a time. Skipper is almost the vehicle for the comedy, it happens to him and around him in a lot of ways. In some ways, I just have to play it cool, play the adventurous archaeologist, and let the comedy happen. I’ve got some good comic lines, but it’s almost like the straighter I play it, the funnier it will be.&lt;/p&gt;

&lt;p&gt;Q: Anything you want to tell fans and potential Skipper supporters?&lt;/p&gt;

&lt;p&gt;Absolutely! The cast of this movie is awesome! I’ve worked with a lot of the folks involved before, but never all at once! It’s just, like, wow, I get to be on set with Candice and Christine and Ian and Lawrence and Ray and so many others, people whom I admire for the acting chops, their comic abilities (you gotta see Spyfall, Ray and Trev just steal their bits!). Brett has made two great comedies in a row, Zombie and Spyfall, both of which have won awards at various festivals, and Skipper Smith should be as good. I laughed when I read both of those scripts, but not as much as Skipper. If we pull it off, it will be a romp, a hoot (I think I just dated myself, eh?).&lt;/p&gt;

&lt;p&gt;We need help to pull it off, though, to pay for sets, and costumes, and locations, and cast and crew. Brett almost seems like a magician, making such good movies with small budgets, but he isn’t pulling things from the air. It’s hard work, and figuring out how to deliver high quality on low dollars is a real slog. The more money we have to take care of things that money is best at, the more time and energy we have for what the people are best at, which is the comedy, the creativity.&lt;/p&gt;

&lt;p&gt;We want to put our time and creative energy into the comedy, into the movie, not into the logistics behind the movie. That’s what we need your help for: Help us make the movie you want to see.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Avoid frustration with new gmail accounts, keep these things in mind....</title>
   <link href="http://peter-whittaker.com/argh-gmail-frustration"/>
   <updated>2015-02-13T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Argh-Gmail-You-Frustrate-Me-So</id>
   <content type="html">&lt;p class=&quot;lead&quot;&gt;tl;dr:Trying to set up &quot;send as&quot; as a new email address in an existing gmail or GAE account? Remember that Google disables both IMAP and POP3 by default in new accounts. If you read and retain nothing else, read and retain that. It will save you considerable grief.&lt;/p&gt;

&lt;p&gt;I’m doing some work that requires a dedicated email address, one that contains neither my company name nor any personal identifier. The why’s and wherefore’s don’t matter much, the important thing is simply that the client wants me to use such an address, the requirement is sufficiently reasonable and not worth arguing (bigger fish to fry and all that).&lt;/p&gt;

&lt;p&gt;Since this is going to be used for work purposes I want to keep all email sent to and from this address with the rest of my work emails. You know, for convenience of organization and reference, satisfying my inner neat freak, etc.&lt;/p&gt;

&lt;p&gt;I use Google’s email services exclusively: My personal email address is at gmail.com, my company email is hosted by Google App Engine (GAE), and I use the gmail web interface and the Android gmail app exclusively; I don’t use any other mail clients whatsoever.&lt;/p&gt;

&lt;p&gt;So it was pretty much a no-brainer that the new email address would also be at gmail.com (yes, this was acceptable, and I know that seems a bit strange, given the other restrictions imposed by the client, but just accept and move on, as I did :-&amp;gt;): Google makes it very easy to create a new gmail address and to have multiple gmail sessions in a single browser (at any given time I have multiple tabs open to my gmail.com and GAE addresses, and I keep personal and work emails strictly separate).&lt;/p&gt;

&lt;p&gt;OK, Google, +1 for making it easy to set up the new address. So far so good.&lt;/p&gt;

&lt;p&gt;I wanted to be able to send as this new address using my GAE business account, and, ideally, I wanted all email sent to this new email account to end up in my GAE business account.&lt;/p&gt;

&lt;p&gt;Simple, clear requirement, right? You would think it would be easy to do, right?&lt;/p&gt;

&lt;p&gt;Well, it is. As long as you remember that &lt;strong&gt;GMAIL DISABLES IMAP BY DEFAULT!!!&lt;/strong&gt; Sorry to shout (well, not really), but this caused for much frustration and lost time until I finally stumbled across this this morning.&lt;/p&gt;

&lt;p&gt;(Before you retort that this is obvious: No, it’s not. In fact, it makes only limited sense. It’s yet another example of Google assuming a) that they are the world and b) that they understand every use case or at least c) that use cases they do not understand don’t matter. Just think of how limited Inbox is, e.g. (completely unsupported with GAE, etc.).)&lt;/p&gt;

&lt;p&gt;I’ve enabled “send as” before, I knew where it was in “settings”. I found it, entered the required information, and caught what was reported as an authentication error. WTF? I won’t bore you with the many and varied steps I tried, including logging out of all gmail/GAE accounts, restarting the browser, logging back in, etc., etc., all of which worked…&lt;/p&gt;

&lt;p&gt;…and yet the “reported authentication error” persisted. I write “reported authentication error” because that’s what Google claimed: words to the effect of “Cannot establish connection, check username and/or password”.&lt;/p&gt;

&lt;p&gt;“Check username and/or password”. Not “connection refused, check host, check that IMAP is enabled, etc., etc.”&lt;/p&gt;

&lt;p&gt;Bang bang bang my head. Give up. Send email using new account, Bcc old account, leave problem for another time.&lt;/p&gt;

&lt;p&gt;After supper, decide to resurrect imapsync, which I’d used to migrate a previous job’s email from GoDaddy to GAE (worked like a charm).&lt;/p&gt;

&lt;p&gt;Cannot connect. WTF?&lt;/p&gt;

&lt;p&gt;(Note: As I write this I realize that I was blocked on the authentication error idea, and wasn’t getting enough distance from the problem to seek other causes. I was anchored, but good.)&lt;/p&gt;

&lt;p&gt;Bang bang bang my head. Give up. Go to bed.&lt;/p&gt;

&lt;p&gt;Try again this morning.&lt;/p&gt;

&lt;p&gt;Bang bang bang my head. AARGH!&lt;/p&gt;

&lt;p&gt;Poke, poke, poke. Well, it appears I won’t be able to set up sending as the new email from the work GAE.&lt;/p&gt;

&lt;p&gt;Well, at least I’ll configure retrieval of new email so that they end up in the GAE account. I’ve done this before…&lt;/p&gt;

&lt;p&gt;…but I click on the IMAP tab instead of the Accounts and Forwarding tab (don’t know why, don’t ask, lucky accident, really)…&lt;/p&gt;

&lt;p&gt;…and it’s there that the penny drops: In the GAE, IMAP is enabled (I had a reason for this, never disabled it). Drop, drop, click, click.&lt;/p&gt;

&lt;p&gt;Go to the new gmail account. Settings, IMAP. Enable IMAP.&lt;/p&gt;

&lt;p&gt;Return to GAE account. Try setting up send as again. “Oh, I made a bingo!” (Sorry, watched &lt;em&gt;The Zero Theorem&lt;/em&gt;, have Waltz on the brain.)&lt;/p&gt;

&lt;p&gt;Finally. I can “send as”. Yay.&lt;/p&gt;

&lt;p&gt;Whimper….&lt;/p&gt;

&lt;p&gt;Return to new gmail. Enable POP3. Return to GAE, enable fetching.&lt;/p&gt;

&lt;p&gt;Everything works.&lt;/p&gt;

&lt;p&gt;The bottom line?&lt;/p&gt;

&lt;p&gt;GOOGLE, WTF? THEY’RE YOUR OWN ACCOUNTS AND YOU CANNOT REPORT A MORE USEFUL MESSAGE, LIKE, MAYBE, &lt;em&gt;CHECK THAT IMAP IS ENABLED&lt;/em&gt; OR &lt;em&gt;CONNECTION REFUSED, NOT AN AUTHENTICATION ERROR&lt;/em&gt; OR SOMETHING!?!??!&lt;/p&gt;

&lt;p&gt;Nope, not sorry for the shouting. My second major “Google, WTF?” this week (see &lt;a href=&quot;https://plus.google.com/+PeterWhittaker/posts/FhNCzuvVee1&quot;&gt;a recent G+ post of mine&lt;/a&gt; if you’re curious).&lt;/p&gt;

&lt;p&gt;Another step closer to saying goodbye to Google. Most stuff just works, pretty well, but they do a completely shitty job with stuff people do rarely but need to have &lt;em&gt;just work&lt;/em&gt;. Sigh.&lt;/p&gt;

&lt;p&gt;If you are reading this paragraph, thanks. We must be siblings-in-frustration, we feel each other’s pain.&lt;/p&gt;

&lt;p&gt;Deeper sigh.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Canadian Government's archaic and embarrassing security screening management application</title>
   <link href="http://peter-whittaker.com/Canada-archaic-security-screening-management-embarrassment"/>
   <updated>2015-02-07T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Canada-and-the-archaic-app</id>
   <content type="html">&lt;p class=&quot;lead&quot;&gt;tl;dr: The Canadian Government&apos;s online security screening application supports pretty much all browsers on all platforms for 99% of the process - but that last one percent is Windows only, IE only, and Adobe only. Many of us have neither Windows nor IE, let alone Adobe, and even on Windows many of us use neither IE nor Adobe. The solution is simple: Upgrade the server side of the app - which hasn&apos;t been touched in 15 years or more - so that it generates the PDF instead of offloading PDF generation to the client. Done. Immediate support for most platforms, most browsers. And no need for the slightly inaccurate 28 step process supplied by government support agents if you know enough to question failure and are patient enough to wait to get through to a human being. Deep sigh. Proud to be Canadian, proud of most of what we do, not so proud of this made-in-Canada application.&lt;/p&gt;

&lt;p&gt;Many governments require that employees and contractors be screened prior to being granted access to certain types of information. Canada is no exception, with two distinct but related regimes, one for private information (information about people, business, etc.), one for sensitive government information (cabinet secrets, military information, intelligence, information, etc.). To access the former, you need a &lt;em&gt;reliability status&lt;/em&gt; (the often heard “enhanced reliability” no longer exists); to access the latter, you need a &lt;em&gt;security clearance&lt;/em&gt; at an appropriate level.&lt;/p&gt;

&lt;p&gt;So far so good.&lt;/p&gt;

&lt;p&gt;In an effort to simplify and streamline the administrivia required to obtain either a reliability status or a security clearance, Canada had provided a web application that allows a request to be initiated by a company security officer, assigned to either the applicant or to a security officer for completion (that is, for providing the pages and pages of data required), assigned to a security officer for submission to the government, etc.&lt;/p&gt;

&lt;p&gt;So far so good.&lt;/p&gt;

&lt;p&gt;Up until this point, the process can be successfully navigated with pretty much any browser on pretty much any platform. I’ve worked through this process with Firefox on both Linux and OS X, colleagues and friend have used Chrome or Firefox on Windows, OS X, and Linux, Safari, and within the government it’s all IE, all the time.&lt;/p&gt;

&lt;p&gt;So far so good.&lt;/p&gt;

&lt;p&gt;For reasons that will be neither discussed nor explained herein, the government requires a &lt;em&gt;wet signature&lt;/em&gt; on the forms: electronic signatures are insufficient, and &lt;em&gt;secure electronic signatures&lt;/em&gt;, as defined in Canadian law (PIPEDA and its regulations; perhaps I’ll write about this in a future post) are not supported at all.&lt;/p&gt;

&lt;p&gt;OK. Probably not so bad, right? Probably so far so good? Print the form, sign, scan and submit via email, or fax, or send the paper in, right?&lt;/p&gt;

&lt;p&gt;Almost. And not so good.&lt;/p&gt;

&lt;p&gt;While all the data exists on the server side, the government’s process offloads generation of a printable form to Adobe products tied into IE.&lt;/p&gt;

&lt;p&gt;And you have to submit their form of the form, not just all of the data as available in the form visible in your browser as you work through the process. Only a paper copy formatted exactly like the paper form originally designed almost two decades ago will suffice. And the only way to get it is with IE and Adobe.&lt;/p&gt;

&lt;p&gt;In other words, one can 99% of the way through the process with pretty much any browser on pretty much any platform, but can complete the process &lt;strong&gt;only&lt;/strong&gt; with Windows, &lt;strong&gt;only&lt;/strong&gt; with IE, and &lt;strong&gt;only&lt;/strong&gt; with Adobe.&lt;/p&gt;

&lt;p&gt;When I last used Windows, I had IE, one has to, after all, but I didn’t use it; it was there, but I didn’t use it. And I didn’t have Adobe at all: I find their PDF readers to be slow, unstable, and insecure, and I much preferred FoxIt and others of that ilk: Apps that could do one thing only - read PDF - and could do it really well.&lt;/p&gt;

&lt;p&gt;I don’t even have Windows anymore: My family and my company are all OS X (I was the last to convert, and then from Linux, having abandoned Windows long ago).&lt;/p&gt;

&lt;p&gt;Punchline? To finish the process, I have to seek friends or colleagues with Windows and IE and Adobe, and beg the use of their machines and printers for a short while.&lt;/p&gt;

&lt;p&gt;OK, you say, not so bad, probably pretty good.&lt;/p&gt;

&lt;p&gt;Except that it doesn’t work. At least not the first time you try it. To quote a friend,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is that the problem where you spend ages filling in forms, then click on PDF and all you get is the blank form? After a while you give up and get the security officer to print them for you?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s right. Blank forms. Because nowhere do they bother to tell you that there is a 28-step (I’m serious) process that you have to follow before this will work.&lt;/p&gt;

&lt;p&gt;I didn’t know that any of the other times I’d tried this, I would visit friends and colleagues and, if I had an on-site contract, government offices, logging into a supposedly secure system and trying again and again until it worked somewhere.&lt;/p&gt;

&lt;p&gt;This was the situation Thursday night: I tried, it failed. Friday morning, after being on hold for 40 or more minutes, I got the scoop: It was an issue with Adobe’s security settings, and there was a process.&lt;/p&gt;

&lt;p&gt;Offered without comment, here is the process - which I have to execute on a friend’s machine, hoping that they are either ignorant or sufficiently trustworthy of my skillage to know that I won’t harm their machine - and which I cannot execute at all on locked-down corporate or government machines:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Click the Start menu&lt;/li&gt;
  &lt;li&gt;Click “All Programs”&lt;/li&gt;
  &lt;li&gt;In “All Programs”, Locate Adobe Reader and Click to Start&lt;/li&gt;
  &lt;li&gt;A blank Adobe Reader page will open&lt;/li&gt;
  &lt;li&gt;From the top Adobe menu bar, Click “Edit”&lt;/li&gt;
  &lt;li&gt;From the Edit menu, Click “Preferences…”&lt;/li&gt;
  &lt;li&gt;In response to the Preferences menu selection, you will be presented with a new window called Preferences&lt;/li&gt;
  &lt;li&gt;On the left side of the Preferences, there is a Categories list box&lt;/li&gt;
  &lt;li&gt;From the Categories list box, Click “Trust Manager”&lt;/li&gt;
  &lt;li&gt;From “Internet Access from PDF outside the web browser”&lt;/li&gt;
  &lt;li&gt;Click “Change Settings…”&lt;/li&gt;
  &lt;li&gt;A new window called Manage Internet Access will open&lt;/li&gt;
  &lt;li&gt;From the Manage Internet Access window, Click “let me specify a list of allowed and blocked websites”&lt;/li&gt;
  &lt;li&gt;In the Allow/Block web Sites field, Type https://sedsi-oliss.tpsgc-pwgsc.gc.ca&lt;/li&gt;
  &lt;li&gt;Click “allow”&lt;/li&gt;
  &lt;li&gt;Then Click “OK”&lt;/li&gt;
  &lt;li&gt;From the Preferences window, inside the Categories list box, Click “Security (Enhanced)”.&lt;/li&gt;
  &lt;li&gt;From the Enhanced Security, Click “Add Host”&lt;/li&gt;
  &lt;li&gt;A new window will open called Add Privileged Host&lt;/li&gt;
  &lt;li&gt;In Add Privileged Host, Type: https://sedsi-oliss.tpsgc-pwgsc.gc.ca&lt;/li&gt;
  &lt;li&gt;Click “Secure connections only https” check box&lt;/li&gt;
  &lt;li&gt;Click “OK”&lt;/li&gt;
  &lt;li&gt;The user will return back to the Preferences window&lt;/li&gt;
  &lt;li&gt;From the Preferences window, inside the Categories List box, Click “Internet”&lt;/li&gt;
  &lt;li&gt;From within the “Web Browser Option” (top right) ensure that “Display PDF in Browser” has a check in the box&lt;/li&gt;
  &lt;li&gt;Click “OK” at the bottom of the Preferences window&lt;/li&gt;
  &lt;li&gt;Adobe Reader configuration is complete&lt;/li&gt;
  &lt;li&gt;Close the Adobe Reader&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These instructions were followed by the caveat&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Now go back to OLISS with your user id and password and try to print a request that was completed.  When selecting the “print” button, if the security screening form comes up and it’s still not populated with the individual’s information, try to clear/delete the browser Cookies and Temporary Files and restart your computer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, offered mostly without comment. Here are two (which I sent to the government support agent this morning):&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;With certain (more recent?) versions of Adobe, step 23 closes preferences entirely and the user must re-enter.&lt;/li&gt;
  &lt;li&gt;Once the process is complete, there is still a popup in Adobe that must be recognized and agreed-to; again, this could be an effect of a more recent version of Adobe.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wonderful, eh? 2015 and the government’s online security screening system requires this level of futzing about just to get things to work…&lt;/p&gt;

&lt;p&gt;…to get to the point where I can print the paper, collect signatures, and get the signed documents back, whether by scan and email, by fax, or by snail mail. Anything works, as long as they have them.&lt;/p&gt;

&lt;p&gt;They don’t tell you about this 28 step process anywhere on this site…&lt;/p&gt;

&lt;p&gt;…because they haven’t touched it in years. Other than to change the stuff and fluff around the app.&lt;/p&gt;

&lt;p&gt;This is how it should really work (this is was I sent them this morning as well):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The current system favours only Windows users, and excludes users of both OSX (a growing community: I regularly attend technical fora and meetups and it is more and common for private sector teams to be 100% Apple) and Linux (arguably growing, but who knows for sure). The retort “just use Windows under a VM” is empty and meaningless: Why would we, users of other platforms, spend money to license Windows just (and I must emphasize just) for this application?&lt;/p&gt;

  &lt;p&gt;Furthermore, even within Windows, the current system favours only users of IE and of Adobe: The last few times I had Windows machines, I didn’t even have Adobe installed, opting instead for lighterweight, more performant, more secure, and more stable PDF readers such as FoxIt - and there are many others.&lt;/p&gt;

  &lt;p&gt;This is particularly vexing when you consider that many of use get 99% of the way through the OLISS processes online using neither Windows nor IE, e.g., using Firefox or Chrome under OSX, only to have to scramble to find an appropriately configured Windows machine for that last 1%. The level of frustration in the community is considerable. Large organizations will not complain, as they tend to use older, more archaic technology, but smaller, nimbler players are considerably impacted by the government’s lethargy in this area.&lt;/p&gt;

  &lt;p&gt;There is, however, a simple solution: The technology for server-side dynamic PDF generation is cheap and easy to use; in fact, there are numerous viable open source alternatives.&lt;/p&gt;

  &lt;p&gt;Such technology could be integrated relatively easily and simply into the server-side of the OLISS process: OLISS would generate the PDF on request.&lt;/p&gt;

  &lt;p&gt;The end-result? All users of all modern browsers on all modern platforms would be supported 100% of the way through ALL OLISS online processes.&lt;/p&gt;

  &lt;p&gt;Please urge this upon your superiors and technical support teams. This would be fantastic step forward, and relatively inexpensive and straightforward one for your IT group.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Please feel free to share this with government support agents, decision makers, etc. Frankly, I find it embarrassing that my government’s systems are this archaic.&lt;/p&gt;

&lt;p&gt;Oh, did I fail to mention that this system hasn’t changed in, uh, 15 years or more? Sigh.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How I use GitHub Issues, labels, and milestones, to manage this site</title>
   <link href="http://peter-whittaker.com/How-I-use-GitHub-Issues"/>
   <updated>2015-01-17T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/How-I-use-GH-Issues</id>
   <content type="html">&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Update summary, 2015-02-14: Working as described, with one additional milestone, Unsure. More towards the end.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A major reason for having this blog here and not elsewhere was to learn git and GitHub (cf the &lt;a href=&quot;/colophon&quot;&gt;Colophon&lt;/a&gt;). Part of that is using GitHub Issues to manage things. Over the last ten or so years, I’ve become a huge fan of issue-based development management, or, perhaps more accurately, ticket-based change management (which subsumes both changes to code, i.e., development, and changes to operational systems): Having tickets means being able to track what is being done, by whom, when, and by what date; a little more process adds support for change approval mechanisms, rollback, escalation, etc.&lt;/p&gt;

&lt;p&gt;It’s more complicated than that, of course, but that’s the basic idea. Pretty much from the beginning of my work on this site, I started tracking site features and development using &lt;a href=&quot;https://github.com/PeterWhittaker/PeterWhittaker.github.io/issues/&quot;&gt;the issues associated with the site’s repository&lt;/a&gt; - but being a command line kinda person, first I found &lt;em&gt;an amazing, clean, simple, and effective, command line tool for opening, updating, closing, and otherwise managing GitHub Issues&lt;/em&gt;, &lt;strong&gt;&lt;a href=&quot;https://github.com/stephencelis/ghi&quot;&gt;ghi&lt;/a&gt;&lt;/strong&gt;. The install was pretty much a breeze:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew install ghi
ghi config --auth $YourGitHubID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only hiccup was that I had to run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ghi config&lt;/code&gt; command a few times to get the authorization to &lt;em&gt;take&lt;/em&gt;: An interaction between keychain and the GitHub API, I suppose, never did figure it out, just tried it a couple of times until it worked… …and since it was working, well….&lt;/p&gt;

&lt;p&gt;So my basic workflow is&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/* Have an idea */
ghi open
/* describe the idea in one line */
/* force myself to add a body, just in case I cannot later interpret that one line */
vi $theAppropriateFile
git add !$
git commit -a -m &quot;A useful description of what I did; closes #$IdeaIssueNumber&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sometimes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/* Have an idea */&lt;/code&gt; is inspired by other sites (e.g., &lt;a href=&quot;https://github.com/PeterWhittaker/PeterWhittaker.github.io/issues/47&quot;&gt;issue 47&lt;/a&gt; and &lt;a href=&quot;https://github.com/PeterWhittaker/PeterWhittaker.github.io/issues/23&quot;&gt;issue 23&lt;/a&gt;), other times it’s from running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ghi list&lt;/code&gt;, which shows me all current issues (assuming I am in the site’s top-level source folder).&lt;/p&gt;

&lt;p&gt;According to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ghi list|wc -l&lt;/code&gt;, I have 28 open issues right now. That’s not a lot, which means I don’t really need to put in much effort to manage them, but it’s also a nice, manageable, workable number if I want to start experimenting with labels  and milestones…&lt;/p&gt;

&lt;p&gt;…which was what inspired this particular post: After poking through a few articles on how to use GitHub Issues (your &lt;em&gt;google fu&lt;/em&gt; is as good as mine), both from GitHub itself and from GitHub users describing their processes, I realized that one of the great strengths of GitHub’s minimalist, nay, Spartan, approach to issues is that it forces absolutely no process on anyone.&lt;/p&gt;

&lt;p&gt;The flipside of this, of course, is that should one wish to go beyond having a flat list of issues, somehow using labels and milestones to manage them, one has to have a process in mind, a business problem to solve.&lt;/p&gt;

&lt;p&gt;The notional idea that I had prior to googling was managing dependencies. After reading, it wasn’t at all obvious how to do this: GitHub Issues has neither hierarchy nor dependencies, just labels and milestones.&lt;/p&gt;

&lt;p&gt;After some mental hemming and hawing, I decided on the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use labels to denote “clusters”, that is, related issues, and&lt;/li&gt;
  &lt;li&gt;Use milestones to denote sequence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve created the following milestones:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Anytime&lt;/li&gt;
  &lt;li&gt;Next&lt;/li&gt;
  &lt;li&gt;NextA&lt;/li&gt;
  &lt;li&gt;NextB&lt;/li&gt;
  &lt;li&gt;NextC&lt;/li&gt;
  &lt;li&gt;Roadmap&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Anytime&lt;/strong&gt; basically means quick little things I can or should do whenever the fancy strikes me. &lt;strong&gt;Next&lt;/strong&gt; is for things important enough and of the &lt;em&gt;right scope&lt;/em&gt; to be priorities (&lt;em&gt;right scope&lt;/em&gt; means both that I understand the scope and that they will fit in to the windows made available by the union of {my day job, my home life, my hobbies}). &lt;strong&gt;Roadmap&lt;/strong&gt; basically means &lt;em&gt;later&lt;/em&gt;: Things I think I should do but I’ve neither scoped nor considered carefully enough to know whether I really should do them.&lt;/p&gt;

&lt;p&gt;That leaves &lt;strong&gt;Next[ABC]&lt;/strong&gt;. Basically, &lt;strong&gt;A&lt;/strong&gt; comes before &lt;strong&gt;B&lt;/strong&gt; comes before &lt;strong&gt;C&lt;/strong&gt;. That’s it. Or almost.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next[ABC]&lt;/em&gt; are meaningless without clusters, which are denoted by labels, which will appear, change, and disappear, as work is done. I don’t have any good examples yet (I’m writing this post to consolidate the idea in my mind before I sift through the issues to apply it) but the basic idea is this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I have a cool idea, but it depends upon something I haven’t done yet.&lt;/li&gt;
  &lt;li&gt;Create an issue for the cool thing (call it issue 2)&lt;/li&gt;
  &lt;li&gt;Create an issue for the thing I haven’t done yet (call it issue 3)&lt;/li&gt;
  &lt;li&gt;Create a label that captures the essence of why I am going to do those two things. In this case, it might be “CoolThing”. Give that label a colour not currently in use.&lt;/li&gt;
  &lt;li&gt;Apply the label to the two issues.&lt;/li&gt;
  &lt;li&gt;Associate issue 3 (the one that has to come first) with milestone “NextA”.&lt;/li&gt;
  &lt;li&gt;Associate issue 2 (the cool thing) with milestone “NextB”.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, when “Anytime” is either empty or boring and “Next” is empty, look for things in Next[ABC] to move to Next. There is only one rule: &lt;strong&gt;Never move anything from NextB or NextC to Next if there something with the same label in NextA&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;(In my mind, that rule is simpler than having to always shift items from NextC to NextB to NextA as items are popped from NextA and NextB.)&lt;/p&gt;

&lt;p&gt;Why Next[ABC] and not Next[A-F], e.g.? Well, anything more than three levels of dependency likely means I don’t understand the scope and magnitude and complexity of what I am doing. Having said that, it is quite possible that NextA for label &lt;em&gt;Fred&lt;/em&gt; might be a simple thing, NextB for Fred a little more complex, and NextC a lot more. That might mean than when I close the issue for Fred in NextA, I might then break the NextB issue into two, and put one of them in NextA.&lt;/p&gt;

&lt;p&gt;Or at least that’s what I’m thinking right now. I don’t want to overdesign nor do I want to overproscribe (though it does feel overdescribed by now, eh?).&lt;/p&gt;

&lt;p&gt;Enough for now. I’m going to start categorizing things. I’ll report back on this after I’ve played with it for a bit.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Update, 2015-02-14

I&apos;ve been meaning to add this update for a while now: I proceeded as described above and found it worked pretty darned well. I get a nice view of how issues relate using ghi to list issues, labels, etc., at the command line, while the view in GitHub Issues is workable, at least for a small number of milestones.

Important point: If you want to work this way, figure out what your milestones are first, then figure out the order in which you want to display them, then create them in that order: GitHub (or maybe it is just ghi) will assign each one a number, and, by default, present them in numerical order, even if the number is not displayed (it is in ghi, it is not in GitHub Issues, at least not as far as I can see).

One change from the original post: As I began working with milestones as buckets for when things should happen, I realized I needed another bucket, Unsure, which is for things that aren&apos;t in the roadmap (and hence shouldn&apos;t be brought forward) but which should not yet be dismissed. Think of it as a &quot;check again later&quot; sort of holding pen. Fortunately, given that I thought of this bucket last (cf important point, above), it is fortunate that it should indeed appear last. Serendipity FTW.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Why I act</title>
   <link href="http://peter-whittaker.com/why-I-act"/>
   <updated>2015-01-15T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Why-I-act</id>
   <content type="html">&lt;p&gt;(Another Facebook-inspired post, this one because of a comment “you should always get paid, never act for free”.)&lt;/p&gt;

&lt;p&gt;Why is everyone assuming that people act because they want to get paid for it?&lt;/p&gt;

&lt;p&gt;The likelihood of me making as much acting as I do on my day job is, well, practically infinitesimal.&lt;/p&gt;

&lt;p&gt;I act because I thrive on it. I act because I crave the art. Payment is lagniappe. My cheese commercial has been playing for almost two years, I still get comments about it. It paid better than all of my other acting gigs combined (and it paid, by hour or by day, better than my day job, and that is saying something).&lt;/p&gt;

&lt;p&gt;And since that commercial, I have been shot and hung by a treacherous deputy, swallowed by a shark, had a gunfight with Jesse James, been puked on by a zombie, and am in pre-production on a few other things, to name but a few.&lt;/p&gt;

&lt;p&gt;If I get as much as lunch money from my gigs, I’m happy. Hey, I’d be even happier (maybe) if I could do this full time. And yes, I know there are people who succeeded starting as late as I did - but that’s not why I’m do it.&lt;/p&gt;

&lt;p&gt;I act for the sheer joy of it. I’m good at it, as it turns out, but that’s only important because it allows me to work over and over again with some of the best people I’ve ever met.&lt;/p&gt;

&lt;p&gt;That’s why I act.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Charlie Hebdo, I can manage. Boko Haram, not so much</title>
   <link href="http://peter-whittaker.com/charlie-hebdo-and-boko-haram"/>
   <updated>2015-01-13T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/Charlie-Hebdo-and-Boko-Haram</id>
   <content type="html">&lt;p&gt;(From a comment on yet another Facebook conversation….)&lt;/p&gt;

&lt;p&gt;When I first read of the 2000, I was stunned. Then I realized that that was all I would ever be: Stunned. I simply do not have the mental or emotional capacity to perceive 2000 sudden brutal deaths. That’s twice our entire high school population, 6 times our graduating class, killed in a matter of hours.&lt;/p&gt;

&lt;p&gt;Call it a human failing. I fancy that I can wrap my head around a dozen journalists killed for promoting one of my highest values, self-expression and free thought. I cannot wrap my head around the destruction by a madmen of a small village and its heterogeneity and promise.&lt;/p&gt;

&lt;p&gt;So I focus on those I can deal with, those I can frame in my mind.&lt;/p&gt;

&lt;p&gt;One tragedy is manageable, the other overwhelming.&lt;/p&gt;

&lt;p&gt;We recoil from horror writ large, ignore it, forbid it from entering our minds, touching our hearts, sapping our souls.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Sony Vs North Korea (really?)</title>
   <link href="http://peter-whittaker.com/sony-vs-nk"/>
   <updated>2014-12-19T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/SONY-Vs-NK</id>
   <content type="html">&lt;p&gt;This afternoon a Facebook friend wrote to me in a comment,&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I’m sure you’ve been fascinated by this entire ordeal [The Sony compromise, accusations against North Korea, etc.] too! I mean really, an unencrypted folder called “Passwords”? &lt;em&gt;sigh&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fascinated? Not so much fascinated as jaded. Most large corporations simply fail to take security seriously. In 25 or so years in the field, I’ve seen far too many people who are experts in other areas believe they can simply master security (or software development) with very little time and training. “How hard can it be?”&lt;/p&gt;

&lt;p&gt;Anyone who has ever worked in IT or ITS has received tremendous amounts of helpful advice from experts in HR, accounting, finance, etc., all of whom think their experience is portable, transferable, translatable. But how many of those experts ask the IT or ITS folks for advice on their work? NONE. And why is this?&lt;/p&gt;

&lt;p&gt;Oh, it’s obvious? If it’s so obvious one way, why isn’t it obvious the other?&lt;/p&gt;

&lt;p&gt;Witness RSA itself, for a long time the world leader in secure authentication systems. Until their entire network was compromised over a periods of months in a successful effort to break their authentication systems in order to attack one of their largest clients.&lt;/p&gt;

&lt;p&gt;And these are folks who should have known better. As for Sony, they have a long history of bad IT, bad ideas, and worse responses. Witness the root kit episode a few years back.&lt;/p&gt;

&lt;p&gt;Witness CurrentC: When Apple introduced iWallet, retailers supporting CurrentC disabled NFC for iOS devices to hamstring their future competitor, a move widely seen (rightly IMHO) as anti-consumer, anti-choice.&lt;/p&gt;

&lt;p&gt;Within days, CurrentC had been compromised by hackers offended at their position. They simply were not ready to play in that league, but somehow were arrogant enough to think that because they could several other things well they could ITS well.&lt;/p&gt;

&lt;p&gt;Wrong, thanks for playing.&lt;/p&gt;

&lt;p&gt;As for blaming NK, well, sigh. Holding up a credible (or better yet, incredible?) strawman simply diverts attention from those who are truly responsible: Sony.&lt;/p&gt;

&lt;p&gt;Sony didn’t attack itself, that’s not what I mean. What I mean is that IMHO Sony was negligent. At this point, wilfully (they could not not have known otherwise). Whether criminally, well, IANAL and all that.&lt;/p&gt;

&lt;p&gt;I am so looking forward to the day when a Home Depot or Target or Sony or other is found criminally negligent for the exposure of customer records following a compromise. I’m not really sure whether I’m interested in anything happening to the attackers. I just think it’s about time that companies that treat and truck with so much employee and customer data start treating it properly.&lt;/p&gt;

&lt;p&gt;The thing is, good security is actually cheaper than the opposite: Design security in from the beginning, small incremental cost.&lt;/p&gt;

&lt;p&gt;Get compromised, huge cost. Simple economics.&lt;/p&gt;

&lt;p&gt;Big company with large Internet presence? Visible target, will be attacked. Guaranteed. So pay a little now or a lot later. Simple economics.&lt;/p&gt;

&lt;p&gt;(A few years ago I prepared a short presentation to justify that last statement for a client. I’m going to dig it out and turn it into a blog post sometime soon. The graphic on relative costs at various stages ranging from design to operations is really quite striking. Stay tuned.)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OK, now what?</title>
   <link href="http://peter-whittaker.com/ok-now-what"/>
   <updated>2014-12-18T00:00:00+00:00</updated>
   <id>http://peter-whittaker.com/direction</id>
   <content type="html">&lt;p class=&quot;lead&quot;&gt;tl;dr: What do you want me to write about first/next? The experience of setting up this blog? IT Security? Acting? Something else altogether? Let me know via the various social/feedback links available herein.&lt;/p&gt;

&lt;p&gt;I’d been thinking of setting up a blog for long time. Actually, I’ve been thinking of setting up two, one personal (right here!) and one for &lt;a href=&quot;www.edgekeep.com&quot;&gt;EdgeKeep&lt;/a&gt;, my consulting company. But there were the questions: Where to host? What software to use? Yada etc. and so on.&lt;/p&gt;

&lt;p&gt;I’d also been thinking about learning git, GitHub, etc. I’ve been exposed to them a few times, but never enough to really sink in my teeth, and having a working knowledge of these tools is practically &lt;em&gt;de rigueur&lt;/em&gt; for some tech groups and meetups I enjoy.&lt;/p&gt;

&lt;p&gt;Long story short, I combined the ideas and built this, the site you are visiting to read this post. May not seem like much, and for an experienced git user and web developer, it isn’t. But for someone who had practically zero hands-on experience with git, web development, templating, and especially the interplay between CSS and HTML, it was sumthin’. I learned a lot. Made fewer mistakes than I expected, but did have some gut-clench moments when I &lt;em&gt;thought&lt;/em&gt; I’d made mistakes (git rebase is very, very scary and will remain so until I understand it better).&lt;/p&gt;

&lt;p&gt;I started banging on this site in late November. It took me until yesterday to get it where I wanted, to the point I could write this post and publish it in just a few minutes, with either just a few clicks (ew) or just a few commands (vim and git push FTW!).&lt;/p&gt;

&lt;p&gt;Pretty much from the beginning I kept an eventual blog post about the experience, one intended to help other technically savvy but git-and-web inexperienced people do what I did, a complete tutorial and reference in one as-brief-as-possible, as-comprehensive-as-necessary article.&lt;/p&gt;

&lt;p&gt;The draft - and it’s decently high quality - is over 6000 words. So far. I reckon the complete article will push 9,000. That’s a lot of content yet to come, plus the TLC to turn a draft of any quality into a readable article.&lt;/p&gt;

&lt;p&gt;And I’ve got the high-and-doldrums that come with completing anything, the &lt;em&gt;I did it! What’s next…&lt;/em&gt; feeling.&lt;/p&gt;

&lt;p&gt;So I ask you: What’s next? Or, more to the point, what’s first? Do you want me to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Complete the “how I got here” article? It’ll be a while….&lt;/li&gt;
  &lt;li&gt;Write about IT security?&lt;/li&gt;
  &lt;li&gt;Discuss consulting?&lt;/li&gt;
  &lt;li&gt;Talk about acting?&lt;/li&gt;
  &lt;li&gt;Share an exciting and potentially wordy anecdote about my life?&lt;/li&gt;
  &lt;li&gt;Something else altogether?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve yet to add commenting to the blog (&lt;a href=&quot;https://github.com/PeterWhittaker/PeterWhittaker.github.io/issues/28&quot;&gt;it’s on my list&lt;/a&gt;), but elsewhere on this site there are social links and a link for creating a new issue for site problems. Feel free to use one or more of those to let me know what you would like to read next.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</content>
 </entry>
 

</feed>
