Git is such a complex tool that I often feel as if I'm barely using 10% of it's complete functionality. The various commands range from the absolutely essential ( commit , push , pull ) to the more exotic ( cherry-pick , rebase ), to the downright obscure or scary ( fsck , merge-octopus , quiltimport ). You can generally get by with knowing the basic functions of a small set of commands, that allow you to push, pull, commit, change branches and merge. However, this list compiles 6 commands that you may not know about that have seriously improved the way in which I use git.

1. git log -p <path>

I recently came across this as I was searching for an easy way of tracking a file's changes over time. git log is git's way of showing you the history of your codebase, and adding a path to the command will limit the log to the changes in that one file. Finally, the -p flag includes the diff on the file at each commit, so you can see exactly how the file has changed at each point in the history of the code. Example:

$ git log -p README.md commit 524d044afdbedafd94e81c5a0434150efd3a2860 Author: Jon Cairns <jon@ggapps.co.uk> Date: Thu Sep 18 15:30:00 2014 +0100 Update README diff --git a/README.md b/README.md index 8f73735..082d6fa 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ It's been created with Jekyll. Feel free to poke around. Social images courtesy of http://bostinno.streetwise.co/channels/social-media-share-icons-simple-modern-download/. -It builds automatically using a Bitbucket POST hook and a basic sinatra app. +It builds automatically using a Bitbucket POST hook and a basic sinatra app, but for security reasons the S3 configuration has been left out. commit df259431cbd3e6844a100ca5bc31d7c518595e86 Author: Jon Cairns <jon@ggapps.co.uk> Date: Fri Jun 20 12:03:41 2014 +0100 Testing bitbucket web hook diff --git a/README.md b/README.md index ac10f9e..8f73735 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,5 @@ It's been created with Jekyll. Feel free to poke around. Social images courtesy of http://bostinno.streetwise.co/channels/social-media-share-icons-simple-modern-download/. + +It builds automatically using a Bitbucket POST hook and a basic sinatra app. ...

It's worth getting to grips with diff formatting, as git uses it extensively.

2. git checkout <commit>

If you want to see the state of your code at a particular point in history, git makes this trivial. Just run this command with a commit hash as the argument (you can use git log to find the hash you want to revert to). For example:

$ git checkout 05c5fa Note: checking out '05c5fa' . You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so ( now or later ) by using -b with the checkout command again. Example: git checkout -b new_branch_name HEAD is now at 05c5fa... Merge branch 'master' of bitbucket.org:etc/etc

As shown above, git will tell you that you're in a "'detached HEAD' state". This means that any changes won't affect the state of your code before you checked out. To get back to your original state, just run git checkout master (replace master with whichever branch you were on, if different).

3. git checkout <tree-ish> -- <path>

Surprisingly unrelated to the command above (a bug-bear of some git critics), this command allows you to pull a file at a given commit into your current workspace. This is useful in the case when you need to restore a deleted file, or restore a file to a previous state.

Here, <tree-ish> refers to a commit hash, tag or branch, and <path> is the path of the file relative to the top directory of the project. Here's an example:

$ git checkout 05c5fa -- config/routes.rb

Note that this will overwrite any existing file at the same path in your working tree.

4. git stash

This is an extremely useful command that isn't as well known as it should be. If you have uncommitted changes in your tree (i.e. it's "dirty", in git language) that you want to temporarily undo, then this command allows you to stash them for later. This is particularly useful if you want to check the state of your code before your changes, or if you want to merge in someone elses code but aren't yet ready to commit your code as it is.

Stashed changes go into a list, or stack. You can then re-apply these changes when you want, in any order you choose. Here's an example:

$ echo "This is a change" >> README $ git status On branch master Your branch is up-to-date with 'origin/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: README.md no changes added to commit ( use "git add" and/or "git commit -a" ) $ git stash Saved working directory and index state WIP on master: c689fa6 Add second part of refactoring post HEAD is now at c689fa6 Add second part of refactoring post $ git status On branch master Your branch is up-to-date with 'origin/master' . nothing to commit, working directory clean

So I made a change to a file (one that was already tracked by git), which made my working tree dirty, and then used stash to restore my tree to how it was at the previous commit.

I can see the stash list with git stash list and restore the changes by using git stash apply :

$ git stash list stash@ { 0 } : WIP on master: c689fa6 Add second part of refactoring post $ git stash apply On branch master Your branch is up-to-date with 'origin/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: README.md no changes added to commit ( use "git add" and/or "git commit -a" )

If you have multiple items in your stash list, you can specify the stash to restore by using the full name when applying, e.g. git stash apply stash@{1} .

Using stash is a handy way of temporarily reverting and re-applying changes to your working tree.

5. git cherry-pick <commit>

This command allows you to apply a single commit to your working tree. For example, if there's a commit in another branch that you want to apply but the branch isn't ready to fully merge, you can use this command to grab that commit and drop it in to your current branch. Here's an example, merging a single commit from master to random-branch :

$ git cherry-pick 9e4aec [ random-branch 89af20d] Add action to QA.1 Date: Tue May 26 14:53:03 2015 +0100 1 file changed, 4 insertions ( + )

The tricky thing with this is that you can easily end up with merge conflicts. The trouble with picking a commit out of nowhere is that it may relate to files that don't exist in your working tree, or are very different to the existing files.

One very useful application of this is when you have a project with a separate production and staging branch. For example, if something gets fixed on the staging branch, alongside some new features being developed, and the fix needs to be applied to the production branch, cherry-pick can be used to apply only those fixes.

6. git annotate <file>

This handy command shows each line of a file next to information about which commit last changed that line, when it changed and who changed it. Example:

$ git annotate Readme a6b6e79d ( Jon Cairns 2014-04-03 10:11:29 +0100 1 ) # My blog a6b6e79d ( Jon Cairns 2014-04-03 10:11:29 +0100 2 ) 524d044a ( Abe Lincoln 2014-04-03 10:11:29 +0100 3 ) It 's been created with Jekyll. Feel free to poke around. a6b6e79d (Jon Cairns 2014-04-03 10:11:29 +0100 4) a6b6e79d (Jon Cairns 2014-04-03 10:11:29 +0100 5)Social images courtesy of http://bostinno.streetwise.co/channels/social-media-share-icons-simple-modern-download/. df259431 (Jon Cairns 2014-06-20 12:03:41 +0100 6) 524d044a (Abe Lincoln 2014-09-18 15:30:00 +0100 7)It builds automatically using a Bitbucket POST hook and a basic sinatra app, but for security reasons the S3 configuration has been left out.

And so on