tech


github forking

2011-09-09 16:25:23 -0700

I don't think GitHub's default workflow for forking a repo is ideal for my needs. Most of the time when I fork a repo, my aim is to fix something or add a small new feature which I will eventually submit upstream as a pull request. While I am hacking away or living with my modifications for a while before I submit them, I want to continue to be able to easily pull in upstream changes as well. So whenever I fork a repo, I want the following:

  • The master branch will track the pristine upstream master. Whenever I have no changes of my own pending, I can just switch my work tree to use master to keep up with the latest changes with a simple git pull.
  • A volatile hack branch where I work on my changes. This is volatile since I'll regularly be rebasing onto the head of master.
  • A volatile pullme branch where I will put changes that are ready for a pull request submission. I may want to continue to hack away in hack while I'm waiting for the resolution of my pull request, so this is analogous to a release branch. But this is volatile since, for the sake of simplicity, I'll be resetting it whenever I'm ready to submit a new pull request.

(By volatile, I mean that I can feel free to rewrite the history on these branches without repercussions.)

So here's how I go about forking a GitHub repo:

git clone <upstream_repo>         # e.g. http://github.com/mxcl/homebrew.git
cd <upstream_repo_name>           # e.g. homebrew
git remote add fork <forked_repo> # e.g. git@github.com:SethMilliken/homebrew.git
git config branch.hack.remote fork
git config branch.hack.merge refs/heads/hack
git branch --no-track hack origin/master
git push fork hack:refs/heads/hack
git checkout hack

At this point here's how things look branches-wise:

$ git branch -r
* hack
  master
  remotes/fork/hack
  remotes/fork/master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

remotes/fork/master is what the GitHub website uses for it's default branch. I like to clean this up to remove any chance of ambiguity about the name master. To me, master always means the authoritative branch from the canonical repository, never a branch in my fork. Here is how I tidy this up:

  1. Click the Admin button on the forked repo's GitHub page.
  2. Change the Default Branch to "hack".
  3. Delete the forked repo's master with git push fork :master.

If there were a way to name the default branch when you first fork, or to subsequently rename it via a commandline git command, that would simplify things a bit.

To rebase my changes onto incoming upstream, which I do periodically as I'm hacking, and again immediately before submitting a pull request:

git pull --rebase origin master

When I'm finally ready to submit a pull request, here is what I do:

git branch -f pullme hack
git push fork pullme:refs/heads/pullme

This essentially replaces the pullme branch with the current state of the hack branch.

And after my pull request is accepted, I switch back to master.

git checkout master


regenerate missing public key

2011-05-11 17:57:21 -0700 I've had to do this a surprising number of times lately, and have had to figure it out anew each time. So here it is—for my future reference—how to regenerate from your private key the missing corresponding public key in such a way that it is usable in the ~/.ssh/authorized_keys file:
ssh-keygen -i -f <( ssh-keygen -e -f id_rsa ) > id_rsa.pub


hg stash

2011-02-28 21:34:32 -0800

I have become accustomed in Git to using git stash to temporarily set aside in-progress changes in my working directory before pulling from upstream. Whenever I use Mercurial now, I find myself wanting analogous functionality. The Googles don't seem to turn up any decent techniques for this, so I have repeatedly reinvented a series incantations to abuse Mercurial's mq extension to provide it. Yet I always manage to forget whatever trick I used by the time I want to do it again. I finally decided to fix it for good, giving myself hg stash and hg pop, allowing a workflow in a dirty working copy that goes something like:

hg stash
hg fetch
hg pop

Here are the relevant .hgrc lines:

[alias]
stash = !hg qinit &>2 /dev/null; hg qqueue --create stash-temp && hg qnew stash && hg qfinish tip && hg strip tip && hg qqueue patches && hg qqueue --purge stash-temp
pop = !hg -R .hg/strip-backup/`ls -rt .hg/strip-backup/ | tail -1` diff -r tip | patch -p 1 -R

Breaking it down:

# hg stash
hg qinit &>2 /dev/null              # initialize mqueue in this directory
                                    # ignore error output since this has likely already been done
                                    # (this could be potentially be problematic if there is some other problem)
hg qqueue --create stash-temp       # create a new patch queue so we don't mess with any existing ones
hg qnew stash                       # create the 'stash' patch with the outstanding modifications
hg qfinish tip                      # apply the patch
hg strip tip                        # now strip it (for the side-effect of the backup bundle)
hg qqueue patches                   # switch back to original patch queue
hg qqueue --purge stash-temp        # delete the temporary patch queue
# hg pop
hg -R .hg/strip-backup/`ls -rt .hg/strip-backup/ | tail -1`     # use the strip bundle as the repository
                                                                # finding it with some shell tricks
                                                                # (likely a better, more portable way to do this)
diff -r tip                                                     # output it as a diff
patch -p 1 -R                                                   # apply that output as a patch
                                                                # and reverse patch since we spun things around
                                                                # with the bundle

Your working copy's .hg/strip-backup/ directory could start to fill up if you use this a lot, so you may want to clean it out occasionally. I decided to not have hg pop automatically cleanup the bundle just in case something goes wrong.

And why not just hg diff > stash.diff && hg revert --all to stash and patch -p 1 < stash.diff to pop? That doesn't really handle binaries, and somehow it feels a little too dangerous to me. But, yeah, for the majority of cases that would probably be sufficient. So I'll leave that here, too:

[alias]
stash = !hg diff > stash.diff && hg revert --all
pop = !patch -p 1


Pentadacytl (or Vimperator) Form Field Editing

2010-03-16 20:05:32 -0700

I have been using Firefox as my primary browser for quite a while now. What held me back from switching initially were two things: 1) unacceptably poor non-native widgets, which have since improved dramatically and 2) no support for the Mac OS X Keychain, which is now available via the Keychain Services Integration extension. But eventually my desire for vim keybindings everywhere pushed me to switch despite these shortcomings. The vimperator pentadactyl Firefox extension allows for entirely keyboard-based browsing, using bindings similar to vim. And even better, it facilitates form field editing using vim via external editor support. So when I want to edit a form field, I type either "gi" (to enter the field) or "f#" (where # is the QuickHint mode number associated with the field; useful if there are multiple fields on the page) then hit Control-i. This creates a new tab with a buffer for that field in an existing instance of MacVim which is always running in the same Space as my browser, so it comes up almost instantaneously. I edit the contents of this buffer using all the goodness of native vim and then type ZZ. Thanks to a couple of autocommands in my .vimrc, this saves the buffer, saves a copy of its contents in a timestamped archive file, closes the buffer, and then hides MacVim, leaving Firefox active with the text I just wrote inserted in the form field, ready to submit.

Here are the requisite pieces of the puzzle:

.pentadactylrc

This is the pentadactyl configuration file. You need to add a line to set your external editor to the script that opens MacVim.

set editor="~/bin/firefox"

shell

Make a symbolic link to your mvim script so that the script can detect the context it should run in.

ln -s ~/bin/mvim ~/bin/firefox

mvim

This is the mvim script that comes bundled with MacVim, moved to ~/bin and modified. Add this bit to the 'case "$name" in' statement:

    *firefox)
        opts="$opts --remote-tab-wait-silent"
        gui=true
        ;;

.vimrc

Next, a couple of additions to the vim configuration file to make ZZ save the contents to an archive, close the buffer, and return Firefox to the foreground.

augroup VimperatorYPentadactyl
    au! BufRead vimperator-*,pentadactyl-* nnoremap <buffer> ZZ :call FormFieldArchive() \| :silent write \| :bd \| :macaction hide:<CR>
    au BufRead vimperator-*,pentadactyl-* imap <buffer> ZZ <Esc>ZZ
augroup END

Finally, here is the function that saves a copy of the form field contents to an archive file, complete with timestamp and url:

function! FormFieldArchive()
    let l:contents = getbufline("%", 1, "$")
    let l:filepath = expand("%")
    let l:filename = expand("%:t:r")
    let l:formfielddir = $HOME . "/webforms/"
    let l:currentdate = TimestampText('date')
    let l:entry = l:formfielddir . l:currentdate . ".txt"
    let l:entryexists = filereadable(l:entry)
    exec "split " . l:entry
    if l:entryexists
        normal Go
        normal o
        exec "call setline(\".\", \"" . TimestampText('time') . "\")"
    else
        exec "call setline(\".\", \"" . TimestampText('journal') . "\")"
        exec "silent !svn add " . l:entry
    endif
    normal o
    exec "call setline(\".\", \"" . l:filename . "\")"
    normal o
    exec "call setline(\".\", " . string(l:contents) . ")"
    write
    bd
endfunction

function! TimestampText(style)
    let l:iswindows = has("win16") || has("win32") || has("win64")
    if l:iswindows
        if a:style == "long"
            let l:dateformat = strftime("%#x %H:%M:%S ")
        elseif a:style == "short"
            let l:dateformat = strftime("%Y-%m-%d %H:%M:%S ")
        endif
        let l:dateformat .= substitute(strftime("%#z"), '[a-z]\+\($\| \)', '', 'g')
    else
        if a:style == "long"
            let l:dateformat = strftime("%Y %b %d %a %X %Z")
        elseif a:style == "journal"
            let l:dateformat = strftime("%A, %B %d, %Y %H:%M:%S %Z")
        elseif a:style == "short"
            let l:dateformat = strftime("%Y-%m-%d %H:%M:%S %Z")
        elseif a:style == "time"
            let l:dateformat = strftime("%H:%M:%S %Z")
        endif
    endif
    if a:style == "date"
        let l:dateformat = strftime("%Y-%m-%d")
    endif
    return l:dateformat
endfunction

This will create a new file with the current date, e.g. "2010-03-15.txt", if it doesn't already exist. If it does exist, it will add the entry to the end of the file separated by a timestamp. TimestampText() is just a convenience function I wrote that returns a timestamp in various strftime() formats.

[2011-02-28 22:10:12 PST Update: Now using pentadactyl, so updated references appropriately. Added TimestampText() function for completeness. A couple of other minor edits and links added.]


ypbind problem

2007-12-02 19:31:00 -0800

After upgrading to Mac OS X 10.5 Leopard, my logs were getting spammed with the followoing error:

com.apple.nis.ypbind: Domain name not set. Aborting.

Deleting the /etc/defaultdomain file fixed the problem.


universal access zoom feature finally fixed

2007-10-31 17:53:50 -0700

Prior to Mac OS X 10.5 (Leopard), there was a bug that made using the Universal Access zoom feature (accessible by default with Option-Command-8) useless in a multiple display configuration in which the virtual heights of the arranged displays differed. As soon as you activated the zoom feature, the portal created by your displays would lock essentially to the height of the primary display, making it impossible to scroll to the portions of the virtual screen that mapped to the higher or lower displays, and in some cases you couldn't even see the menu bar. Basically, you could pan horizontally across your virtual screen, but not vertically.

This might not make any sense to you if you never tried this yourself, but they fixed this in Leopard and I'm happy about that.


mac os x instant display sleep rant

2007-10-31 17:29:51 -0700

Why is it so freaking hard to bind a keyboard shortcut to putting the displays to sleep immediately in Mac OS X? In the classic Mac OS, there was a hot key that did just this, at least on PowerBooks (I can't remember exactly what it was, and I don't think it was remappable, but it was there). Whenever I stepped away from my PowerBook or didn't anticipate needing to use it right away, I could tap those keys and the backlight would instantly shut off, saving some precious juice. Nowadays, my laptop usage patterns are a bit different and I don't tend to need to squeeze out every last minute of battery life anymore. But on the desktop machines I use regularly, which have no fewer than four displays connected to them, I want to be able to put all of those displays into power saving mode immediately. The main reason is so that my bedroom isn't lit up like a Christmas tree when I want to go to sleep, but I also prefer not to waste power needlessly and hope to maybe get a little more life out of the LCD backlights by turning them off when I'm not actually using them. Let me note here that even with a black screen, LCD backlights still generate a fair amount of light; the displays really need to be off. Another factor is that not all of the displays can be plugged into the same power strip, enabling me to turn them all off and on with a single flip of a switch (e.g. Apple Cinema Displays get their power directly from the computer they are plugged into). And anyway I prefer to have a uniform mechanism for controlling display sleep regardless of which machine I'm using and whether the energy saver settings have put the displays to sleep or I have done so explicitly. Besides, it's nice to be able to just hit a key on the keyboard to wake up the displays. I think that covers the reasons why I want this feature, and hopefully preempts some of the not so helpful suggestions I've seen other netfolk offer in response to similar pleas.

Prior to Leopard, I had a shell script that looked like this:

#!/bin/sh
  
MAGIC_NUMBER=107374183
PMSET=/usr/bin/pmset
GREP=/usr/bin/grep
AWK=/usr/bin/awk
SLEEP=/bin/sleep
    
$PMSET force -a displaysleep $MAGIC_NUMBER
$SLEEP 1
$PMSET force -a displaysleep `$PMSET -g | $GREP displaysleep | $AWK '{print $2}'`
$SLEEP 1

I was able to use a QuickSilver to bind execution of this script to a function key and I was mostly happy. It executed very quickly, and did just what I wanted it to do.

But as you can see, this script relies on a magic number—one that is undocumented and is possibly even the result of a bug. Now that I've upgraded to 10.5, this magic number no longer works; the script does nothing. Without a working magic number, the best you can do with a script like this is have the display sleep after one minute (the lowest effective pmset displaysleep argument is 1; 0 disables display sleep altogether).

Leopard introduces a new feature that allows you to bind a screen corner to "Sleep Display". So now this instant display sleep feature is explicitly supported in the operating system. Sort of. As far as I can tell, there is absolutely no way other than the hot corner to access this feature. Nothing in AppleScript, nothing in pmset, nothing in keyboard shortcuts, no menu item, no hot keys documented or otherwise, nor even a system level API that I could use to write a stupid little Cocoa app. I hope I am wrong; this omission aggravates me much more than it really ought to.

UPDATE: Malcolm Hall has come to the rescue with SleepDisplay.app. Thank you, Malc!


apache customizations

2007-02-26 23:55:17 -0800 I wanted to set up a second site on a machine that was already hosting, so I looked into setting up VirtualHosts. Here are some bits that I learned that were not in the documentation or clear from the documentation. Everything outside of the VirtualHost containers are default settings for all containers. The first container is the default; if no container matches the requested host exactly, the first one will be used. So don't use a servername or serveralias in that container if you want a last resort default. You'll want to specify ServerName, ServerAlias, and DocumentRoot as minimum settings in your subsequent containers.
# Main configuration
# Example settings...
ServerName default.mydomain
ServerAdmin admin_address@mydomain

User www
Group www

DocumentRoot "/default/docroot"
<Directory "/default/docroot">
	Options Indexes FollowSymLinks
	AllowOverride None
	Order allow,deny
	Allow from all
</Directory>

IndexOptions FancyIndexing VersionSort
IndexOptions NameWidth=*
IndexOptions IconWidth=16
IndexOptions IconHeight=16
IndexOptions SuppressDescription
# ... other index settings

# ... many other settings

# Use name-based virtual hosting.
NameVirtualHost *

# Default host
<VirtualHost *>
	# no ServerName directive
	ErrorLog "logs/default-host-error.log"
	CustomLog "logs/default-host-access.log" common
	# These lines let us use the rewrite rules defined in the main configuration.
	RewriteEngine on
	RewriteOptions inherit
</VirtualHost>

<VirtualHost *>
	ServerName virtualhost1.mydomain
	ErrorLog "logs/vh1-host-error.log"
	CustomLog "logs/vh1-host-access.log" common
	DocumentRoot "/some/other/docroot"
	<Directory "/some/other/docroot">
		Options Indexes FollowSymLinks
		AllowOverride None
		Order allow,deny
		Allow from all
	</Directory>
	# to enable serving up default "~/Sites" directories on Mac OS X
	UserDir "Sites"
</VirtualHost>


ssh-ftp

2006-08-08 23:59:59 -0700

I was poking around in iStumbler and noticed a "Secure File Sharing" option which mapped to sftp-ssh. Looking through the various directories responsible for providing services in Mac OS X, I discovered that the sftp-ssh service is provided by the ssh launchd daemon. So by enabling the "Remote Login" option in the Sharing System Preference Pane, you automatically get sftp. Cool.


new blog software

2006-06-27 16:51:50 -0700 I'm migrating blogs hosted at araxia.net from MoveableType to blojsom. I'm posting the status on the migration here.