for ... else in Python

Python has an interesting for statement (reference) which lets you specify an else suite.

In a construct like this one:

for i in foo:
  if bar(i):
    break
else:
  baz()

the else suite is executed after the for, but only if the for terminates normally (not by a break).

Here's some code written without for...else:

def contains_even_number(l):
  "Prints whether or not the list l contains an even number."
  has_even_number = False
  for elt in l:
    if elt % 2 == 0:
      has_even_number = True
      break
  if has_even_number:
    print "list contains an even number"
  else:
    print "list does not contain an even number"

The equivalent code snippet below illustrates how the use of for...else lets you remove an extraneous flag variable from that loop:

def contains_even_number(l):
  "Prints whether or not the list l contains an even number."
  for elt in l:
    if elt % 2 == 0:
      print "list contains an even number"
      break
  else:
    print "list does not contain an even number"

Use your good judgment when deciding whether to use the for...else construct. It's not unequivocally better, but when there's an asymmetry between the two possibilities, you can make your code more readable by using for...else to keep the "happy path" logic at the top and the exceptional/error case at the bottom.

Version control with Git: git-rebase --interactive

Git 1.5.3 introduces git-rebase --interactive, which lets you alter the commit history in various ways, including splitting, squashing (combining), inserting, and removing patches. In each case git-rebase rewrites the subsequent commit history so no one else is the wiser.

Start by doing:

git-rebase -i f00bab

where f00bab is the commit before the first commit you want to change.

Git opens an editor describing the commits since that commit, in chronological order, in the following format:

pick 1e4dfd7 Foo bar commit.
pick 6b78037 Baz quuz commit.
:

You can edit this list to tell Git to do certain things:

  • Remove a line to delete the corresponding commit.
  • Move lines around to reorder commits.
  • Change pick to squash on a line to combine that commit with the previous commit.
  • Change pick to edit to modify or split that commit (see below).
  • Git will attempt to reapply all other commits (lines which are still labeled pick).

When you squash a commit, Git prompts you for a new message for the combined commit.

When you choose edit, Git applies that commit but pauses the rebasing process so you can edit your tree. There are a couple of ways to proceed:

  • To edit the commit message only, just do git commit --amend.
  • To edit the commit itself, make some changes, git add them, and do git commit --amend.
  • To split the commit into multiple commits, do git reset HEAD^ to rewind the branch without touching the working copy. Stage and commit (git add ...; git commit) your first change. Repeat that as many times as you like until the branch catches up to your working copy. git-add --interactive can be useful if you want to pick and choose parts of files to stage. Notice that if you only use git add and git add --interactive then your working tree never changes. If you really want to work with the intermediate states— for example, to run unit tests or whatnot— use git-stash to put away your working copy temporarily.
  • You can also make additional commits here, which will appear right after the commit being edited.

In any case, when you're finished at that point in history, use git-rebase --continue to move on.

Whenever you change a commit, Git applies the patches from subsequent commits (at least to the best of its ability; if a change you make causes a subsequent patch to not apply cleanly, then Git will stop to ask you to resolve the conflict). However, these new commits will have different identifiers.

Further reading: git-rebase documentation

Version control with Git: git-add --interactive

Git 1.5.0 introduces git-add --interactive, which lets you stage changes at a finer granularity than the file level. This is useful if you've made a number of changes in a file before you realize that they logically should go in as separate commits. It works by allowing you to pick and choose hunks of the diff to stage.

To start:

$ git add --interactive
           staged     unstaged path
  1:    unchanged       +26/-6 path/to/file1
  2:    unchanged        +1/-0 some/other/file

*** Commands ***
  1: status   2: update   3: revert   4: add untracked
  5: patch    6: diff     7: quit     8: help
What now>

git shows you all the files which have changes from HEAD. To pick and choose diffs, select 5 (patch) at the menu. Git prompts you to select one of the listed files by number.

Git then shows you hunks of the diff between HEAD and your working copy. For each hunk, you can choose whether or not you want to stage the hunk. For large hunks, Git will also offer to split the hunk into smaller hunks which you can stage independently. (There are other options, such as options to stage or unstage all remaining hunks.)

Once you're done, you can select 7 to quit, do git diff --cached to verify the staged changes, and do git commit as usual to commit.

Update: you can also use git-add --patch FILENAME, which skips the menu and jumps directly to the hunk selection part. I usually use this instead of git-add --interactive now and I've aliased it to gap in my shell. If you use Emacs, magit is a great extension that also supports staging and unstaging individual hunks.

Further reading: git-add documentation

Version control with Git: branches

In git, branches are a lightweight way to manage multiple lines of development. You might use a separate branch for work on an experimental feature, or to maintain parallel lines of code with just a few differences. In either case, it's often essential to copy changes ("merge") from one branch to another to keep them synchronized, and git handles this very well.

To create a new branch named mynewfeature and switch to it:

git branch mynewfeature
git checkout mynewfeature

Now, commits you make will appear on the mynewfeature branch, but will not affect the default branch (which is named master). Running gitk --all conveniently shows you commits and branches, and lets you see graphically when your branches diverged or were synchronized.

To resume working on the master branch:

git checkout master

Git will put away all the file versions associated with your branch and restore the file versions associated with the master.

Typically, after you branch, new changes will be committed both on the branch and on the master (main line). Merging changes on the master into your branch regularly will ensure that your branch contains the latest updates from the main line (but does not apply to the master any changes you've made on the branch). Assuming you have checked out a branch, you can merge changes from the master with:

git merge master

Git applies all the changes made on the master since your branch diverged from it (or since the last time you merged). It then (usually) creates a new commit representing the merged state. If there is a merge conflict, Git will not make the commit: you will be asked to fix up the merge and then git commit -a the result.

Once you are done with your new feature, it's time to merge it back into the master! Checkout the master and then merge your branch like so:

git merge mynewfeature

You can then delete the branch:

git branch -d mynewfeature

Keyboard navigation in Google search results

Google Labs has an experimental version of Google search that lets you use GMail-like keyboard shortcuts to navigate through Google search results!

Like in GMail, an arrow is displayed next to the search results to indicate the currently selected one. j and k go to the next and previous results, respectively, o opens a result, and / puts you back in the search box.

To activate keyboard shortcuts for your Google account, go to Google Experimental Search and select the Keyboard shortcuts experiment.