<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Faux&#039; Blog &#187; Tech</title>
	<atom:link href="http://blog.prelode.com/category/tech/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.prelode.com</link>
	<description>On Prelode&#039;s development.. once it begins, anyway..</description>
	<lastBuildDate>Fri, 30 Dec 2011 15:21:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>My gitolite set-up</title>
		<link>http://blog.prelode.com/2011/12/my-gitolite-set-up/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=my-gitolite-set-up</link>
		<comments>http://blog.prelode.com/2011/12/my-gitolite-set-up/#comments</comments>
		<pubDate>Thu, 29 Dec 2011 18:12:34 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[git]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=356</guid>
		<description><![CDATA[I&#8217;m paranoid, but also poor. I use gitolite to control access to my git repositories, because github wanted $200/month to meet half of my requirements, and wern&#8217;t interested in negotiating (I tried). Like github, I have two types of git repositories. Public repositories; which show up on gitweb and git-daemon and etc., that everyone can [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m paranoid, but also poor.  I use <a href="https://github.com/sitaramc/gitolite">gitolite</a> to control access to <a href="http://git.goeswhere.com/">my git repositories</a>, because <a href="https://github.com/plans">github wanted $200/month</a> to meet half of my requirements, and wern&#8217;t interested in negotiating (I tried).</p>
<p>Like github, I have two types of git repositories.  Public repositories; which show up on gitweb and git-daemon and etc., that everyone can access; and private repositories, which contain my bank details.</p>
<p>My conf file consists of:</p>
<p><strong>A set of user groups:</strong> While gitolite supports multiple keys for one user, I prefer to treat my various machines as separate users, for reasons that&#8217;ll become apparent later.<br />
<code>@faux    = admin fauxanoia fauxhoki fauxtak<br />
@trust   = @faux alice<br />
@semi    = fauxcodd fauxwilf bob<br />
</code></p>
<p><strong>A set of repositories</strong>, both public and private:<br />
<code>@pubrepo = canslations<br />
@pubrepo = coke<br />
@pubrepo = cpptracer<br />
...<br />
@privrepo = bank-details<br />
@privrepo = alices-bank-details<br />
</code></p>
<p><strong>Descriptions</strong> for all the public repositories, so they show up in gitweb:<br />
<code>repo    coke<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;coke = "Coke prices website"</p>
<p>repo    cpptracer<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cpptracer = "aj's cppraytracer, now with g++ support"<br />
</code></p>
<p>And <strong>permissions</strong>:<br />
<code>repo    @pubrepo<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RW+     =   @trust<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RW&nbsp;     =   @semi<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;R&nbsp;&nbsp;       =   @all daemon gitweb<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;config  core.sharedRepository = 0664</p>
<p>repo    @privrepo<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RW+     =   @trust<br />
</code></p>
<p>This allows trusted keys to do anything, and semi-trusted keys (i.e. ones on machines where there are other people with root) to only append data (i.e. they can&#8217;t destroy anything, and can&#8217;t make any un-auditable changes).</p>
<p>Next, to protect against non-root users on the host itself, I have <code>$REPO_UMASK = 0027;</code> in my .gitolite.rc.  This makes the repositories themselves inaccessible to other users.  However, gitweb needs to be able to read public repositories; the above <code>config core.sharedRepository = 0664</code> does this.</p>
<p>This leaves only <code>/var/lib/gitolite/projects.list</code> (which is necessary as non-git users can&#8217;t ls <code>/var/lib/gitolite/repositories/</code>, so gitweb can&#8217;t discover the project list itself), and <code>repositories/**/description</code>, again for gitweb.</p>
<p>For this, I have a gitolite-admin.git/hooks/post-update.secondary of:</p>
<p><code>#!/bin/sh<br />
chmod a+r /var/lib/gitolite/projects.list<br />
find /var/lib/gitolite -name description -exec chmod a+r {} +<br />
</code></p>
<p>Now, gitweb can display public projects fine, and local users can&#8217;t discover or steal private repositories.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/12/my-gitolite-set-up/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Diagnosing character encoding issues</title>
		<link>http://blog.prelode.com/2011/10/diagnosing-character-encoding/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=diagnosing-character-encoding</link>
		<comments>http://blog.prelode.com/2011/10/diagnosing-character-encoding/#comments</comments>
		<pubDate>Tue, 25 Oct 2011 15:35:55 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=325</guid>
		<description><![CDATA[Natural language is horrible. Unicode is an attempt to make it fit inside computers. I&#8217;m going to make up some terms: Symbol: A group of related lines representing what English people would call a letter Glyph: A group of related lines that might be stand-alone, or might be combined to make a symbol And use [...]]]></description>
			<content:encoded><![CDATA[<p>Natural language is horrible.  Unicode is an attempt to make it fit inside computers.</p>
<p>I&#8217;m going to <a href="http://www.explosm.net/comics/1463/">make up some terms</a>:</p>
<ul>
<li><strong>Symbol</strong>: A group of related lines representing what English people would call a letter</li>
<li><strong>Glyph</strong>: A group of related lines that might be stand-alone, or might be combined to make a <strong>symbol</strong></li>
</ul>
<p>And use some existing, well defined terms.  If you use one of these wrongly, people <em>will</em> get hurt:</p>
<ul>
<li><strong>Code point</strong>: A number between 0 and ~1.1 million that uniquely identifies a <strong>glyph</strong>.  They have numbers, written like U+0000, and names,</li>
<li><strong>Encoding</strong>: A way of converting information to and from a stream of <strong>bytes</strong>,</li>
<li><strong>Byte</strong>: The basic unit of storage on basically every computer; an octet of 8 bits.  This has <em>nothing to do with</em> letters, or characters, or&#8230; etc.</li>
</ul>
<p>Let&#8217;s start at the top:</p>
<ul>
<li>Here&#8217;s an grave lower case &#8216;a&#8217;: <strong>à</strong>.  This is a <strong>symbol</strong>.
<ul>
<li>It could be represented by a single <strong>glyph</strong>, the code point numbered &#8220;U+00E0&#8243; and named &#8220;Latin Small Letter A With Grave&#8221;, like above,</li>
<li>It could be represented by two <strong>glyphs</strong>, like: <strong>à</strong>.
<ul>
<li>This looks identical (unless your browser can&#8217;t cope, which is plausible), but is actually two <strong>glyphs</strong>; an &#8216;a&#8217; (U+0061: &#8220;Latin Small Letter A&#8221;) followed by a U+0300: &#8220;Combining Grave Accent&#8221;.  These two <strong>glyphs</strong> combine to make an identical <strong>symbol</strong>.</li>
<li>This is, of course, pointless in this case, but there are many symbols that can only be made with combining characters.  <strong>Normalisation</strong> is the process of simplifying these cases.</li>
<li>Don&#8217;t believe me?  Good.  Not believing what you see is an important stage of debugging.  Copy the above into Notepad, or any other Unicode-safe editor, and press backspace.  It&#8217;ll remove just the accent, and leave you with a plain &#8216;a&#8217;.  Do this with the first <strong>à</strong> and it&#8217;ll delete the entire thing, leaving nothing.  See?  Different.</li>
</ul>
</li>
</ul>
</li>
<li>So, let&#8217;s assume we&#8217;re going with the complex representation of the <strong>symbol</strong>, the two code points: U+0061 followed by U+0300. We want to write them to any kind of storage, be it a file, or a network, or etc.  We need to convert them to bytes: Encoding time.
<ul>
<li>Encodings generate &#8220;some&#8221; <strong>bytes</strong> to represent a <strong>code point</strong>.  It can be anywhere between zero and infinity.  There&#8217;s really no way to tell.  Common encodings, however, will generate between one and six bytes per code point.  Basically everyone uses one of the following three encodings:</li>
<li><strong>UTF-8</strong>: generates between one and six bytes, depending on the number of the code point.  Low-numbered code-points use less bytes, and are common in English and European languages.  Other languages will generally get longer byte sequences.  Common around the Internet and on Linux-style systems.</li>
<li><strong>UTF-16</strong>: generates either two or four bytes per code point.  The vast majority of all real languages available today fit in two bytes.  Common in Windows, Java and related heavy APIs.</li>
<li><strong>I have no idea what I&#8217;m doing</strong>: Anyone using anything else is probably doing so by mistake.  The most common example of this is ISO-8859-*, which means you don&#8217;t care about non-Western-European people, i.e. 80% of the people in the world.  These generate one byte for every code point, i.e. junk for everything except ~250 selected code points.</li>
</ul>
</li>
<li>Let&#8217;s look at <strong>UTF-8</strong>.
<ul>
<li>First code point: U+0061.  This happens to be below U+007F, so is encoded in a single byte in UTF-8.  This happens to align with low-ASCII, a really old encoding that&#8217;s a subset of ISO-8859.  The single byte is 0&#215;61, 0b0110001.  Note that the first bit is &#8217;0&#8242;.</li>
<li>Second code point: U+0300.  This is not below U+007F, so goes through the normal UTF-8 encoding process.  In one sentence, UTF-8 puts the number of bytes needed in the first byte, and starts every other byte with the bits &#8220;10&#8243;.  In this case, we need two bytes, which are 0xCC, 0&#215;80; 0b11001100, 0b1000000.  Note how the first byte starts with &#8220;110&#8243;, indicating that there are two bytes (two ones, followed by a zero), and the second byte starts with &#8220;10&#8243;.</li>
<li>Note: All valid UTF-8 data can easily be validated and detected; if a byte has it&#8217;s left-most bit set to &#8217;1&#8242;, it must be part of a sequence.  No exceptions.</li>
<li>Consider
<pre>$ xxd -c1 -b some.txt | grep -1 ': 1'
0000046: 01100001  a
0000047: 11001100  .
0000048: 10000000  .
</pre>
<p>, which shows the byte patterns outlined: The &#8216;a&#8217; with the leading 0, then the two bytes of the combining character.  xxd is the only tool you should trust when diagnosing character encoding issues.  Everything else will try and harm you, <em>especially</em> your text editor and terminal.</li>
</ul>
</li>
<li><strong>UTF-16</strong> is much easier to recognise and much harder to confuse with other things as, for most Western text (including XML and..), it&#8217;ll be over 40% nulls (0&#215;00, 0b00000000).</li>
<li>Now you&#8217;ve done your conversion, you can write the bytes, to your file or network, and ensure that whoever is on the other end has enough information to work out what format the bytes are in.  If you don&#8217;t tell them, they&#8217;ll have to guess, and will probably get it wrong.</li>
</ul>
<p>In summary:</p>
<ul>
<li>Have some data, in bytes?  Don&#8217;t pretend it&#8217;s text, even if it looks like it is; find out or work out what encoding it&#8217;s in and convert it into something you can process first.  It&#8217;s easy to detect if it&#8217;s UTF-8, UTF-16 or if you have a serious problem.</li>
<li>Have some textual information from somewhere?  Find an appropriate encoding, preferably UTF-8 or UTF-16, to use before you send it anywhere.  Don&#8217;t trust your platform or language, it&#8217;ll probably do it wrong.</li>
<li>Can&#8217;t work out what&#8217;s in a file?  Run it through xxd and look for nulls and bytes starting with &#8217;1&#8242;.  This&#8217;ll quickly tell you if it&#8217;s UTF-16, UTF-8 or effectively corrupt.</li>
</ul>
<p>Hopefully that&#8217;s enough information for you to know what you don&#8217;t know.</p>
<p>For more, try <a href="http://www.joelonsoftware.com/articles/Unicode.html">Joel on Software: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/10/diagnosing-character-encoding/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PuTTY Tray</title>
		<link>http://blog.prelode.com/2011/10/putty-tray/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=putty-tray</link>
		<comments>http://blog.prelode.com/2011/10/putty-tray/#comments</comments>
		<pubDate>Sun, 09 Oct 2011 15:58:15 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=317</guid>
		<description><![CDATA[I&#8217;ve released an updated version of PuTTY Tray to puttytray.goeswhere.com, direct download: putty.exe p0.61-t004 please see the site for the latest version and details. This is a fork of Barry Haanstra&#8217;s PuTTY Tray, which is abandoned. Main advantages: Now built against PuTTY 0.61, getting features like Windows 7 Jumplist and Aero support, and four years [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve released an updated version of PuTTY Tray to <a href="http://puttytray.goeswhere.com/">puttytray.goeswhere.com</a>, <del datetime="2011-12-21T11:36:45+00:00">direct download: <a href="http://b.goeswhere.com/putty-tray-v004/putty.exe">putty.exe p0.61-t004</a></del> please see the site for the latest version and details.</p>
<p>This is a fork of <a href="http://haanstra.eu/putty/">Barry Haanstra&#8217;s PuTTY Tray</a>, which is abandoned.</p>
<p>Main advantages:</p>
<ul>
<li>Now built against <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html">PuTTY 0.61</a>, getting features like Windows 7 Jumplist and Aero support, and four years of core PuTTY development</li>
<li>Ctrl+mousewheel zoom support</li>
<li>URL detection works on URLs ending with close-brackets</li>
<li>Much easier to continue development of, build script generator works and source, issue and pull-request tracking provided by <a href="https://github.com/FauxFaux/PuTTYTray">github</a>.</li>
</ul>
<p>Please <a href="https://github.com/FauxFaux/PuTTYTray/issues/new">raise a bug</a> if you have any problems or requests!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/10/putty-tray/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Addition is free, static compilation is expensive</title>
		<link>http://blog.prelode.com/2011/09/addition-is-free-static-compilation-is-expensive/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=addition-is-free-static-compilation-is-expensive</link>
		<comments>http://blog.prelode.com/2011/09/addition-is-free-static-compilation-is-expensive/#comments</comments>
		<pubDate>Sun, 04 Sep 2011 14:41:11 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=278</guid>
		<description><![CDATA[Summing the integers from 1 to 10,000,000? Perl: 1.01 seconds Python: 2.04 seconds Java, including javac: 0.76 seconds Java, including ecj: 0.61 seconds Java, including javac, with -Xint: 1.01 seconds. Java, including javac, with -Xint on the compiler too: 1.16 seconds. -Xint disables practically all optimisations that Java offers, forcing the JVM into interpretation mode, [...]]]></description>
			<content:encoded><![CDATA[<p>Summing the integers from 1 to 10,000,000?</p>
<ul>
<li>Perl: 1.01 seconds</li>
<li>Python: 2.04 seconds</li>
<li>Java, including javac: 0.76 seconds</li>
<li>Java, including ecj: 0.61 seconds</li>
<li>Java, including javac, with -Xint: 1.01 seconds.</li>
<li>Java, including javac, with -Xint on the compiler too: 1.16 seconds.</li>
</ul>
<ul>
<li>-Xint disables practically all optimisations that Java offers, forcing the JVM into interpretation mode, so it&#8217;ll operate much like perl and python do.</li>
<li>ecj is Eclipse&#8217;s compiler for Java, a faster and cleaner implementation of javac that can run standalone.</li>
</ul>
<p>i.e. including compilation time on an entirely unoptimised compiler, Java is still twice the speed of Python.</p>
<p>(This isn&#8217;t really interesting or surprising to me, but the question comes up often enough that I&#8217;d like to have these here to link <a href="http://xkcd.com/386/">WRONG</a> people to.)</p>
<p><span id="more-278"></span></p>
<hr style="margin: 2em"/>
<p>Testcases:<br />
<code><br />
time (N=10000000; printf "class A { public static void main(String... arg) { long j = 0; for(long i = 0; i < $N; ++i) { j += i; } System.out.println(j); } }" > A.java &#038;&#038; javac A.java &#038;&#038; java A)<br />
time (N=10000000; printf "class A { public static void main(String... arg) { long j = 0; for(long i = 0; i < $N; ++i) { j += i; } System.out.println(j); } }" > A.java &#038;&#038; javac A.java &#038;&#038; java -Xint A)<br />
time (N=10000000; printf "class A { public static void main(String... arg) { long j = 0; for(long i = 0; i < $N; ++i) { j += i; } System.out.println(j); } }" > A.java &#038;&#038; javac -J-Xint A.java &#038;&#038; java -Xint A)<br />
time (N=10000000; printf "class A { public static void main(String... arg) { long j = 0; for(long i = 0; i < $N; ++i) { j += i; } System.out.println(j); } }" > A.java &#038;&#038; java -jar ecj.jar -source 1.5 A.java &#038;&#038; java A)<br />
time (N=10000000; printf "j = 0\nfor i in range(1,$N):\n\tj = j + i\nprint j" | python -)<br />
time (N=10000000; printf 'my $j = 0; for ($i = 0; $i < '$N'; ++$i){ $j += $i; } print $j' | perl -w)<br />
</code></p>
<p>Versions, from Debian stable:<br />
</code><code><br />
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)<br />
perl, v5.10.1<br />
OpenJDK Runtime Environment (IcedTea6 1.8.7) (6b18-1.8.7-2~squeeze1), OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)<br />
ecj 3.5.1<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/09/addition-is-free-static-compilation-is-expensive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows XP End of Support Countdown Gadget</title>
		<link>http://blog.prelode.com/2011/08/xp-gadget-leak/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=xp-gadget-leak</link>
		<comments>http://blog.prelode.com/2011/08/xp-gadget-leak/#comments</comments>
		<pubDate>Tue, 02 Aug 2011 18:31:39 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=273</guid>
		<description><![CDATA[The Windows XP End of Support Countdown Gadget gives you a nice countdown until Windows XP, and, more importantly, IE6 will actually finally be unsupported. It, however, leaks memory. A lot of memory; about 1kb/second. Noting that it&#8217;s running all the time, and not important, this is rather inconvenient. FTFY. Can&#8217;t redistribute a patched &#8220;binary&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://b.goeswhere.com/gadget-xpsupport.png" alt="Windows XP End of Support Countdown Gadget" style="float: left; padding: 1em"/>  The <a href="http://www.microsoft.com/download/en/details.aspx?id=11662">Windows XP End of Support Countdown Gadget</a> gives you a nice countdown until Windows XP, and, more importantly, IE6 will actually finally be unsupported.</p>
<p>It, however, leaks memory.  A lot of memory; about 1kb/second.  Noting that it&#8217;s running all the time, and not important, this is rather inconvenient.</p>
<p><a href="http://b.goeswhere.com/0001-Leak-slower.patch">FTFY</a>.  Can&#8217;t redistribute a patched &#8220;binary&#8221; as the original is not redistributable.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/08/xp-gadget-leak/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java stacktraces straw man</title>
		<link>http://blog.prelode.com/2011/06/java-stacktraces-straw-man/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=java-stacktraces-straw-man</link>
		<comments>http://blog.prelode.com/2011/06/java-stacktraces-straw-man/#comments</comments>
		<pubDate>Sun, 05 Jun 2011 22:58:15 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=257</guid>
		<description><![CDATA[It&#8217;s a sad fact of life that many developers spend a good deal of time staring at stack traces. My personal favorite situation is when you get to: Exception in thread "main" java.lang.NullPointerException &#160;&#160;&#160;&#160;at com.goeswhere.dmnp.linenos.B.foo(B.java:13) ..and, line 13 is: &#160;&#160;System.out.println(first.substring(1) + second.toUpperCase() + third.toLowerCase()); Basically, the end of any happiness. LineNos can fix this: $ [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s a sad fact of life that many developers spend a good deal of time staring at stack traces.</p>
<p>My personal favorite situation is when you get to:<br />
<code>Exception in thread "main" java.lang.NullPointerException<br />
&nbsp;&nbsp;&nbsp;&nbsp;at com.goeswhere.dmnp.linenos.B.foo(<a href="http://git.goeswhere.com/?p=dmnp.git;a=blob;f=linenos/src/test/java/com/goeswhere/dmnp/linenos/B.java;h=889c1de3871669b72c3f86abd981419e00f625f3;hb=HEAD">B.java</a>:13)</code></p>
<p>..and, line 13 is:</p>
<p><code>&nbsp;&nbsp;System.out.println(first.substring(1) + second.toUpperCase() + third.toLowerCase());</code></p>
<p>Basically, the end of any happiness.</p>
<hr style="margin: 2em"/>
<p><a href="http://git.goeswhere.com/?p=dmnp.git;a=tree;f=linenos">LineNos</a> can fix this:</p>
<p><code>$ java -Xbootclasspath/p:linenos.jar -javaagent:linenos.jar=com/goeswhere com.goeswhere.dmnp.linenos.B<br />
Exception in thread "main" java.lang.NullPointerException<br />
&nbsp;&nbsp;&nbsp;&nbsp;at com.goeswhere.dmnp.linenos.B.foo(B.java:13), <b>attempting to invoke toUpperCase</b> #4<br />
&nbsp;&nbsp;&nbsp;&nbsp;at com.goeswhere.dmnp.linenos.B.run(B.java:9)<br />
&nbsp;&nbsp;&nbsp;&nbsp;at com.goeswhere.dmnp.linenos.B.main(B.java:5), attempting to invoke run #2</code></p>
<p>This is implemented entirely as a Java Agent; it requires no VM modifications and is portable anywhere that supports <a href="http://download.oracle.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html#retransformClasses(java.lang.Class...)">instrumenters transforming classes</a> (i.e. everywhere that matters).</p>
<hr style="margin: 2em"/>
<p>It works by adding extra line numbers for each call on a line.  Currently it adds call-number*1000 to the line number, so that it&#8217;s debuggable and easier to do in two phases, but this makes it much less efficient.</p>
<p>i.e., assuming Java allowed labels for line-numbers, it does:</p>
<p><code>13:<br />
8013: System.out.println(<br />
1013: [secret StringBuilder construction (used to implement String concatenation)]<br />
2013: first.substring(1)<br />
3013: +<br />
4013: second.toUpperCase()<br />
5013: +<br />
6013: third.toLowerCase()<br />
7013: [secret StringBuilder#toString()]);</code></p>
<p>Thus, the real stacktrace looks like:<br />
<code>Exception in thread "main" java.lang.NullPointerException<br />
&nbsp;&nbsp;&nbsp;&nbsp;at com.goeswhere.dmnp.linenos.B.foo(B.java:4013)<br />
&nbsp;&nbsp;&nbsp;&nbsp;at com.goeswhere.dmnp.linenos.B.run(B.java:9)<br />
...</code></p>
<p>It additionally overrides StackTraceElement#toString() to decompile the named class and report the invocation that&#8217;s on that line, i.e. lookup line 4013 in the above bytecode, and report that it&#8217;s an invocation of toUpperCase, thus giving the intended result.</p>
<hr style="margin: 2em"/>
<p>It has no run-time performance penalty beyond the extra time to load classes, and the increase in the size of line-number table (actually, I have no idea how this affects performance, but I can&#8217;t imagine it&#8217;s much).  Printing a stacktrace is slower, however (although, it probably wouldn&#8217;t be much slower in a real implementation).  Also, it&#8217;s a straw-man, so leaks memory, but this isn&#8217;t an important part of the implementation.</p>
<p>A much better implementation would be to store the [Class<>, line-number] -> hint mapping instead of the whole file and doing decompilation; or to replace the entire line-number table with a bytecode number table (i.e. 1->1, 2->2, 3->..), and do it all at print-time.  Patches welcome.</p>
<hr style="margin: 2em"/>
<p>In summary: Dear Oracle, please make the JVM do this by default.  Lots of Love, Faux.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/06/java-stacktraces-straw-man/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>The Goat Inventory Tracker</title>
		<link>http://blog.prelode.com/2011/05/goat-inventory-tracker/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=goat-inventory-tracker</link>
		<comments>http://blog.prelode.com/2011/05/goat-inventory-tracker/#comments</comments>
		<pubDate>Wed, 25 May 2011 21:33:41 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[git]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=212</guid>
		<description><![CDATA[The year is 1516. The Royal Mail has just been founded. You&#8217;re an enterprising goat farmer. You want to service requests for goats by mail. You have multiple paddocks. Paddocks contain multiple goats. An order comes in for some goats. You spring into action! Firstly, you need to select some goats to send out. For [...]]]></description>
			<content:encoded><![CDATA[<style type="text/css">
abbr { border-bottom: 1px dashed }
abbr.gitcmd { border-bottom: 1px dashed red }
hr { margin-top: 2em; margin-bottom: 2em; clear: both }
img.inline { float: left; padding: 1em; }
</style>
<p>The year is <abbr title="sometime in the (near?) future">1516</abbr>.  The <abbr title="software development revolution">Royal Mail</abbr> has just been founded.</p>
<p>You&#8217;re an enterprising <abbr title="software engineer">goat farmer</abbr>.  You want to service requests for <abbr title="software changes">goats</abbr> by <abbr title="bug tracker">mail</abbr>.  You have multiple <abbr title="files">paddocks</abbr>.  <abbr title="Files">Paddocks</abbr> contain multiple <abbr title="outstanding hunks">goats</abbr>.</p>
<hr />
<p><a href="http://www.flickr.com/photos/abbsworth/5751516011/sizes/l/in/photostream/"><img class="inline" src="http://b.goeswhere.com/goat-abbsworth-5751516011.jpg" alt="one of the only two cute goat photos on the entire internet, (c) 2011 abbsworth, used without permission"/></a></p>
<p><abbr title="A bug report">An order</abbr> comes in for some <abbr title="code fixes">goats</abbr>.  You spring into <abbr title="the kitchen to get some coke">action</abbr>!</p>
<p>Firstly, you need to select some <abbr title="code that fixes the issue">goats</abbr> to send out.</p>
<hr />
<p>For this, you&#8217;ve designed a tool called the <abbr title="Goat Inventory Tracker">Goat Inventory Tracker</abbr>.  It deals with all the management aspects, so you can do what you love most; <abbr title="software engineering">breeding goats</abbr>.</p>
<p>You decree, <abbr class="gitcmd" title="git add -p">Goat Inventory Tracker, Allow Me To Pick From My Goats</abbr>!</p>
<p>The Goat Inventory Tracker shows you each of your <abbr title="hunks">goats</abbr> across all of your <abbr title="files">paddocks</abbr>, and asks you if you would like to include it.  If you do, it takes the <abbr title="hunk">goat</abbr> and places it in <abbr title="the staging area">a pending crate</abbr>.</p>
<p>When you&#8217;re done selecting <abbr title="hunks">goats</abbr>, you can <abbr class="gitcmd" title="git status">look</abbr> into the <abbr title="staging area">pending crate</abbr> and check everything is what you want.  If it helps, you can <abbr class="gitcmd" title="use git stash --keep-index to view">preview</abbr> your <abbr title="working tree">shipping log</abbr> as if you had no outstanding <abbr title="hunks">goats</abbr>.  </p>
<p>This is important, as you&#8217;re required to maintain <abbr title="passing tests">various arbitary constraints</abbr> by the local <abbr title="land ba..development manager">land baron</abbr>.</p>
<hr />
<p><a href="http://www.flickr.com/photos/loredana_preston/5754380402/in/photostream/"><img class="inline" style="float:right" src="http://b.goeswhere.com/goat-loredana_preston-5754380402.jpg" alt="the other only cute goat photo on the entire internet, (c) 2011 Loredana Preston, used without permission"/></a></p>
<p>Now that you&#8217;re happy, you can decree <abbr class="gitcmd" title="git commit">Goat Inventory Tracker, Seal This Box</abbr>!</p>
<p>This will, obviously, <abbr title="commit">seal</abbr> the <abbr title="commit">box</abbr> of <abbr title="hunks">goats</abbr>, and place it <abbr title="in your local history">near the post box</abbr> with a <abbr title="commit message">shipping note</abbr> of your choice.</p>
<hr />
<p>You repeat this procedure for a couple of <abbr title="cokes">days</abbr>.  During this time, loads of <abbr title="commits">boxes</abbr> pile up <abbr title="locally">near the mail point</abbr>.</p>
<p>You don&#8217;t like mess.</p>
<p>You decree, <abbr class="gitcmd" title="git rebase --interactive @{upstream}">Goat Inventory Tracker, Allow Me To Rearrange My Boxes</abbr>!</p>
<p>You are then able to reorder, <abbr title="reword">change the shipping note of</abbr>, <abbr title="squash">merge</abbr>, and <abbr title="edit">completely change or split</abbr> your <abbr title="commits">boxes</abbr>.</p>
<hr />
<p><img class="inline" style="width:150px;height:205px" src="http://b.goeswhere.com/postman_pat.jpg" alt="Postman Pat, Postman Pat, Postman Pat and his black and white cat.  EARLY IN THE MORNING, JUST AS DAY IS DAWNING..."/></p>
<p>Once you&#8217;re happy with your outstanding <abbr title="commits">orders</abbr>, you can decree <abbr class="gitcmd" title="git push">Goat Inventory Tracker, Summon The Postman</abbr>!</p>
<p><abbr title="(Tiny parts of) git fetch will run">The postman will arrive</abbr>, and will then check he can load your <abbr title="commits">boxes</abbr> into his <abbr title="history(?)">wagon</abbr>.  Due to safety concerns, the <abbr title="push process">postman</abbr> isn&#8217;t allowed to pile your <abbr title="commits">boxes</abbr> on top of other people&#8217;s <abbr title="commits">boxes</abbr>, or allowed to re-order other people&#8217;s <abbr title="commits">boxes</abbr>.  This means that if <abbr title="upstream has moved on since your last pull">he has any boxes in his van already</abbr>, you&#8217;ll need to do some work first.</p>
<hr />
<p>No worry; you just decree <abbr class="gitcmd" title="git pull --rebase">Goat Inventory Tracker, Pile My Boxes Atop These Lowly Boxes</abbr>!  It will.</p>
<p>Sometimes your <abbr title="commits">boxes</abbr> won&#8217;t <abbr title="apply">fit nicely</abbr> on top of the other people&#8217;s, at which point the Goat Inventory Tracker will <abbr title="pani..conflict">panic</abbr>, and you&#8217;ll need to <abbr title="resolve">reshape</abbr> your <abbr title="commits">boxes</abbr>.  Remember that the Goat Inventory Tracker has many tools for dealing with <abbr title="commits">boxes</abbr>, as explained above.</p>
<p>Once you&#8217;re done piling your boxes, the <abbr title="push process">postman</abbr> will be happy and will <abbr title="have placed your commits in upstream">leave with your boxes</abbr>, and you have entered them into your <abbr title="history">account book</abbr>.</p>
<hr />
<p>You&#8217;ve made some money, you&#8217;ve made your customers happy, and you&#8217;ve done basically no work. Excellent.  Back to <abbr title="fixing the code">breeding goats</abbr>.<br />
<span id="more-212"></span></p>
<hr />
<script type="text/javascript"><!--
function f() {
 var t=document.getElementsByTagName('abbr');
 for(var i=0;i<t.length;++i) {
   var q=t[i].title;
   t[i].title=t[i].firstChild.nodeValue;
   t[i].firstChild.nodeValue=q;
  }
}
//--></script><br />
Or, view this post in <abbr title="goat mode" onclick="f()" style="cursor: pointer">git mode</abbr>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/05/goat-inventory-tracker/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Tiny Windows utilities</title>
		<link>http://blog.prelode.com/2011/04/tiny-utilities/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tiny-utilities</link>
		<comments>http://blog.prelode.com/2011/04/tiny-utilities/#comments</comments>
		<pubDate>Fri, 22 Apr 2011 21:50:10 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=202</guid>
		<description><![CDATA[I have a set of tiny utilities that I use on a daily basis but have never bothered to release. All are in the tinies v001 archive, in both x64 and legacy format. Symbols, source and signatures are available. No installer is available; just drop them into your All Programs -> Startup folder. shiftfocus.exe adds [...]]]></description>
			<content:encoded><![CDATA[<p>I have a set of tiny utilities that I use on a daily basis but have never bothered to release.</p>
<p>All are in the <a href="http://b.goeswhere.com/tinies-v001.7z">tinies v001 archive</a>, in both x64 and legacy format.  <a href="http://b.goeswhere.com/tinies-syms-v001.7z">Symbols</a>, <a href="http://git.goeswhere.com/?p=tinies.git;a=tag;h=refs/tags/v001">source and signatures</a> are available.  No installer is available; just drop them into your All Programs -> Startup folder.</p>
<div style="height: 2em;"></div>
<hr style="clear: left"/>
<div style="height: 2em;"></div>
<p><strong>shiftfocus.exe</strong> adds an extra set of hotkeys for focusing windows.  <code>Ctrl+win+arrow</code> focuses the window to that side of the current window.</p>
<p>This makes the most sense with Aero&#8217;s Snap turned on.  If you have two windows &#8220;half-maximised&#8221; on a screen (i.e. one has been <code>win+left</code>&#8216;d, and the other <code>win+right</code>&#8216;d), then you can switch between them using <code>ctrl+win+left</code> and <code>ctrl+win+right</code>.</p>
<div style="height: 2em;"></div>
<hr style="clear: left"/>
<div style="height: 2em;"></div>
<p><strong>topkey.exe</strong> adds <code>win+w</code> to toggle a windows&#8217; always-on-top flag, and <code>win+return</code> to create a new command prompt &#8220;in the current directory&#8221;.  (This works for Explorer windows, and things that have the directory at the start of the title, i.e. Notepad++.)</p>
<div style="height: 2em;"></div>
<hr style="clear: left"/>
<div style="height: 2em;"></div>
<p>
<strong>mousex.exe</strong> allows you to use an xbox360 controller as a mouse.  Different analogue sticks are different sensitivity.  A/B for left/right click.  Shoulder analogue controls for the scrollwheel.</p>
<div style="height: 2em;"></div>
<hr style="clear: left"/>
<div style="height: 2em;"></div>
<p>And, for more niche users:</p>
<div style="height: 2em;"></div>
<hr style="clear: left"/>
<div style="height: 2em;"></div>
<p><img style="float: left; padding: 2em" src="http://b.goeswhere.com/powerstatustray.png"/></p>
<p><strong>powerstatustray.exe</strong> shows which drives are spun-up, and notifies you when a drive spins up or down.  (Yes, actually, this one was released before.)</p>
<div style="height: 2em;"></div>
<hr style="clear: left"/>
<div style="height: 2em;"></div>
<p><strong>keydump.exe</strong> shows what you can keylog by binding globally to DirectInput.  Most keylogging preventers/detectors completely ignore this, and/or only work through blacklisting, which is laughably pointless.  It doesn&#8217;t bother translating numbers into keycaps, but it&#8217;s obvious whether it&#8217;s working and whether it&#8217;s been detected.</p>
<div style="height: 2em;"></div>
<hr style="clear: left"/>
<div style="height: 2em;"></div>
<p>Others, to date:</p>
<ul>
<li>aukiller: Legacy XP application.</li>
<li>foobar2000-loader.exe: Demo of pre-loading a dll into an application via the debug api.</li>
<li>keytoputty.exe: Take input and send it to a running instance of putty, i.e. to allow input during full-screen applications.</li>
<li>loaddlls.exe just calls LoadLibrary on all it&#8217;s arguments.</li>
<li>noelev.exe: Legacy implementation of setting <code>__COMPAT_LAYER=RunAsInvoker</code>.</li>
<li>quickkey.exe: Legacy XP application.</li>
<li>unrequireadmin.exe: An even less healthy implementation of noelev.exe.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/04/tiny-utilities/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sometimes people really do just want to help&#8230;</title>
		<link>http://blog.prelode.com/2011/04/people-really-want-to-help/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=people-really-want-to-help</link>
		<comments>http://blog.prelode.com/2011/04/people-really-want-to-help/#comments</comments>
		<pubDate>Mon, 11 Apr 2011 00:51:38 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[Rant]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=194</guid>
		<description><![CDATA[Late last year we were playing TrickyTrucks. TrickyTrucks is okay fun single-player, but what really makes it fun is the competition. For this, it has built in scoreboards, per track. Attempting to beat certain people&#8217;s times on tracks is the fun. What it lacks is a cross-track scoreboard, i.e. some kind of championship, and/or notifications [...]]]></description>
			<content:encoded><![CDATA[<p>Late last year we were playing <a href="http://www.gravitysensation.com/trickytruck/">TrickyTrucks</a>.  </p>
<p>TrickyTrucks is okay fun single-player, but what really makes it fun is the competition.  For this, it has built in scoreboards, per track. Attempting to beat certain people&#8217;s times on tracks <strong>is</strong> the fun.</p>
<p>What it lacks is a cross-track scoreboard, i.e. some kind of championship, and/or notifications of people beating your scores.  Even <a href="http://www.audio-surf.com/">Audiosurf</a>, one of the&#8230; most entertainingly engineered indie games recently, got this right.</p>
<p>I implemented one.</p>
<p>After some initial beta testing (and ensuring I was near the top of the championship), I messaged the TrickyTrucks author with <a href="http://git.goeswhere.com/?p=tt.git;a=summary">the source of the scraper</a> and <a href="http://git.goeswhere.com/?p=ttscores.git;a=summary">of the web interface</a>, asking for permission to link to <a href="http://ttscores.goeswhere.com/">the website</a> on the official forum, so others could join us in competing for the championship title.</p>
<p>An aside, on licensing: Both of these components were released under the BSD.  That allows anyone, including the TrickyTrucks author, to use the code for any purpose, including incorporating it into his official website.  The component split was done such that there was a neat interface for him to implement on a non-scraper backend.  The best result for me would be for there to be an official API and an officially hosted version of the site, such that I never had to do anything ever again to continue appreciating it.</p>
<p>An aside, on development costs: Reverse engineering binary protocols is a nightmare.  Especially so with only access to a read client (with no source).  Especially so when there&#8217;s no way to get the server to return things consistently or with user-specified plaintext.  Especially when the protocol has (what you believe to be) NIH compression.  Don&#8217;t ever, ever try and pay someone to do this unless they really, really want to.</p>
<p>He replied that this would be fine, but only if I removed the link from the website to the source.  I, grudgingly (given it was already in the wild), did so and posted on the forum.</p>
<p>His response?  Delete the post, and change the server to have some additional, weak protection against 3rd-party clients.</p>
<p><strong>What</strong>.  <strong>The</strong>.  <strong>Hell</strong>.</p>
<p>(Eventually he sent me details of an API to use, but I&#8217;d lost interest by then.)</p>
<hr />
<p>This post brought to you by <a href="http://developer.spotify.com/en/libspotify/overview/">libspotify</a> being <a href="http://getsatisfaction.com/spotify/topics/libspotify_crashes_on_loading_access_violation">incompatible</a> with most 3rd party DLLs, probably due to the copy protection on their DLL.  Copy protection.  On something that requires a paid account, verified against their server.  Please tell them what you think about this on <a href="http://getsatisfaction.com/spotify/topics/libspotify_crashes_on_loading_access_violation">my GetSatisfaction thread</a> (apparently this is what passes as a bug tracker these days).</p>
<p>This prompted me to waste ALL WEEKEND porting <a href="http://git.goeswhere.com/?p=foo_input_spotify.git;a=summary">foo_input_spotify</a> to <a href="http://despotify.se/">libdespotify</a>.  foo_input_spotify will increase the value of their product.  Why are they making my life miserable?  Perhaps it&#8217;s unintentional.  Time will tell.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/04/people-really-want-to-help/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>History cleanliness in git</title>
		<link>http://blog.prelode.com/2011/04/history-cleanliness-in-git/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=history-cleanliness-in-git</link>
		<comments>http://blog.prelode.com/2011/04/history-cleanliness-in-git/#comments</comments>
		<pubDate>Thu, 07 Apr 2011 23:36:16 +0000</pubDate>
		<dc:creator>Faux</dc:creator>
				<category><![CDATA[git]]></category>
		<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://blog.prelode.com/?p=185</guid>
		<description><![CDATA[This post is for documentation only. It was going to be a rebuttal to jwh&#8217;s Mercurial fanboyism but I realised while writing it and re-reading his post that I have absolutely no idea what he&#8217;s talking about, nor can I work out how to get hg to tell me. Given a repo of: My proposals [...]]]></description>
			<content:encoded><![CDATA[<p>This post is for documentation only.  It was going to be a rebuttal to <a href="http://jhw.dreamwidth.org/1868.html">jwh&#8217;s Mercurial fanboyism</a> but I realised while writing it and re-reading his post that I have absolutely no idea what he&#8217;s talking about, nor can I work out how to get <code>hg</code> to tell me.</p>
<p>Given a repo of:<br />
<img src="http://b.goeswhere.com/gitpics/naturist-full.png"/></p>
<p>My proposals for alternatives to this simple workflow are as follows.  These all result in the same order of code going into the master branch, but have different histories.  (Actually, I think there&#8217;s still mistakes in there but I&#8217;m tired of staring at the <a href="http://git.goeswhere.com/?p=githistories.git;a=summary">horrible procedural script that generates it</a>, so it&#8217;ll do).</p>
<p><strong>1. </strong> A <strong>flat history</strong>, made by rebasing everything on top of master instead of merging:<br />
<img src="http://b.goeswhere.com/gitpics/flat-full.png"/></p>
<p><strong>2.</strong> <strong>Only merges on master</strong>, giving you the illusion of a neat history&#8230;<br />
<img src="http://b.goeswhere.com/gitpics/onlymerges-flat.png"/></p>
<p>&#8230;but, underneath, loads of ugly information available:<br />
<img src="http://b.goeswhere.com/gitpics/onlymerges-full.png"/></p>
<p><strong>3.</strong> A hybrid, <strong>supercommits</strong>, whereby you keep a flat history but you maintain where branches were:<br />
<img src="http://b.goeswhere.com/gitpics/supercommits-flat.png"/></p>
<p>&#8230;or, with the history information visible:<br />
<img src="http://b.goeswhere.com/gitpics/supercommits-full.png"/></p>
<hr />
<p><strong>Thoughts:</strong></p>
<p>Serious concurrent projects like git itself use the &#8216;only merges on master&#8217; approach.  I strongly agree that they shouldn&#8217;t be flattening the history; it&#8217;s nice to be able to see groups of patches as they go into master, and to navigate the history of &#8220;feature commits&#8221;, instead of the history of &#8220;developer changes&#8221;.</p>
<p>The flat model seems to work much more like how I think about software development:</p>
<ul>
<li>You start working on something.</li>
<li>You do your normal develop, commit, developer-test, commit cycle.</li>
<li>Master moves on a bit while you&#8217;re messing around.  The fact that you happened to start developing before a specific commit on master, instead of just after it (so it would be the root of your branch) is reasonably irrelevant; you may as well move the branch-root (or branch point) up to the top of master.</li>
<li>Additionally, if you do a real merge, you need to test your changes.  This leads to more developer testing, and possibly more commits.</li>
<li>What do you do with these commits?  Assuming your merge with master is still local you can fix it (this is git, you can fix anything), but it&#8217;s inconvenient.</li>
<li>When you&#8217;ve rebased, you can continue committing on your rebased branch like normal, with confidence that when you merge it&#8217;ll all be fine (as it&#8217;ll be a fast-forward merge).</li>
</ul>
<p>&#8216;Supercommits&#8217; is a hybrid of these two; you can use the rebase-onto-master workflow from &#8216;flat&#8217;, but you can logically group your set of commits into a&#8230; family?  I like this idea, but haven&#8217;t really implemented it in practice so can&#8217;t really comment.</p>
<p>(Apologies for screenshots of text; I&#8217;m lazy, ansifilter was NOT WORKING and it&#8217;s prettier than gitk.)</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.prelode.com/2011/04/history-cleanliness-in-git/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

