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