Git Tutorial – Git Fu With The Command Line

Git Tutorial – Git Fu With The Command Line

There are many different GUIs for Git these days: GitHub for Mac, Tower, SourceTree, Xcode and more. While these options are fine for most situations, many experienced developers would tell you that the Command Line Interface (CLI) is the best option and should be the primary tool you use. git-logo

While “best” is an imprecise, subjective term, the Git CLI is certainly the most powerful and versatile way to use Git, as the GUI options usually just run the CLI commands under the hood. Anything a Git GUI can do, the command line can do — probably better.

This Git tutorial introduces you to a few ways to use the command line more effectively with Git.

In particular, you’ll learn how to make the command line a friendlier place by adding autocompletion, and how to make the command line prompt provide you with help and Git guidance.

Note: It’s often been the case that graphical Git clients have simply executed Git commands under-the-hood. However, an open source, portable, pure C implementation of the Git core methods is now available in libgit2, and is being used by industry heavyweights, such as Microsoft, to power their Git tools

Getting Started

If you’re new to Git or just need to brush up, here are two links to help you get up to speed.

Also, before continuing, make sure that you’ve installed Xcode’s Command Line Tools (CLT). To find out what you have, open a terminal window and at the prompt enter

xcode-select -p

If you see no error message, you’re OK.

If the tools are not there, type the line below into Terminal, and then follow the instructions in the resulting dialog.

xcode-select --install

Why You Care About Fancy Git Commands

…but might not realize it yet.

To start, take a quick look at some of the most common issues people have with Git repositories, or repos for short. Almost all Git questions on Stack Overflow involve the CLI.

From the volume of CLI related questions, it’s safe to draw the conclusion the majority of developers predominantly use the CLI to handle their Git repositories.

One reason for it’s popularity is because it provides the extra power necessary when your needs have outgrown even the most complete GUI, and your repo requires some low level, fine-grained TLC.


Anything a GUI can do, the CLI can do (and often better).

Even if your Git GUI of choice is capable of handling your exact situation, you’re likely to have a session of tinkering with menu options and checkboxes to figure out how to address your specific issues. All the while, the GUI will simply be executing CLI commands in the background, so you might as well do it for yourself. If for nothing more than to gain a solid understanding of what’s happening under-the-hood.

One more reason to love Git’s CLI is that it’s been around longer, and has a much larger community, than any Git GUI. Subsequently, there are more people out there who can answer your questions about the Git CLI.

Another shortcoming of Git GUIs shows up when you start using a standard branching model, because these tools often don’t have built-in support for the popular models. However, Atlassian’s SourceTree supports the popular Git-flow model, and it happens to have a pretty useful overview of the different classes of branching models, also known as work-flows.

But what if I want a cool graph of my commits? Can the non-graphical CLI provide that?”

Yes, yes it can. Follow along and you’ll learn how to make it happen.

Cool History Graphs

It turns out the Git CLI can produce useful, almost pretty, commit-history graphs for you. It even lets you filter for the commits you actually want to see.

First, you need a repo with something worth looking at, and for this tutorial you’ll find it inside the git-extras repository.

You’re just going to use this repo as an example to illustrate some CLI usage, but the commands here are quite useful and you may want to install them to enhance Git’s CLI further.

Look at that, one more reason to love the Git CLI — the ability to extend Git via the command line is yet another reason why the Git CLI holds an edge over any Git GUI.

Now use one of Git’s most basic commands, git clone to make a copy of this repo on your local machine. First open a terminal window and navigate to the folder where you want to store the repo, and then type the commands below:

git clone
cd git-extras

If you get any errors from the clone command above, you can replace that line by:

git clone

Note: If you do see an error here, it’s likely because you’ve not setup your SSH keys with GitHub. You can find out more about how to do that and why it’s important here

Make sure you’re in the git-extras directory by entering pwd on the command line. Now, take a look at a graph of commits by entering the following command:

git log --graph --pretty=oneline --abbrev-commit

And you should see something like this.

*  3b3efd7 Merge pull request #219 from jhoffmann/patch-1
| * 3c6a084 Update
* |  47bb6e0 Merge pull request #222 from jsipprell/pull/filenames-containing-spaces
|\ \ 
| * | 1479b96 git effort: handle filenames containing whitespace cleanly
| |/ 
* |  039bb5c Merge pull request #223 from mwoc/master
|\ \ 
| * | 0687de7 Use two-space indents for log entry output, so it again is debian changelog compatible (as before commit 1235e4a5)
| |/ 
* |  a87403b Merge pull request #229 from yggdr/master
|\ \ 
| * | e4290df invoke bash via /usr/bin/env for portability
* | |  45b3515 Merge pull request #231 from petersohn/master
|\ \ \ 
| |/ / 
|/| |  
| * | 698d027 Make git-obliterate work if there are whitespaces in filename
|/ / 
* | d437418 Release 1.9.1
* |  ef00c0c Merge pull request #225 from pmalek/master
|\ \ 
| |/ 
             -- And it continues!

Hit the spacebar a few times; you’ll see pages and pages of commits and a graphical representation of the branches and merges over time. When you’ve soaked in the graph, hit q to quit.

That’s probably a lot more commits than you want to dig through. But you can limit the scope in various ways — for instance, by date.

Enter the command below:

git log --graph --pretty=short --abbrev-commit --since=April

This shows just the commits since April, and it also grabs some useful info about each commit by asking for a short format instead of oneline.

Now you see a much shorter graph like this one.

* commit 3b3efd7
| Merge: 47bb6e0 3c6a084
| Author: 
|   Merge pull request #219 from jhoffmann/patch-1
*  commit 47bb6e0
|\ Merge: 039bb5c 1479b96
| | Author: 
| | 
| |   Merge pull request #222 from jsipprell/pull/filenames-containing-spaces
| |  
| * commit 1479b96
|  Author: Jesse Sipprell 
|    git effort: handle filenames containing whitespace cleanly
*  commit 039bb5c
|\ Merge: a87403b 0687de7
| | Author: 
| | 
| |   Merge pull request #223 from mwoc/master
| |  
| * commit 0687de7
|  Author: Maarten Winter 
|    Use two-space indents for log entry output, so it again is debian changelog compatible (as before commit 1235e4a5)
*  commit a87403b
|\ Merge: 45b3515 e4290df
| | Author: 
| | 
| |   Merge pull request #229 from yggdr/master
| |  
| * commit e4290df
| | Author: Konstantin Schukraft 
| | 
| |   invoke bash via /usr/bin/env for portability
| |   
* |  commit 45b3515
|\ \ Merge: d437418 698d027
| |/ Author: 
| |    Merge pull request #231 from petersohn/master
| |  
| * commit 698d027
|/ Author: eszabpt 
|    Make git-obliterate work if there are whitespaces in filename
* commit d437418
| Author: TJ Holowaychuk 
|   Release 1.9.1
* commit ef00c0c
| Merge: e91da7c 4145f22
| Author: TJ Holowaychuk 
|   Merge pull request #225 from pmalek/master
* commit 4145f22
| Author: Patryk Małek 
|   Fixed git-changelog errors when multiple files match change|history
* commit de6463f
 Author: Patryk Małek 
   Fixed git-changelog errors on first usage

Don’t forget to hit q to exit.

Next, filter your graph to show only commits by a specific user. Graph all the commits by Patryk Małek since April, and list a bunch more info on each commit by entering the following command:

git log --graph --pretty=fuller --abbrev-commit --since=April --author="Patryk Małek"

Now you have a shorter, much more useful list of commits with quite a lot of detail.

* commit 4145f22
| Author:   Patryk Małek 
| AuthorDate: Mon Jun 16 16:32:47 2014 +0200
| Commit:   Patryk Małek 
| CommitDate: Mon Jun 16 16:32:47 2014 +0200
|   Fixed git-changelog errors when multiple files match change|history
* commit de6463f
 Author:   Patryk Małek 
 AuthorDate: Mon Jun 16 16:27:57 2014 +0200
 Commit:   Patryk Małek 
 CommitDate: Mon Jun 16 16:27:57 2014 +0200
   Fixed git-changelog errors on first usage

Maybe you’d like a list all of the accepted pull-requests since June? Just enter the following:

git log --grep="pull request" --since=21june

Since GitHub automatically adds a useful message each time an owner of a repo accepts a pull request, you see something like this:

commit 3b3efd74831639a7eeac37add0a1332a0ca9e847
Merge: 47bb6e0 3c6a084
Date:  Sun Jul 6 12:37:41 2014 +0530
  Merge pull request #219 from jhoffmann/patch-1
commit 47bb6e0f7423304caf214998e44e717400b30bad
Merge: 039bb5c 1479b96
Date:  Sun Jul 6 12:36:08 2014 +0530
  Merge pull request #222 from jsipprell/pull/filenames-containing-spaces
  git-effort handles filenames with whitespace.
commit 039bb5c318f329d62cff86d3a11b4c516586b963
Merge: a87403b 0687de7
Date:  Sun Jul 6 12:34:21 2014 +0530g
  Merge pull request #223 from mwoc/master
  Restoring debian changelog compatibility.
commit a87403b57f3c72034be2abc066d40425e8a9a7ba
Merge: 45b3515 e4290df
Date:  Sun Jul 6 11:55:00 2014 +0530
  Merge pull request #229 from yggdr/master
  /usr/bin/env for portability.
commit 45b3515ff811e603c2c9278553af26c7c996c117
Merge: d437418 698d027
Date:  Sun Jul 6 11:52:58 2014 +0530
  Merge pull request #231 from petersohn/master
  git-obliterate now supports whitespaces in filename.
commit ef00c0c2c6d146081347dd75d27ee6a9facb4ee9
Merge: e91da7c 4145f22
Author: TJ Holowaychuk 
Date:  Sat Jun 21 21:53:16 2014 -0700
  Merge pull request #225 from pmalek/master
  Fixed git-changelog errors on first usage

Note: Pull requests are not really a feature of Git, but are part of a work-flow made available by Git repository hosting services such as GitHub and Bitbucket.

Even the Best Tools Aren’t Without Fault

OK, the Git CLI is cool and powerful, but you might feel that:

  • There’s a bunch of typing, especially if you’re using a formal branching strategy.
  • It’s easy to misspell parameters, options, names of branches, remotes, files and commit hashes.
  • Here’s the clincher: It’s easy to complete hours of work, only to find that you’ve been working on and committing to — and possibly even force-pushing — #gasp — the WRONG BRANCH.
  • All in all, the CLI involves a lot more stuff to remember than just sticking with a GUI.

Stay with me; you’ll see the true value of the CLI in a few moments.

Those are fair points. Any type of GUI that’s outstandingly effective uses menus to organize the functionality servicing the myriad of use cases. Why do GUIs use menus?

Run an Internet search for ‘recognition vs. recall‘ and you’ll see a ton of results that talk about how much faster and more efficiently your brain recognizes something than it recalls something.

For example, pretend you’re trying to remember the name of that one actor who played a supporting role in your favorite movie of 2014, without peeking at your phone. Unfortunately, you just can’t seem to remember.

You could probably pick his name out of a cast list, because your marvelous brain will recognize the name even though it couldn’t recall it.

That’s precisely why GUIs use menus. Users can easily scan menus and recognize the item they want. You can actually use the same idea on the command line.

If you’ve used the command line to navigate around your file system, you probably use a combination of recall and recognition to navigate.

Imagine that you’re typing cd ~/D[tab][tab] (recalling the first letter), and you’ll see a list of items in your home folder that start with the letter D. Then recognizing the name from that list, you type a few more characters, and then [tab] to finish out the directory name.

Everything seems a little easier now, doesn’t it?

Meet your new friend autocompletion, who will help you by offering your brain something to recognize, rather than recall. It’s the next really cool Git CLI tip you’re going to learn all about.

Autocompletion – It’s Not Just For Navigation Anymore

If you’re an experienced terminal user, you probably have a totally awesome shell that you absolutely love, and it’s probably bash since that’s the default shell on OS X. If you use another totally awesome shell, this next section may or may not be relevant, since it’s based on scripts that aim at bash.

Now for the fun stuff! You’re going to install Git bash-completion, aka Git-flow-completion. This provides autocompletion of Git commands, parameters, options, branch names, filenames, remotes — and did I mention branch names??

This is amazingly helpful if you use feature branches. For example, I regularly use branch names that are more than 20 or 30 characters long, because the teams I work with use user-stories for the feature-branch names.

With autocompletion, you’ll prevent brain strain by using command line to help you recognize even the longest, most difficult-to-remember names.

Next up – Homebrew, the power tool that makes installing autocompletion (and more) a breeze.

Homebrew Installation

There are several ways to install Git bash-completion. Since you’re probably a Mac user, this tutorial will use Homebrew for the installation.

What is Homebrew? Are we making beer now?

Well, I’m glad you asked, and no you’re not going to learn how to make a refreshingly crisp ale in this tutorial.

Homebrew is a totally awesome package-manager, written in Ruby and utilising Git. In Homebrew’s own words it “… installs the stuff you need that Apple didn’t.”

Note: As with any software installation, it’s always good to backup your system and data before proceeding.

First check to see if Homebrew is already on your Mac by entering brew -v in the command-line.

  • If you see something like Homebrew 0.9.5, then you already have Homebrew.
  • However, if you see Command not found, then you don’t have Homebrew installed. You’re going to change that right now.

Installing Homebrew is stunningly easy. Enter the following into your command line:

ruby -e "$(curl -fsSL"

This will download and run a Ruby script.

Note: If you’re running Snow Leopard or later, by default you should have a fairly modern and stable version of Ruby on your machine. However, it’s recommended that you run the latest versions of Mac OS X and Ruby.

If you’re curious about what Homebrew’s installed, look in the directory /usr/local/Cellar/.

Updating Homebrew and Playing Doctor

Now that you have Homebrew installed, make sure all of the brew formulae are up to date as there are continual updates, pretty much every day. To bring it up to speed, just enter brew update at the prompt and give it a minute or so, as there are many formulae out there.

Optionally, you can use Homebrew to clean up your environment by entering brew doctor.

For this tutorial, it’s not mandatory and can feel a bit squirrelly because it will tell you about any oddities it finds in your system environment and tell you how to fix them. However, the good doctor’s output can be very helpful when you’re looking for a clue about why an installation went awry.

You’re almost there! Now that you have installed and updated Homebrew, you’re going to install the magical autocomplete tool. Enter the following command at your prompt:

brew install git bash-completion

You’re not quite finished yet. Next, you need to change your bash profile so that bash-completion loads whenever you open the terminal. Follow these instructions.

  1. Open a terminal window, and navigate to your home folder: cd ~
  2. Check to see if the file .bash_profile exists in your home folder: ls .bash_profile
  3. If the file is missing, create it: touch .bash_profile
  4. Open the file in your favorite text editor: open .bash_profile
  5. At the end of the file (unless you have a reason to put it elsewhere), enter the following:
  6. if [ -f `brew --prefix`/etc/bash_completion ]; then
      . `brew --prefix`/etc/bash_completion
  7. Close your editor and the terminal window.

Test It Out!

Open a new terminal window. Just like in bash, autocompletion happens when you type the [tab] key. So, start typing git [tab][tab] and be sure to type a space after ‘git’ before hitting the tab button. You should see the output below.

 ~ $ git 
add                      column                   get-tar-commit-id        pull                     shortlog 
am                       commit                   grep                     push                     show 
annotate                 config                   gui                      rebase                   show-branch 
apply                    credential               help                     reflog                   stage 
archive                  credential-osxkeychain   imap-send                release                  start 
bisect                   deliver                  init                     relink                   stash 
blame                    describe                 instaweb                 remote                   status 
branch                   diff                     log                      repack                   submodule 
bundle                   difftool                 loggraph                 replace                  subtree 
checkout                 fetch                    merge                    report                   svn 
cherry                   filter-branch            mergetool                request-pull             tag 
cherry-pick              finish                   mv                       reset                    whatchanged 
citool                   format-patch             name-rev                 revert                   
clean                    fsck                     notes                    rm                       
clone                    gc                       p4                       send-email               
 ~ $ git

Sweet! A list of all the Git commands pops up, and you can continue typing where you left off.
Rage Comic Doctor is In

Note: If you get error messages instead of the cool list above, then run brew doctor, as mentioned earlier. Fixing the issues the good doctor finds should solve your problems. You’ll know everything is fixed when brew doctor gives you the message “Ready to Brew.”

A conflict with the Git installed by Xcode is a common issue. Try overwriting that Git using brew git install followed by brew link git.

If there are problems, brew doctor will provide you with instructions to fix them.

Now navigate to a Git repository (Navigate to the git-extras folder you installed earlier if you don’t have any repos of your own).

Type git bra[tab], and you’ll see that autocompletion recognizes this as git branch. Continue typing ma[tab], and autocompletion extends this to git branch master. YAY!

[ctrl]-c out of that and try a different command. Type git log --gr[tab][tab] and you see the two available flags. Continue typing a[tab][return].

Nice! You have a graph of your most recent commits! Now use autocomplete to run the following verbose command quickly, typing only as much as is necessary to trigger autocomplete:

log --graph --pretty=oneline --decorate --abbrev-commit

Very cool.

Wait, Wait! Wasn’t there something about remembering what branch I checked out locally?

Yes, thanks for reminding me. Is it even possible for a command-line environment to keep the user informed in real time? Well, what about the prompt?

The prompt is a truly excellent way to keep you informed about the state of the local repo that’s in your current working directory, especially if you have multiple terminals open inside multiple repos.

A Branch in Your Prompt

Before continuing, are you curious to see what your current prompt really is?

In your terminal window, enter echo $PS1, and you’ll see the prompt’s definition. The default prompt for bash is ‘\s-\v\$ ’

When you installed the completion script, you also installed the bash-prompt script as well. So now it’s pretty easy to set up a custom prompt to show your repository’s active branch.

In your terminal window, enter the following. (Note that there is no space on either side of the “=” sign, and that there are two underscores at the beginning of __git_ps1.)

PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '

Now you should have a prompt that looks something like this.

[jeff.wolski@Jeff-Wolskis-MacBook-Pro git-extras (master)]$

Well, the branch is there, but this prompt is not as useful as it could be. Enter each line below (one at a time) into your terminal command line, and observe what happens to the prompt.

Start with just the branch name in the prompt using the function __git_ps1.

PS1='$(__git_ps1 "%s")'

The "%s" is a placeholder for the value returned from the function.
You need some space between the prompt and the cursor, so add a space after the function call.

PS1='$(__git_ps1 "%s") '

Now add a $ near the end.

PS1='$(__git_ps1 "%s") \$ '

How about some parenthesis around the branch name?

PS1='$(__git_ps1 "(%s)") \$ '

Some people like to see the present working directory, so add \w to the beginning of the prompt.

PS1='\w $(__git_ps1 "(%s)") \$ '

But a nicer format might be to use \W instead (it’s much less verbose).

PS1='\W $(__git_ps1 "(%s)") \$ '

Many people switch between users and like to the current one to be in the prompt. Add \u to the beginning of the prompt now.

PS1='\u \W $(__git_ps1 "(%s)") \$ '

If you appreciate a reminder of what machine you’re on, add the machine name to the prompt with \h.

PS1='\u@\h \W $(__git_ps1 "(%s)") \$ '

Perhaps you need a timestamp for each command as you look back in your terminal history? Put a date function into your prompt with:

PS1='$(date +%k:%M:%S) \W $(__git_ps1 "(%s)") \$ '

Now close the terminal window you’ve been using and then open a new one. You’ll discover the prompt you just created is no longer there!

If you create a prompt you’d like to be the default for all your terminal windows, type it into the .bashrc file in your home directory — create the file with touch .bashrc if it does not yet exist. The bash shell reads the ~\.bashrc file whenever it starts up.

Where To Go From Here

Congratulations! You’ve set up Git completion on your machine, which should save plenty of brain-power, time and typing. By completing this Git tutorial, you also learned how to create a customized prompt with the branch name included.

Play around with different options for a custom prompt until you find one that’s right for you. You can find more bash prompt options here.

Also, Homebrew is a fantastic tool you can use to install any number of other command line enhancements. Learn more about Homebrew to discover other ways it might enrich the quality of your life :].

Finally, check out How To Use Git Source Control with Xcode in iOS 7, if you haven’t already.

Thanks for working through this Git tutorial! If you have questions, comments or just want to share your discoveries, leave your notes in the comments below.

Git Tutorial – Git Fu With The Command Line is a post from: Ray Wenderlich

The post Git Tutorial – Git Fu With The Command Line appeared first on Ray Wenderlich.



Write a comment