February 20, 2012

Super Simple Introduction to Git

estimated reading time: 12 minutes and 2 seconds.

With the advent of easily installed distributed version control systems, version control should be one of the first things done in any software project of virtually any size. I believe this is particularly important for college students learning to program for two reasons. First, committing to version control systems is habit forming, and version control is an excellent habit to form. Second, students are likely to want to be able to go back to previous work since the learning process entails making many mistakes.

My goal with this post is to provide an extremely brief overview of the Git version control tool. You don’t really need that much to start using Git. There are numerous tutorials and introductions to Git available online, some of which I’ll link to here as well, but most of these go into far more detail than you really need to get working locally.

In fact, if you’re interested in a basic, albeit somewhat longer, introduction to Git, I think the best one available is part of the Pro Git book. Just start reading here and go to the end of the chapter. If you’re still on the fence, keep reading. You should see that using version control really isn’t that complicated.

Installing Git

To use Git, you have to have Git installed on your system. If you’re on Mac OS X, use the Git for Mac installer. If you’re on Windows, use the Git for Windows installer. If you’re on Linux, then you’re probably able to easily install the latest version of Git with your standard system package manager.

Creating a Repository

Let’s start out with a simple project in which you want to create a repository. This tutorial assumes you’re using Git via the command line because that will work for virtually any sort of project.

It’s probably a good idea to start by double-checking that you have installed Git correctly:

bash$ git --version
git version 1.7.6.1

If you get an error, then you’ll need to fix your installation. If you don’t, then you can setup a repository like this:

bash$ cd ~/my-project
bash$ git init
Initialized empty Git repository in /Users/masseya/my-project/.git/

Congratulations. You’ve created a git repository for your project. Pretty simple, right?

Adding and Committing Changes

An empty repository is rather boring. You may already have files in your project, but for the sake of this tutorial assume we have the following files:

bash$ ls
awesome.txt markdown.md web.html

You can see that git isn’t currently tracking any of them with this command:

bash$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add [file]..." to include in what will be committed)
#
#	awesome.txt
#	markdown.md
#	web.html
nothing added to commit but untracked files present (use "git add" to track)

Note that Git is actually telling you how to add these files using the “git add” command. Let’s go ahead and do that for all the files in our directory:

bash$ git add .

bash$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached [file]..." to unstage)
#
#	new file:   awesome.txt
#	new file:   markdown.md
#	new file:   web.html
#

Now that you’ve added these files, git will know to commit them when you execute your next commit. If you don’t commit them, then git won’t know anything about them. For example, we don’t currently have any commits in our repository. Thus, the Git command that displays the history of commits will fail like this:

bash$ git log
fatal: bad default revision 'HEAD'

To commit files, you will need to provide a commit message. Commit messages describe the changes that took place in a particular commit. They are important because they explain to the people with whom you’re collaborating how the project was put together. For now, let’s just provide a simple commit message that explains this was where we started:

bash$ git commit -m "Initial commit."
[master (root-commit) 2b5fa7f] Initial commit.
 3 files changed, 17 insertions(+), 0 deletions(-)
 create mode 100644 awesome.txt
 create mode 100644 markdown.md
 create mode 100644 web.html

Now that we have committed changes, we’ll be able to see our messages in the history using “git log”:

bash$ git log
commit 2b5fa7f801c2227dd418djkw47ed70906becfafb
Author: Aaron Massey [akmassey@example.com]
Date:   Mon Feb 20 12:34:00 2012 -0500

    Initial commit.

Let’s walk through the process of making a simple change to a file and then committing it to the repository as a way to wrap up this section:

bash$ echo "Here is a simple change." >> markdown.md                     

bash$ 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:   markdown.md
#
no changes added to commit (use "git add" and/or "git commit -a")

bash$ git add markdown.md 

bash$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD [file]..." to unstage)
#
#	modified:   markdown.md
#

bash$ git commit -m "Just a simple change."
[master 40468b7] Just a simple change.
 1 files changed, 1 insertions(+), 0 deletions(-)

bash$ git log
commit 40468b7b211f28a3fbf8dab99a17283de80af770
Author: Aaron Massey [akmassey@example.com]
Date:   Mon Feb 20 12:37:29 2012 -0500

    Just a simple change.

commit 2b5fa7f801c2227dd418djkw47ed70906becfafb
Author: Aaron Massey [akmassey@example.com]
Date:   Mon Feb 20 12:34:00 2012 -0500

    Initial commit.

At this point, you have a repository with two commits to it. You can continue making changes and committing them just like this. If at any point you want to go back to an earlier version or see the differences between the current version and the previous version, you can. Git supports those operations, but I won’t cover them in this super simple introduction. You will want to refer to some of the additional references at the end of this tutorial to see how to do these things.

Sharing Changes

You don’t have to share changes in Git to take advantage of version control. Even if all you’re doing is committing changes to a local git repository like the one we just setup, then you’ll still be able to benefit from having a repository of your work locally. This will allow you to revert to an earlier version, compare your current changes with the last committed version, or any of the other nice features version control provides you.

Still, you will likely want to share your project with someone else at some point. This is actually far easier than you might think, particularly if everyone you want to share the project with has access to the same server. Let’s start by creating a bare repository that we can store on our server:

bash$ cd ..

bash$ git clone --bare ./my-project my-project.git
Cloning into bare repository my-project.git...
done.

This creates a new directory, called my-project.git, that contains the bare repository. Now we simply need to put that directory on the server in a location that everyone in our project can access.

bash$ scp -r my-project.git user@example.com:/opt/git
config                                        100%  142     0.1KB/s   00:00    
description                                   100%   73     0.1KB/s   00:00    
HEAD                                          100%   23     0.0KB/s   00:00    
applypatch-msg.sample                         100%  452     0.4KB/s   00:00    
commit-msg.sample                             100%  896     0.9KB/s   00:00    
post-commit.sample                            100%  160     0.2KB/s   00:00    
post-receive.sample                           100%  552     0.5KB/s   00:00    
post-update.sample                            100%  189     0.2KB/s   00:01    
pre-applypatch.sample                         100%  398     0.4KB/s   00:00    
pre-commit.sample                             100% 1578     1.5KB/s   00:00    
pre-rebase.sample                             100% 4951     4.8KB/s   00:00    
prepare-commit-msg.sample                     100% 1239     1.2KB/s   00:00    
update.sample                                 100% 3611     3.5KB/s   00:00    
exclude                                       100%  240     0.2KB/s   00:00    
55ca36c127697f88eaf45fcff800cf4bee799f        100%  105     0.1KB/s   00:00    
5fa7f801c2227dd418f0df47ed70906becfafb        100%  132     0.1KB/s   00:00    
468b7b211f28a3fbf8dab99a17283de80af770        100%  168     0.2KB/s   00:00    
eb0c665faee38bbaeba503eb5a717a0baee7a0        100%  123     0.1KB/s   00:00    
6f488a0404a703f87ab10e316131752be37661        100%   46     0.0KB/s   00:00    
d953444788ec3119a2a0c8bd86757c34555bc0        100%  124     0.1KB/s   00:00    
2ded0461b3f9b4f162071dc77f1643807575b9        100%  110     0.1KB/s   00:00    
4cc438bc951fb50f16be42deac2492fab20072        100%   93     0.1KB/s   00:00    
packed-refs

Note that I’m simply using the secure file copy command to recursively transfer the bare repository we created to the /opt/git directory of the server. The /opt/git directory is in the location on our server where everyone on our project has ‘group’ level access. Once it’s been transferred, everyone should be able to clone the repository to their local machine like this:

bash$ git clone user@example.com:/opt/git/my-project.git
Cloning into my-project...
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 8 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (8/8), done.
Resolving deltas: 100% (1/1), done.

This will create our original my-project directory on our local file system. Whomever cloned the repository will have access to the history of the project:

bash$ git log
commit 40468b7b211f28a3fbf8dab99a17283de80af770
Author: Aaron Massey [akmassey@example.com]
Date:   Mon Feb 20 12:37:29 2012 -0500

    Just a simple change.

commit 2b5fa7f801c2227dd418djkw47ed70906becfafb
Author: Aaron Massey [akmassey@example.com]
Date:   Mon Feb 20 12:34:00 2012 -0500

    Initial commit.

If you’ve made changes to your project and you want to share them, then you would first add and commit those changes as described in the previous section. Once you’ve added and committed the changes you will need to push them to the repository. However, we haven’t told git about the repository yet, so we’ll need to add a remote repository like this:

bash$ git remote add origin user@example.com:/opt/git/my-project.git

This will tell our local repository that we have a remote repository we call ‘origin’ at the URL provided. Then we can push our changes to that repository as follows:

bash$ echo "Here's another change." >> awesome.txt 

bash$ 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:   awesome.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

bash$ git add awesome.txt 

bash$ git commit -m "Simple change to awesome.txt"
[master 4ea7421] Simple change to awesome.txt
 1 files changed, 1 insertions(+), 0 deletions(-)

bash$ git push origin master
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 379 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To user@example.com:/opt/git/my-project.git
   40468b7..4ea7421  master -> master

Now that there are changes on the server, your colleagues may wish to get access to them. This is accomplished with the git push command, which your colleagues would have to execute on their own machines in the location where they cloned the repository:

bash$ git log
commit 40468b7b211f28a3fbf8dab99a17283de80af770
Author: Aaron Massey [akmassey@example.com]
Date:   Mon Feb 20 12:37:29 2012 -0500

    Just a simple change.

commit 2b5fa7f801c2227dd418djkw47ed70906becfafb
Author: Aaron Massey [akmassey@example.com]
Date:   Mon Feb 20 12:34:00 2012 -0500

    Initial commit.

bash$ git pull origin master
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From example.com:/opt/git/my-project
 * branch            master     -> FETCH_HEAD
Updating 40468b7..4ea7421
Fast-forward
 awesome.txt |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

Please note that this assumes there were no conflicts in the files that were changed. If there were conflicts, they can be resolved, but that is slightly outside the scope of this super simple introduction. (If you’re actually experiencing this, perhaps the basic branching and merging described in the Pro Git book will help you.)

Standard Git Workflow

Now that you have Git installed, you’ve setup a local repository, and you’ve begun sharing changes with others, you should begin following the standard git workflow. It consists of the following basic steps:

  1. Update your local repository by pulling the latest changes from the remote.
  2. Make and commit your local changes.
  3. Once you feel you have something worth sharing, push it to the repository.

This will work flawlessly if no one else has committed changes to the repository since you began working. If someone has committed changes since you began working, then you may need to resolve those conflicts. Essentially, that means iterating over two basic steps:

  1. Pull the changes from the repository and replay your commits on top of them using the ‘git pull –rebase’ command.
  2. Git may prompt you to resolve conflicts in a particular file. Once you have them resolved you should add them using ‘git add’ and continue replaying your commits on top of the new repository using ‘git rebase –continue’.

There are many, many valid ways to use git. If you’re interested in a little more information about common workflows in Git, please read Yehuda Katz’s post on the subject.

Additional References

If you’re interested in learning more about Git, I would recommend three basic references. First, gitref.org is an excellent site to go to as a first introduction to git. It provides more detail that you’ll find here, but not nearly as much as you would find in a book-length treatment of the subject. Chances are, you’ll find what you need there. However, if you do find you need more information, my next recommendation would be the online version of Pro Git by Scott Chacon. This is an excellent resource with beautiful diagrams of the examples and tons of information. Still, if that doesn’t work for you, then I would recommend the Git Community Book, which is similarly comprehensive, and probably has what you need.

Once you have been using Git locally for a while, you’re likely to want to share code with others. There are two reasonably good alternatives: [GitHub] and [BitBucket]. I prefer GitHub to BitBucket, but the free account for BitBucket does allow you to create as many private repositories as you want. Thus, you may find it’s the only solution that’s practical for you. I wasn’t aware of this until recently, but you can get an educational account with GitHub. This will allow you to have five private repositories, which might be all you need.

Edit 29 July 2017: Both GitHub and now BitBucket allow free accounts for students and teachers. Their pricing models have changed somewhat since writing this article, but you should be able to do anything you would need for a classroom setting with either provider for free.

I hope you have found this introduction helpful. Please contact me if you have any additional questions.