With XCode 4′s integration of Git for version control, I found an old post from Marcus Zarra and Matt Long of Cocoa Is My Girlfriend to be very useful. Back in 2008 they built a handy build number script. It builds on Daniel Jalkut’s Perl script, originally written for Subversion, that injects a unique identifier from the latest commit into an application’s Info.plist file on every build. Such unique identifiers are very useful for developers in distinguishing larger public revisions (i.e. version 1.5) from smaller internal revisions (i.e. build 5369f78).

How to arrange your new build phase. (Click to enlarge.)
I’ve written a Python script that does much the same thing for XCode 4. To use this script, create a new “Shell Script Phase” at the end of your target’s build phase chain, just after “Copy Bundle Resources.” Paste the contents of the script into that phase, and be sure to specify “/usr/bin/python” as the shell for the script. The script will edit the Info.plist file of your fresh app build to reflect the latest git revision.
# XCode 4 auto-versioning script for Git
# Inspired by the work of Axel Andersson, Marcus S. Zarra and Matt Long
# http://valthonis.net/u/19
"""
NOTE: Due to its use of build environment variables, this
script will only work from inside XCode's build process!
"""
import os
import csv
from subprocess import Popen, PIPE
from Foundation import NSMutableDictionary
cmd = "/usr/local/bin/git rev-parse --short HEAD" # get the short commit hash from git
build_number = Popen(cmd, shell=True, stdout=PIPE).stdout.read()
info_plist = os.environ['BUILT_PRODUCTS_DIR'] + "/" + os.environ['WRAPPER_NAME'] + "/Info.plist"
# Open the plist and write the short commit hash as the bundle version
plist = NSMutableDictionary.dictionaryWithContentsOfFile_(info_plist)
core_version = csv.reader([plist['CFBundleVersion'].rstrip()], delimiter=" ").next()[0]
full_version = ''.join([core_version, ' build ', build_number])
plist['CFBundleVersion'] = full_version
plist.writeToFile_atomically_(info_plist, 1)
EDIT: Changed script to append build number to project version. The plist should now use the form “1.5 build 5369f78″
EDIT: Fixed a bug where successive builds might repeatedly append build numbers to the core version (resulting in version strings like “1.6 build 5a32f28 build 03b295e” and so on.) As such, the script now requires Python’s built-in csv module. Also, this script is now available via gist.github.