Not happy Jan
While the integration of Git into XCode is a good step forward, for me it doesn’t go far enough. As I have been using XCode and Git I have been finding many use cases which you just can’t do within XCode, and that force you to drop back to the command line in order to get things. This includes things like seeing the true state of things, creating branches from previous commits and helping XCode/Git sort out the state of a project after it got a little confused.

So this week I am going to keep on the subject of Git point out a couple useful command line basics. This won’t be an extensive Git tutorial, so if you are looking for a whole lot more, then you should head on over to somewhere like Top 10 Git Tutorials for Beginners where you’ll find an extensive list of dedicated tutorials.

Before you can use any of these commands, you’ll need to open a Terminal window and navigate to the directory that your project is located in.

So show me!
The most basic Git command you can use is simply:

git

Which will respond with a summary of all the Git sub-commands

usage: git [--version] [--exec-path[=]] [--html-path]
           [-p|--paginate|--no-pager] [--no-replace-objects]
           [--bare] [--git-dir=] [--work-tree=]
           [-c name=value] [--help]
            <command> [<args>]

The most commonly used git commands are:
   add        Add file contents to the index
   bisect     Find by binary search the change that introduced a bug
   branch     List, create, or delete branches
   checkout   Checkout a branch or paths to the working tree
   clone      Clone a repository into a new directory
   commit     Record changes to the repository
   diff       Show changes between commits, commit and working tree, etc
   fetch      Download objects and refs from another repository
   grep       Print lines matching a pattern
   init       Create an empty git repository or reinitialize an existing one
   log        Show commit logs
   merge      Join two or more development histories together
   mv         Move or rename a file, a directory, or a symlink
   pull       Fetch from and merge with another repository or a local branch
   push       Update remote refs along with associated objects
   rebase     Forward-port local commits to the updated upstream head
   reset      Reset current HEAD to the specified state
   rm         Remove files from the working tree and from the index
   show       Show various types of objects
   status     Show the working tree status
   tag        Create, list, delete or verify a tag object signed with GPG

See 'git help ' for more information on a specific command.

Of these the one I use the most is:

git status

As it shows what the current state of my repository is. For example, on a project I am currently working on, running this command results in:

165-156-000-000:NotchFlow joalah$ git status

# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   NotchFlow/CalcViewController.m
#
no changes added to commit (use "git add" and/or "git commit -a")

Which tells me that I am working on the “master” branch and there is one file that has been modified but not yet committed to the repository. While I knew from the XCode IDE that I had modified a file, the IDE itself doesn’t tell you what branch you are currently working on. For that you need to either open the XCode Organizer, or drop into the command line.

Another important command for seeing what is what, is:

git branch

By itself it will list out the branches that exist in the repository, and mark the one that is currently checked out:

n165-156-000-000:NotchFlow joalah$ git branch
  CleanupMVC
  LastWorking
  NewKeyboardLayout
  SmallerPreferences
* master

And finally the ultimate list of information from the repository can be seen in:

git log

This lists everything that has been done, tracking each change with the hash of each commit, and listing them all the way to the creation of the repository. For a project that has been under development for a while this can be a surprisingly long list of commits. Thus the log command comes with a slew of options with which to limit, arrange and order its results. The simplest of these options are things like:

-n Limits the output to the previous ‘n’ commits
-pretty=oneline Summarizes the commit hash and comment all on one line
--abbrev-commit Displays a partial commit hash

An example of using this command is:

n165-156-000-000:NotchFlow joalah$ git log --pretty=oneline -5 --abbrev-commit
adb209e Merged in cleanup of main view controller
55f771a Removed unused file
0624d7c Finished ceasing up locations
705aef0 ignoring autogenerated files
7c03745 Cleaned up location of text

One benefit of the abbreviated commit hash is that you can use it anywhere that git wants to see a commit hash. Thus you don’t have to key in all 40 characters of the full commit hash all the time.

Jumping from branch to branch
Once you know what branches are available, you can easily switch between them with

git checkout <branchname>

For example:

n165-156-000-000:NotchFlow joalah$ git branch
  CleanupMVC
  LastWorking
  NewKeyboardLayout
  SmallerPreferences
* master

n165-156-000-000:NotchFlow joalah$ git checkout CleanupMVC
M	NotchFlow/CalcViewController.m
Switched to branch 'CleanupMVC'

n165-156-000-000:NotchFlow joalah$ git branch
* CleanupMVC
  LastWorking
  NewKeyboardLayout
  SmallerPreferences
  master

Splitting branches
Like from within the XCode Organizer tool you can create and delete branches using the git command line. But differing from the Organizer you can also do so with a lot more options than just creating a new branch from the current state of an existing branch. The git command for branching:

git branch

Has some useful variations such as:

-m [<oldbranch>] <newbranch> Move or rename an existing branch.
-d <branchname> Delete an existing branch.
<branchname> [<start-point>] Create a new branch.
 
 

To me the ‘Create’ variant is the most useful, as it allows you to create a new branch from an arbitrary starting point in the repository. The starting point can be one of:

<branchname> The name of an existing branch
<commit-id> An arbitrary commit id from anywhere in the repository
<tag> An arbitrary tag from anywhere in the repository
 

If it is not specified, the the HEAD of the currently checked out branch will be used.

The following is an example of creating a new branch from a pre-existing commit, and then checking out the new branch. Note that the starting point was specified with only the abbreviate commit hash:

n165-156-000-000:NotchFlow joalah$ git log -5 --pretty=oneline -5 --abbrev-commit
55f771a Removed unused file
0624d7c Finished ceasing up locations
7c03745 Cleaned up location of text
e0bb05d Working with new messaging system
e70b543 Back together again .. I think

n165-156-000-000:NotchFlow joalah$ git branch WorkingAgain e0bb05d

n165-156-000-000:NotchFlow joalah$ git branch
* CleanupMVC
  LastWorking
  NewKeyboardLayout
  SmallerPreferences
  WorkingAgain
  master

n165-156-000-000:NotchFlow joalah$ git checkout WorkingAgain
M	NotchFlow/CalcViewController.m
Switched to branch 'WorkingAgain'

n165-156-000-000:NotchFlow joalah$ git log -5 --pretty=oneline -5 --abbrev-commit
e0bb05d Working with new messaging system
e70b543 Back together again .. I think
2c98537 New Display sort of broken
50b9427 Mostly works
e7d9596 Sort of working with new keyboard
n165-156-000-000:NotchFlow joalah$ 

Marking the trail
Finally in order to indicate a significant milestone in the life of a project, you can mark a commit with a tag. A tag is simply a text string that carries some meaning for the person who created it. Typically you would tag a commit to indicate something like the completion of a specific release version. The tags also include a description part which you can use to explain why a commit has been tagged.

With the git command:

git tag

You can list, add and delete tags through the commands options. Useful combinations of these options are:

-l [-n[<num>]] List all the existing tags, with ‘num’ lines of their descriptions. Note that if not specified, ‘num’ defaults to zero!
<tagname> -m <msg> [<commit>] Create a new tag with a a description <msg> for either the last or an arbitrarily specified commit hash
-d <tagname> Delete a tag
 

As an example:

n165-156-000-000:NotchFlow joalah$ git tag -l -n1
v0.01           Technical Feature Complete

n165-156-000-000:NotchFlow joalah$ git tag v1.0 -m 'testing tags'

n165-156-000-000:NotchFlow joalah$ git tag -l -n1
v0.01           Technical Feature Complete
v1.0            testing tags

n165-156-000-000:NotchFlow joalah$ git tag -d v1.0
Deleted tag 'v1.0' (was 3e9e48e)

n165-156-000-000:NotchFlow joalah$ git tag -l -n1
v0.01           Technical Feature Complete

Given a git tag, you can see the associate hash from this command:

git show <tag>

By default this produces a rather verbose output, but it can be trimmed down with a format option as in this example:

n165-156-000-000:NotchFlow joalah$ git show V1.0 -s --format="%h, %cd, %cr, %s"
tag v1.0
Tagger: Peter Milway <peter@JoalahDesigns.com>

testing tags
e41ee4a, Wed Mar 14 15:51:28 2012 -0400, 68 minutes ago, Added changes

Once again its been a pleasure
Peter

Advertisements

 
Note that I have revised these ideas in Displaying Git details in XCode projects revisited
 

Cruising along
Source or Version control systems (VCS) are great for organizing the journey of a program from its initial inception through to the latest release. Through judicious use of committing and tagging your files, you can easily retrieve, rebuild or debug any version of code from anywhere in your programs life history. (In fact proper use of a source control system is something that all professional software writers should master, no matter what flavour.)

With XCode 4, Apple chose to integrate Git directly into the editor, allowing it to offer to build repositories each time you create a new project. As a VCS, Git is one of the more modern varieties, built on a premise of distributed software development, and easily creating and merging multiple branches of code in parallel. But while not everyone works with a development team distributed around the world, everyone should know how to create and merge branches in XCode.

The ease of branching in Git helps to keep the main branch of your code pollution free. Instead of trying a new feature in the release version, you branch off the entire codebase to a test branch, and try new things, or build new features in isolation. Then when you are happy with the result, you just merge your changes back into the main branch.

Rocks ahead!
But what if you have multiple versions/branches of your program out in the world and a user reports a bug. How do you know what code was used to build that particular instance? Somehow you need to reconcile the bug report with the code in your VCS, so you know what code to pull for testing. If only the bug report was tagged with the codes version and you could look up that tag in the repository!

A naive way of doing this would be to place a string or numeric constant in your code that described the current version information and then manually update it when you decided on a version change. This could then be displayed on a label somewhere in your program.

NSString* version = @"Version 1.1";

...

label.text = version;

The problem with this type of system is two-fold:

  1. The version is never guaranteed to be correct, as you will make changes to the program, but forget to update the string.
  2. The VCS doesn’t know about the string, so you can’t ask it to return that particular version

Charting a better course
As the VCS knows about the what versions of code are stored in it, it is better to get the VCS to tell the code what version it is. And if you make this a part of the projects build system, then this will happen automatically and seamlessly – thus saving you from a manual step.

In the case of Git, there are several items that can be extracted from the repository that can uniquely identify what version of code was used to build a particular application’s release:

  • The current branch name
  • The last commit hash
  • The last commit date
  • The last commit comment
  • The last repository tag

All of these items can easily be obtained by running the command line Git tool like so:

Current branch name: git rev-parse –abbrev-ref HEAD
Last commit hash: git log –pretty=format:”%h” -1
Last commit date: git log –pretty=format:”%ad” –date=short -1
Last commit comment: git log –pretty=format:”%s” -1
Last repository tag: git describe –abbrev=0 –tags

(For a description of these commands, please see the Git website)

All that is needed now is to integrate this into your projects build system!

Building the build system
When I researched how to get the Git details into a project, there were several solutions that involved running a script, creating a file and modifying plists and then doing a few more other things. To me they were overcomplicating things a bit. All I wanted was to create a bunch of #defines in a simple .h file and include that where it was needed. Doing this in XCode 4 turned out to be very very easy. All that I did was:

  1. Select the target I want to include the Git data in
  2. Selected the Build Phases of the target
  3. Created a new “Run Script”
  4. Entered OSX shell script commands to create the .h file
  5. Imported the .h file into the source as needed
  6. Added the .h file to my git .ignore file

Visually this is:

Select the Target in the project</td
Go to the Build Phases</td
Create a new “Run Script” phase</td
Move the “Run Script” to be before the “Compile Sources” phase</td
Edit the “Run Script”</td

All is revealed
My script file that I inserted into the Run Scripts is:

# build data file that is included in the source
# so we can automatically report Git repo information
# in the application

cd ${PROJECT_DIR}/${TARGETNAME}
gitDataFile="gitDataAutoGenerated.h"
buildDate=`date "+%F %H:%M:%S"`
currentBranch=`git rev-parse --abbrev-ref HEAD`
lastCommitHash=`git log --pretty=format:"%h" -1`
lastCommitDate=`git log --pretty=format:"%ad" --date=short -1`
lastCommitComment=`git log --pretty=format:"%s" -1`
lastRepoTag=`git describe --abbrev=0 --tags`

echo -e "//-----------------------------------------" > $gitDataFile
echo -e "// Auto generated file" >> $gitDataFile
echo -e "// Created $buildDate" >> $gitDataFile
echo -e "//-----------------------------------------" >> $gitDataFile
echo -e "" >> $gitDataFile
echo -e "#define BUILD_DATE              @\"$buildDate\"" >> $gitDataFile
echo -e "#define GIT_CURRENT_BRANCH      @\"$currentBranch\"" >> $gitDataFile
echo -e "#define GIT_LAST_COMMIT_HASH    @\"$lastCommitHash\"" >> $gitDataFile
echo -e "#define GIT_LAST_COMMIT_DATE    @\"$lastCommitDate\"" >> $gitDataFile
echo -e "#define GIT_LAST_COMMIT_COMMENT @\"$lastCommitComment\"" >> $gitDataFile
echo -e "#define GIT_LAST_REPO_TAG       @\"$lastRepoTag\"" >> $gitDataFile

And for the current state of my project, this produces the gitDataAutoGenerated.h file with the following contents:

//-----------------------------------------
// Auto generated file
// Created 2012-03-04 18:41:15
//-----------------------------------------

#define BUILD_DATE              @"2012-03-04 18:41:15"
#define GIT_CURRENT_BRANCH      @"master"
#define GIT_LAST_COMMIT_HASH    @"8988fe8"
#define GIT_LAST_COMMIT_DATE    @"2012-03-04"
#define GIT_LAST_COMMIT_COMMENT @"Toned down size of elements in preferences"
#define GIT_LAST_REPO_TAG       @"v0.01"

Which I include in my program as:

#import "gitDataAutoGenerated.h"
...
programBuildDate.text = BUILD_DATE;
programCreateDate.text = GIT_LAST_COMMIT_DATE;
programVersion.text = [NSString stringWithFormat:@"%@%@%@", GIT_LAST_REPO_TAG,([GIT_LAST_REPO_TAG length]>0?@".":@"") , [GIT_LAST_COMMIT_HASH uppercaseString]];

And I display this on an information page as:

So now if ever I get a bug report from this program, I can use the Git commit hash of 8988fe8 to go straight to the source and re-create exactly the program version that the user has. Of course if I was being really retentive I would have archived the software binary along with the source code! But thats a story for a different day.

Hope you all found this useful.
Peter

It Lives!  My first steps into the world on blogs, and I’m hoping they’ll be more “Young Frankenstein” than “Frankenstein”.

As I mention in the About, Joalah Designs is my way of trying keep busy during the daytime hours – performing programming of various sorts in various locations.  And that this blog is meant to document some of the interesting and challenging ideas and situations that I encounter during my journey through the working world.

This is going to encompass everything from control systems programming for industry, through to iOS programming and even a bit of business practices and philosophy to boot. So sit down, strap in and hang on – it may be a bumpy ride.

And if your curious about what I can do for you, check out my main website at www.JoalahDesigns.com

Regards
Peter M