Tuesday, March 22, 2011

Ruby rescue gotcha

Today in the codebase at my day job, I found a particularly cute bug related to "rescue" in Ruby. This isn't a particularly unknown gotcha–I've read about it on the net at least once–but this is a particularly sweet (or devious) manifestation, and as far as I can tell it was purely accidental, not a contrived example.

The following example uses rspec, but the key thing is begin; 2 / 0 rescue NoMethodError; end versus begin; 2 / 0; rescue NoMethodError; end–what is the difference between these two statements?


require File.expand_path('spec_helper', File.dirname(__FILE__))

describe "enigma" do
it "fails, but why" do
lambda { begin; 2 / 0 rescue NoMethodError; end }.
should raise_error(ZeroDivisionError)
end

it "passes, but why" do
lambda { begin; 2 / 0; rescue NoMethodError; end }.
should raise_error(ZeroDivisionError)
end
end


I'll post the answer in a comment in a few days; feel free to post your answers as comments if you wish.

Thursday, March 03, 2011

Levitation-perl and deleted files

Ever since I started using levitation-perl, I was curious about how it handles deleted files. Well, I found out (the hard way). The symptom was that I did a routine merge and a lot of files showed up as conflicts (including ones which hadn't been edited, on the wiki or on the git side, for a long time). This is usually a symptom of git not being able to find a reasonable common ancestor.

Turned out that any files which had been deleted in mediawiki caused what amounts to a rewrite of the git history (as if those files had never been created). This is not specific to mediawiki/levitation; the same kind of thing would happen in pure git if you ran git filter-branch or similar mechanisms to delete a file from git and make sure it was erased from the history (see for example Rewriting History or The trouble with firmware).

The consequence of the rewritten history for my merge was that the common ancestor was very old (prior to when the deleted file was first created, about two years ago in my case), rather than a few days old as would be the case without the history rewrite.

How did I get my merge done? In my specific case, the content of the deleted files was nothing sensitive, so I was fine with having them remain in the git history. If they needed to stay gone from the history, I would probably have needed to follow RECOVERING FROM UPSTREAM REBASE in the git-rebase manpage.

My solution was to create a merge commit whose parents are the two corresponding commits in the rewritten and non-rewritten history. Since the git repository I was using is public, you can follow along. The commands here are somewhat edited (I've snipped out my dead ends and multiple invocations of gitk to see what I had at each step).


[browse gitk history to find that 681b7936 is one of the commits
with message "add domain and Domain"]
$ git checkout 681b793629d88729b919f40d0862884147db0d8d
Note: checking out '681b793629d88729b919f40d0862884147db0d8d'.

You are in 'detached HEAD' state. . . .
HEAD is now at 681b793... add domain and Domain
$ git checkout -b withdeletedfiles
Switched to a new branch 'withdeletedfiles'
$ gitk levitation/master&
[browse history to find that a6d0885... is the commit with message
"add domain and Domain"]
$ git checkout a6d0885
Note: checking out 'a6d0885'.

You are in 'detached HEAD' state. . . .

HEAD is now at a6d0885... add domain and Domain
$ git checkout -b withoutdeletedfiles
Switched to a new branch 'withoutdeletedfiles'
$ git merge -s ours withdeletedfiles
Merge made by ours.
$ git diff --stat -w withdeletedfiles withoutdeletedfiles
Main/W/P/.3a/WP:INDEX | 2 --
Wikiproofs/S/u/b/Subject index | 30 ------------------------------
2 files changed, 0 insertions(+), 32 deletions(-)
$ git checkout master
Switched to branch 'master'
$ git merge withoutdeletedfiles
Removing Main/W/P/.3a/WP:INDEX
Removing Wikiproofs/S/u/b/Subject index
Merge made by recursive.
Main/W/P/.3a/WP:INDEX | 2 --
Wikiproofs/S/u/b/Subject index | 30 ------------------------------
2 files changed, 0 insertions(+), 32 deletions(-)
delete mode 100644 Main/W/P/.3a/WP:INDEX
delete mode 100644 Wikiproofs/S/u/b/Subject index
[1]+ Done gitk
$ gitk&
[1] 3233
$ git show 01a25538177dbe768e130aa94f7d49be11a63733
commit 01a25538177dbe768e130aa94f7d49be11a63733
Merge: 4ba316c e908dc8
Author: Jim Kingdon
Date: Tue Mar 1 20:40:57 2011 -0500

Merge branch 'withoutdeletedfiles'

$ git diff --stat -w e908dc8..master
Interface/S/e/t/Set theory | 13 +-
Main/O/u/t/Out lines | 311 +++++++++++
[and the rest of the diffs I'd expect from the wiki to git]
22 files changed, 4626 insertions(+), 47 deletions(-)
$ git diff --stat -w 4ba316c..master
Main/W/P/.3a/WP:INDEX | 2 --
Wikiproofs/S/u/b/Subject index | 30 ------------------------------
2 files changed, 0 insertions(+), 32 deletions(-)
$ git push
$