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