[FrontPage] [TitleIndex] [WordIndex

Note: You are looking at a static copy of the former PineWiki site, used for class notes by James Aspnes from 2003 to 2012. Many mathematical formulas are broken, and there are likely to be other bugs as well. These will most likely not be fixed. You may be able to find more up-to-date versions of some of these notes at http://www.cs.yale.edu/homes/aspnes/#classes.

Git has a number of very complicated features designed to allow large teams of programmers to work together on a project without clobbering each other's code, but the main thing that it does is allow you to keep around a history of every significant change that you have made to your files.

1. Setting up Git

Typically you run git inside a directory that holds some project you are working on (say, hw1). Before you can do anything with git, you will need to create the repository, which is a hidden directory .git that records changes to your files:

ja54@tick:~/cs223> mkdir git-demo
ja54@tick:~/cs223> cd git-demo
ja54@tick:~/cs223/git-demo> git init
Initialized empty Git repository in /home/classes/cs223/class/aspnes.james.ja54/git-demo/.git/

Now let's create a file and add it to the repository:

ja54@tick:~/cs223/git-demo> echo 'int main(int argc, char **argv) { return 0; }' > tiny.c
ja54@tick:~/cs223/git-demo> git add tiny.c

The git status command will tell us that Git knows about tiny.c, but hasn't commited the changes to the repository yet:

ja54@tick:~/cs223/git-demo> git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   tiny.c
#

The git commit command will commit the actual changes, along with a message saying what you did. For short messages, the easiest way to do this is to include the message on the command line:

ja54@tick:~/cs223/git-demo> git commit -a -m"add very short C program"
[master (root-commit) 5393616] add very short C program
 Committer: James Aspnes <ja54@tick.zoo.cs.yale.edu>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:

    git config --global user.name "Your Name"
    git config --global user.email you@example.com

If the identity used for this commit is wrong, you can fix it with:

    git commit --amend --author='Your Name <you@example.com>'

 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 tiny.c

The -a argument tells Git to include any changes I made to files it already knows about. The -m argument sets the commit message.

Because this is the first time I ever did a commit, and because I didn't tell Git who I was before, it complains that its guess for my name and email address may be wrong. It also tells me what to do to get it to shut up about this next time:

ja54@tick:~/cs223/git-demo> git config --global user.name "James Aspnes"
ja54@tick:~/cs223/git-demo> git config --global user.email "aspnes@cs.yale.edu"
ja54@tick:~/cs223/git-demo> git commit --amend --author="James Aspnes <aspnes@cs.yale.edu>" -m"add a very short C program"
[master a44e1e1] add a very short C program
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 tiny.c

Note that I repeated the -m business to git commit --amend; if I hadn't, it would have run the default editor (vim) to let me edit my commit message. If I don't like vim, I can change the default using git config --global core.editor, e.g.:

ja54@tick:~/cs223/git-demo> git config --global core.editor "emacs -nw"

I can see what commits I've done so far using git log:

ja54@tick:~/cs223/git-demo> git log
commit a44e1e195de4ce785cd95cae3b93c817d598a9ee
Author: James Aspnes <aspnes@cs.yale.edu>
Date:   Thu Dec 29 20:21:21 2011 -0500

    add a very short C program

2. Editing files

Suppose I edit tiny.c using my favorite editor to turn it into the classic hello-world program:

   1 #include <stdio.h>
   2 
   3 int 
   4 main(int argc, char **argv)
   5 {
   6     puts("hello, world");
   7     return 0;
   8 }

I can see what files have changed using git status:

ja54@tick:~/cs223/git-demo> git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   tiny.c
#
no changes added to commit (use "git add" and/or "git commit -a")

Notice how Git reminds me to use git commit -a to include these changes i n my next commit. I can also do git add tiny.c if I just want include the changes to tiny.c (maybe I made changes to a different file that I want to commit separately), but usually that's too much work.

If I want to know the details of the changes since my last commit, I can do git diff:

ja54@tick:~/cs223/git-demo> git diff
diff --git a/tiny.c b/tiny.c
index 0314ff1..f8d9dcd 100644
--- a/tiny.c
+++ b/tiny.c
@@ -1 +1,8 @@
-int main(int argc, char **argv) { return 0; }
+#include <stdio.h>
+
+int 
+main(int argc, char **argv)
+{
+    puts("hello, world");
+    return 0;
+}

Since I like these changes, I do a commit:

ja54@tick:~/cs223/git-demo> git commit -a -m"expand previous program to hello world"
[master 13a73be] expand previous program to hello world
 1 files changed, 8 insertions(+), 1 deletions(-)

Now there are two commits in my log:

ja54@tick:~/cs223/git-demo> git log | tee /dev/null
commit 13a73bedd3a48c173898d1afec05bd6fa0d7079a
Author: James Aspnes <aspnes@cs.yale.edu>
Date:   Thu Dec 29 20:34:06 2011 -0500

    expand previous program to hello world

commit a44e1e195de4ce785cd95cae3b93c817d598a9ee
Author: James Aspnes <aspnes@cs.yale.edu>
Date:   Thu Dec 29 20:21:21 2011 -0500

    add a very short C program

3. Renaming files

You can rename a file with git mv. This is just like regular mv, except that it tells Git what you are doing.

ja54@tick:~/cs223/git-demo> git mv tiny.c hello.c
ja54@tick:~/cs223/git-demo> git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       renamed:    tiny.c -> hello.c
#

These changes don't get written to the repository unless you do another git commit:

ja54@tick:~/cs223/git-demo> git commit -a -m"give better name to hello program"
[master 6d2116c] give better name to hello program
 1 files changed, 0 insertions(+), 0 deletions(-)
 rename tiny.c => hello.c (100%)

4. Adding and removing files

To add a file, create it and then call git add:

ja54@tick:~/cs223/git-demo> cp hello.c goodbye.c
ja54@tick:~/cs223/git-demo> git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       goodbye.c
nothing added to commit but untracked files present (use "git add" to track)
ja54@tick:~/cs223/git-demo> git add goodbye.c
ja54@tick:~/cs223/git-demo> git commit -a -m"we need a second program to say goodbye"
[master 454b24c] we need a second program to say goodbye
 1 files changed, 8 insertions(+), 0 deletions(-)
 create mode 100644 goodbye.c

To remove a file, just delete it before doing the commit:

ja54@tick:~/cs223/git-demo> rm goodbye.c 
ja54@tick:~/cs223/git-demo> git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    goodbye.c
#
no changes added to commit (use "git add" and/or "git commit -a")
ja54@tick:~/cs223/git-demo> git commit -a -m"no, goodbye.c was a bad idea"
[master defa0e0] no, goodbye.c was a bad idea
 1 files changed, 0 insertions(+), 8 deletions(-)
 delete mode 100644 goodbye.c

5. Recovering files from the repository

If you make a mistake, you can back out using the repository. Here I will delete my hello.c file and then get it back using git checkout -- hello.c:

ja54@tick:~/cs223/git-demo> rm hello.c
ja54@tick:~/cs223/git-demo> ls
ja54@tick:~/cs223/git-demo> git checkout -- hello.c
ja54@tick:~/cs223/git-demo> ls
hello.c

I can also get back old versions of files by putting the commit id before the --:

ja54@tick:~/cs223/git-demo> git checkout a44e1 -- tiny.c
ja54@tick:~/cs223/git-demo> ls
hello.c  tiny.c

The commit id can be any unique prefix of the ridiculously long hex name shown by git log.

Having recovered tiny.c, I will keep it around by adding it to a new commit:

ja54@tick:~/cs223/git-demo> git commit -a -m"keep tiny.c around"
[master 23d6219] keep tiny.c around
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 tiny.c

6. Undoing bad commits

Suppose I commit a change that I didn't want to make. For example, let's suppose I decide to add some punctuation to the greeting in hello.c but botch my edit:

ja54@tick:~/cs223/git-demo> vim hello.c
ja54@tick:~/cs223/git-demo> git commit -a -m"add exclamation point"
[master f40d8d3] add exclamation point
 1 files changed, 1 insertions(+), 1 deletions(-)

Only now does it occur to me to test my program:

ja54@tick:~/cs223/git-demo> gcc -o hello hello.c
ja54@tick:~/cs223/git-demo> ./hello
hello, wolrd!

Disaster!

I can use git diff to see what went wrong. The command below compares the current working directory to HEAD^, the commit before the most recent commit:1

ja54@tick:~/cs223/git-demo> git diff HEAD^ | tee /dev/null
diff --git a/hello.c b/hello.c
index f8d9dcd..dc227a8 100644
--- a/hello.c
+++ b/hello.c
@@ -3,6 +3,6 @@
 int 
 main(int argc, char **argv)
 {
-    puts("hello, world");
+    puts("hello, wolrd!");
     return 0;
 }

And I see my mistake leaping out at me on the new line I added (which git diff puts a + in front of). But now what do I do? I already commited the change, which means that I can't get it out of the history.2

Instead, I use git revert on HEAD, the most recent commit:

ja54@tick:~/cs223/git-demo> git revert HEAD
[master fca3166] Revert "add exclamation point"
 1 files changed, 1 insertions(+), 1 deletions(-)

(Not shown here is where it popped up a vim session to let me edit the commit message; I just hit :x<ENTER> to get out of it without changing the default.)

Now everything is back to the way it was before the bad commit:

ja54@tick:~/cs223/git-demo> ./hello
hello, world

7. Looking at old versions

Running git log will now show me the entire history of my project:

fca3166a697c6d72fb9e8aec913bb8e36fb5fe4e Revert "add exclamation point"
f40d8d386890103abacd0bf4142ecad62eed5aeb add exclamation point
23d6219c9380ba03d9be0672f0a7b25d18417731 keep tiny.c around
defa0e0430293ca910f077d5dd19fccc47ab0521 no, goodbye.c was a bad idea
454b24c307121b5a597375a99a37a825b0dc7e81 we need a second program to say goodbye
6d2116c4c72a6ff92b8b276eb88ddb556d1b8fdd give better name to hello program
13a73bedd3a48c173898d1afec05bd6fa0d7079a expand previous program to hello world
a44e1e195de4ce785cd95cae3b93c817d598a9ee add a very short C program

If I want to look at an old version (say, after I created goodbye.c), I can go back to it using git checkout:

ja54@tick:~/cs223/git-demo> git checkout 454b2
Note: checking out '454b2'.

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 454b24c... we need a second program to say goodbye
ja54@tick:~/cs223/git-demo> ls
goodbye.c  hello  hello.c

Now I have both goodbye.c and hello.c, as well as my compiled program hello, since I didn't tell Git about it. Note that I also got lots of horrendous warnings about the fact that I am living in the past and shouldn't expect to make any permanent changes here.

To go back to the last commit, use git checkout master:

ja54@tick:~/cs223/git-demo> git checkout master
Previous HEAD position was 454b24c... we need a second program to say goodbye
Switched to branch 'master'
ja54@tick:~/cs223/git-demo> ls
hello  hello.c  tiny.c

8. More information about Git

All Git commands take a --help argument that brings up their manual page. There is also extensive documentation at http://git-scm.org.


CategoryProgrammingNotes

  1. The pattern here is that HEAD is the most recent commit, HEAD^ the one before it, HEAD^^ the one before that, and so on. This is sometimes nicer than having to pull hex gibberish out of the output of git log. (1)

  2. Technically I can using git reset, but git reset can be dangerous, since it throws away information. (2)


2014-06-17 11:58