How I Take Notes

The older I get, the more notes I take. Historically I've been a terrible at it.

I have exercise books from the past 10 years at work, filled with useless scribble - the working notes for algorithms and data structures, doodles and the occasional meeting note. Undated, uncategorised and completely inaccessible. I've tried to start a diary every couple of years since G.H.W Bush invaded Kuwait. Until late last year, none of them lasted more than a month.

The keys to me taking more (and better) notes have been speed, access and simplicity. Regardless of my intentions, I don't take notes unless it's quick, I can do it anywhere, and I don't have to think about the mechanism.

My first push in the right direction came from reading Mind Performance Hacks. The main "hack" that left an impression on me was having a system to catch ideas as they occur, instead of trying to remember to write them down later. They had some detailed system about carrying (physical!) cards with columns on them, but that was never going to fly. Instead, I came up with my own system based on text files, which I can easily modify on my phone, tablet or computer (one of which is almost always handy).

I created a folder called "catch" and added one file per category - for example, "blog". Whenever I had an idea for a blog post, I'd add it in. I decided to take notes on some books I was reading, so I added another folder called "books" and created a document for each book.

I use btsync to sync the files across my devices, but you could easily use Dropbox, Google Drive etc.

This worked well enough, but I found it a little slow and cumbersome. I wanted to be able to get the idea out of my head quickly, without having to find the right file. Enter the bash script!

function record(){
    output=*"$(date +%F)$(date +%r)* "$3" \n\n"
    target="$1"/"$2".md

    #echo "Writing \"$3\" to $target"
    if [ -z "$2" ]; then
        ls "$1"    
    elif [ -z "$3" ]; then 
            vim "$target"
    else
            printf "$output" >> "$target"
    fi
}

alias catch="record ~/notes/catch"
alias book="record ~/notes/books"

At work I usually have a cygwin terminal open, and at home I spend most of my time in a terminal. My record() function (and the aliases that point to it) let me really quickly take notes. For example, if I type

$ catch blog "How I take notes"

a new line, prepended with the date, is added to my blog file:

*2014-10-29 09:11:29 PM* How I take notes 

If I type catch blog, my blog file opens in my text editor. If I type catch, I get a list of my catch files/categories.

Lately, I've started taking notes about what I do at work:

$ wl "Adding <feature> to <program>:
> - remember the <blah> has to be hooked up to the <hoosit>"

This feels like it will be very beneficial going forward, particularly for those rare jobs whose precise details I always forget.

For well-categorized ideas, I can search within a document to find something. If I can't remember where exactly I put something, I can use the usual file system tools to help me. grep happily tells the files that match the phrase I'm looking for:

$ grep -rin "notes" .
./blog.md:5:*2014-10-2909:11:29 PM* How I take notes 

I've been taking notes for about a year, and I'm starting to see the benefits. Each time an idea resurfaces, I flesh out the notes a little more, so the concept progresses.


clearInterval() workaround for some Android browsers

Speedy is written with Cordova, using HTML and Javascript to create an installable (and sellable) Andorid app. Cordova uses the Android browser to render the appliation, which in general works quite well. However, I ran into a problem on some devices with window.clearTimeout(), and it took me a while to work around it.

At its core, Speedy is just a function that runs every so often to display some text. The idiomatic way to do this in Javascript is with setInterval(). In the simplest case, you'd use it like this:

var intervalId = window.setInterval(function(){console.log("Hi")}, 1000);

When you want to cancel the interval, you'd use cancelInterval():

window.cancelInterval(intervalId);

This all works fine, even on the 'problem' browsers. In fact, moving back to this simpler usage ended up being the solution to my problem.

Because I'm using Angular, I decided to make a somewhat more idiomatic wrapper for setInterval():

function $interval(fn, ms){
    var t = window.setInterval(function(){$scope.$apply(fn)}, ms);
    return function(){ window.clearInterval(t)};
}

...

var stop = $interval(function(){console.log('Hi')}, 1000);

...

stop();

To me this nicely encapsulates the set/clearInterval() calls. It works in desktop browsers, on my Nexus 4, and even on my ancient Huawei 8150 running Android 2.3. It failed on my Samsung Galaxy S running 4.2.2, and I had reports of it failing on an HTC One S running 4.1.1 and on an HP tablet.

I haven't precisely tracked down the issue, but it seems that the function returned by $interval is failing to properly close over t. In any case, the solution was to return the interval id, rathern than a function, and pass it to window.cancelInterval() when required.

On a related note, as part of my testing I found that Angular's $timeout() (which wraps window.setTimeout()) also does not work on some Android browsers. Even worse than with setInterval(), $timeout() never starts at all. I'm not sure if this is an issue with closures in general (although I use them elsewhere in the codebase), or if somehow interval/timeout are particularly sensitive to being wrapped.


Advanced Mercurial

I've been using Mercurial for roughly 3 years. By and large the basic pull/push/branch/merge/commit commands have done everything I've needed. Lately I've been feeling a little constrained, perhaps because git is opening my eyes to new concepts.

I had started to think that maybe Mercurial just wasn't that good. It turns out I just didn't know how to use it.

(A lot of these tips and tricks come from Steve Losh)

Secret Business

hg phase -fs

A changeset in mercurial has a phase associated with it: secret; draft; or public. A new (unpushed) commit creates a draft changeset; pushing it makes it public. This is usually all hidden away, but you can manually set the phase of you want. In particular, setting it to secret means that it will not be pushed when you push the rest of the repo. This (for example) will let you maintain a private bookmark without pushing a new head.

You can also set a new commit to secret using hg commit -s.

To make a changeset (and its ancestors) pushable again, change its phase to draft:

hg phase -d

Push like Git

hg push -r .

Mercurial uses '.' as shorthand for "the parent changeset of the working directory", ie usually the head of the branch you are on. Using -r to specify the revision to push, along with '.', means that you only push the current branch. Again, this is a way to maintain a local branch/bookmark.

Don't look stupid

hg commit --amend

Adds the changes in your working directory to the last (draft/unpushed) commit. So when you commit then see a stupid mistake, you can fix it and amend the commit. ie, no more commit -m typo

Avoid merge commits

hg rebase

Rebase takes a series of changesets and moves them to a different parent. In essence, it lets you clean up local history and avoid polluting the hg timeline with a bunch of merge commits.

Let's say you’re working on an issue on default. Once you're done, you pull and because someone else has committed to default, there's now two heads. Rather than just merge them in, you can rebase your changes on top of the other/public head:

hg rebase -m "Fixes issue 1765"

By default, rebase uses the parent of the working directory as the base, but you can specify it explicitly with -b <changeset>. You can also set the destination with -d.

Rebase starts at the “-b” revision, then works backwards until it finds a common ancestor with the destination. It takes all of the commits after that ancestor, 'replays' them as children of the destination destination changeset, then deletes the originals. Note that you might have to resolve multiple merge conflicts as hg works its way through the changesets.

If all that sounds crazy, think of it this way. If you have uncommitted changes in your working directory, pull then update (without -C), hg automatically attempts to "rebase" your uncommitted changes onto the new tip. This is just a fancy way of doing something similar with work you’ve locally committed.

Rebase is a plugin that needs to be enabled, and can only be used on draft/unpushed changesets.

Show progress

There’s a progress bundled extension for hg, which gives you a progress indicator for long-running operations. You just need to enable in [extensions] and also set verbose = False under [ui]

Shelve to change branches

hg shelve
hg unshelve

You do a bunch of work then realise you’re on the wrong branch. Often Mercurial will let you switch branches, but sometimes it can’t rebase your uncommitted changes cleanly onto the new branch.

hg shelve puts all the uncomitted changes in your working directory aside. It’s like hg update -C, except you keep a copy of your changes. hg unshelve re-applys the changes to your working directory, after you’ve done whatever else you like (pull, update, change branches etc).

Shelve is an extension that you need to enable.

Patch a changeset to another branch

hg export -o patch <revisions>
hg import patch

Lets say you commit a change on your dev branch then later realize that it needs to be on prod. You could copy it across manually, or you could create a patch. I've had bad luck creating a patch with TortoiseHg in the past, but the commandline version works perfectly. Note that this takes only the diff for the list of revisions you give it, so you need to make sure to include everything you need.

Useful Stuff

I have a bunch of random useful extensions and aliases in my mercirial.ini/hgrc:

[extensions]
purge=
rebase= 
graphlog= 
progress=
shelve=

[ui]
verbose = False

[alias]
co = commit -m
secret = phase -fs
draft = phase -d
last = log -l 5 --style compact
preview = merge --preview 
addremove = addremove -s 100
untrack = rm -Af
amend = commit --amend
nudge = push -r .
glog = glog -l 10 --template "{rev}:[{branch} {bookmakrs}] {author|user}: \"{desc}\"\n"
repull = pull --rebase

Make it look pretty

If you’re using bash (on linux or cygwin), you can make your life a lot easier by showing the current branch and bookmark in the command prompt. Add this to your .bashrc:

hg_branch() {
            hg branch 2> /dev/null 
}

hg_bookmarks() {
            hg bookmarks 2>/dev/null | awk '/\*/ {print $2}'
}

DEFAULT="\e[37;40m"
PINK="\e[35;40m"
GREEN="\e[32;40m"
ORANGE="\e[33;40m"

export PS1="\n${GREEN}\w ${ORANGE}\$(hg_branch) ${PINK}\$(hg_bookmarks)${DEFAULT}\n$ "

This will give you a prompt that looks like this:

~work/tmp default dev
$