CSE 451-oriented git tutorial

git is a modern version control system for managing the sharing and versioning of your source code. git is a "distributed" version control system, as opposed to classical systems such as CVS and Subversion which are based on a single central repository. While distributed version control is very powerful, this tutorial will teach you to use git in a "client-server" fashion that mimics the classical version control systems that you may already be used to.

This tutorial will show you how to set up a repository for project 1, import the Linux kernel code, and share the repository amongst your group members. This tutorial assumes that you are using Linux, and assumes that you will create your git repository in the shared project directory that has been created for your group. You can generalize these instructions to create a repository in another location (e.g. for your private use on other CSE assignments). If you want to use Windows to access a git repository, I've had success using Git for Windows. You can reuse the same repository for all of your CSE 451 projects, or create a separate repository for each project.

Tip

git has many, many more advanced features than are presented in this tutorial. If you'd like to learn about them, git has a comprehensive set of man pages (man 1 git or git --help <command>), as well as the online Git Community Book. Try these references first if you get unexpected output or error messages from git commands.

Repository setup

The following steps should first be performed by one member of your group:

  1. Log in to a CSE server that has access to the course project directories, such as one of the CSE Linux lab machines, forkbomb, or attu. git should already be installed on these servers.

    Important

    You can use attu to create your repository, but remember that you should not work on project 1 on attu; use forkbomb instead.

  2. cd to your group's project directory, e.g.:

    $ cd /projects/instr/12sp/cse451/X
    

    (replace X with your group's letter.)

  3. Run this exact git command to initialize an empty shared repository called project1-git:

    $ git init --shared=group --bare project1-git
    

    You should see this output:

    Initialized empty shared Git repository in /projects/instr/12sp/cse451/X/project1-git/
    

Important

At this point, you should not directly change anything located in your central repository directory (/projects/instr/12sp/cse451/X/project1-git/); this directory contains all of the important git metadata as well as your code (buried somewhere), but should not be modified directly. You must clone the repository elsewhere and make changes using the normal git commands (see the following sections).

Importing the Linux kernel code

It is probably easiest to have one member of your group perform these steps before others access the repository. Run these commands in a directory on your CSE 451 VM, CSE Home VM, Linux lab machine, or other machine where you will perform your development for project 1.

  1. cd to a directory where you will perform your development. For this tutorial, we'll use this example directory:

    $ mkdir ~/cse451
    $ cd ~/cse451/
    
  2. Run this git command to "clone" the repository (remember to replace X with your group letter). This is equivalent to creating a local "working copy" in other version control systems:

    $ git clone <username>@attu.cs.washington.edu:/projects/instr/12sp/cse451/X/project1-git/
    

    Note

    Because no commits have been made to your repository yet, you should get this message as output from the clone command: "warning: You appear to have cloned an empty repository." This is normal.

  3. Copy the Linux kernel code (as described in the project 1 description) and extract it in a directory in your repository (the last command may take a few minutes):

    $ cd ~/cse451/project1-git/
    $ cp /cse/courses/cse451/12sp/linux-2.6.38.2-CSE451.tar.gz .
    $ tar xzf linux-2.6.38.2-CSE451.tar.gz
    
  4. You should now have a directory linux-2.6.38.2/ in your repository directory. Run the git status command and confirm that your output is the same:

    $ git status
    # On branch master
    #
    # Initial commit
    #
    # Untracked files:
    #   (use "git add <file>..." to include in what will be committed)
    #
    #       linux-2.6.38.2-CSE451.tar.gz
    #       linux-2.6.38.2/
    nothing added to commit but untracked files present (use "git add" to track)
    
  5. Add the source code to your repository, commit it, and push the commit to the central repository (unlike most classical version control systems, git separates the notion of a "commit" from a "push" / "pull" to/from the repository). These commands will take several minutes to complete:

    Warning

    You must run these commands before compiling the kernel source code! Otherwise, you will commit object files and other products of the build, which will take up unnecsssary space in your repository and will cause headaches when different members of your team are working in their local repositories.

    $ git add linux-2.6.38.2
    
    $ git commit -m "Initial commit of linux-2.6.38.2 source code."
    [master (root-commit) b482242] Initial commit of linux-2.6.38.2 source code.
    create mode 100644 linux-2.6.38.2/... <many lines of output>
    
    $ git push --mirror
    <enter your password>
    Counting objects: 24584, done.
    Delta compression using up to 2 threads.
    Compressing objects: 100% (24289/24289), done.
    Writing objects: 100% (24584/24584), 86.44 MiB | 1.63 MiB/s, done.
    Total 24584 (delta 1033), reused 0 (delta 0)
    To <username>@attu.cs.washington.edu:/projects/instr/12sp/cse451/X/project1-git/
    * [new branch]      master -> master
    

    Note

    The --mirror flag is needed for the initial git push to tell git which local branches (in your cloned repository) should be pushed to which remote branches (in the central repository). "Mirroring" the branches just tells git to match the local "master" branch to the remote "master" branch. Ease-of-branching is one of the best features of git; for more information (not really needed for CSE 451), see the Git Community Book online.

Also, you can now delete the linux-2.6.38.2-CSE451.tar.gz file from your current directory.

Cloning the repository

Now, every other member of your group should follow these steps to check out a copy of the repository (the person that performed the steps in the previous section can skip this section). Run these commands in a directory on forkbomb, your personal machine, or other machine where you will perform your development for project 1.

Note

Unfortunately the CSE 451 VM is configured to use host-only networking, so you will not be able to directly access your repository via ssh from inside of the VM. If you want to use the 451 VM for building the kernel, you could clone the repository on your local machine (where the VM is running) and then copy back-and-forth from the local machine into the VM (rsync(1) may help with this). Or, just clone your repository on forkbomb and only copy the built kernel to the 451 VM (as is recommended in the project 1 description). If you really know what you are doing and understand the security risks, you can also change the networking configuration of the 451 VM, but this is not recommended.

  1. cd to a directory where you will perform your development. For this tutorial, we'll use this example directory:

    $ mkdir ~/cse451
    $ cd ~/cse451/
    
  2. Run this git command to "clone" the repository (remember to replace X with your group letter). This is equivalent to creating a local "working copy" in other version control systems:

    $ git clone <username>@attu.cs.washington.edu:/projects/instr/12sp/cse451/X/project1-git/
    

    This command will create a project1-git/ directory that contains your local working copy of the repository. It will take several minutes for the clone to complete as it checks out the entire Linux source code tree that was committed in the previous section.

Once this process is complete, you are ready to work on project 1; create new directories in the top-level of your repository to work on the shell and other code (e.g. project1-git/shell/), or work inside of the project1-git/linux-2.6.38.2/ directory for the kernel parts of the project. See the following section for commands to commit your changes to the repository so that the rest of your group can see them.

Committing to the repository

The commit and update process in git differs slightly from that of other version control systems that you may have used. To illustrate how it works, we'll make a minor change to one file and create a new file. Run the following commands from the top level of your repository (i.e. ~/cse451/project1-git/):

$ echo Hello >> linux-2.6.38.2/README
$ echo newfile >> newfile

Now run the git status command and observe its output:

$ 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:   linux-2.6.38.2/README
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       newfile
no changes added to commit (use "git add" and/or "git commit -a")

This output shows two things: first, you must explicitly add new ("untracked") files to the repository before they can be committed (as is true in other version control systems), and second, you must explicitly stage your changes before they will be committed (unlike subversion, which will automatically commit all of your changes if you just run svn commit). To further illustrate this, run the git commit command:

$ git commit -m "Test commit."

This command will be a no-op: you should see the same output as from the git status command, ending in the same message: 'no changes added to commit (use "git add" and/or "git commit -a")'. To add your changes to the commit set, you must use the git add command on the changed files (whether they are new files or files already in the repository!):

$ git add linux-2.6.38.2/README
$ git add newfile
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   linux-2.6.38.2/README
#       new file:   newfile

You can see that the change to the README file and the addition of newfile have now been staged for the commit. To commit all of the changes that you have staged, run git commit:

$ git commit -m "Test commit"
[master 41a5c0d] Test commit
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 newfile

Details

The -m argument to git commit is used to provide a commit message; if you forget to use it, then git will open a text editor for you to enter a commit message.

To make git behave more like subversion and automatically commit all of the changes you have made to files already in the repository (automatically commit all unstaged changes), use the -a flag with git commit. You may find, however, that the process of staging / unstaging changes is one of the most useful features of git: it allows you to make lots of changes to lots of files, but only commit the changes you want while leaving other files with in-progress changes uncommitted.

If you get a message like "file.c: needs merge" when you try to commit, then you have a conflict from a previous git pull that has not been resolved. See the Updating your local repository section below for more information about resolving conflicts.

However, at this point you have committed some changes, but your commit is not yet visible to the other users of the repository. git separates the notion of committing changes from the notion of "pushing" your update to the central repository. This can be useful for an individual developer that wants to make many changes to the code in many small logical commits before pushing all of them to the central repository, but for your CSE 451 projects you will probably just want to push after every commit.

To push your commits to the central repository, simply run git push:

$ git push
<enter password>
...
To <username>@attu.cs.washington.edu:/projects/instr/12sp/cse451/X/project1-git/
   b482242..41a5c0d  master -> master

Note

If your push fails, it may be because you need to run a git pull to update your repository first (and potentially resolve conflicts) before pushing your new commit. See Updating your local repository below.

Details

If you ever run the git status command and see the output "Your branch is ahead of 'origin/master' by N commits", then this means that you have local commits that have not been pushed to the repository yet.

Reminder

Remember to push your commits! If you are not used to using git, then this may be the hardest thing to get used to. Try not to forget to do this, or your group members will wonder why it seems like you're not doing any work.

Updating your local repository

To update your local copy of the repository with commits that other members of your group have made, simply use the git pull command:

$ git pull
<enter password>
...
linux-2.6.38.2/README |    1 +
newfile               |    1 +
2 files changed, 2 insertions(+), 0 deletions(-)
create mode 100644 newfile

Note

git will not allow you to pull updates into your repository while you have unstaged changes. Therefore, you must stage (git add) and commit all of your changes before pulling. If you have changes that you don't want to make visible to your group yet (because they cause the build to break, for instance) but you still want to update your local repository, you can just commit your changes, do not push them yet, and pull the updates. Then, when your changes are ready, you can commit again and then push to the central repository.

As with any version control system, pulling updates from the central repository may cause conflicts with changes that you have committed locally. Pay attention to the output from your pull command for instructions to resolve conflicts; generally, resolving a conflict entails opening up the conflicted file(s) in your editor, finding the conflicts (surrounded by the text markers <<<<<<<, =======, and >>>>>>>), fixing the conflicting code, staging the changes (git add) and committing and pushing again. For more information about resolving conflicts, the github user manual has a useful webpage.


If you find any bugs in this tutorial, please e-mail them to the CSE 451 TAs. If you have any problems while using git that are not covered in this tutorial, try the man pages (man 1 git and git --help <command>) and the Git Community Book, and then contact the TAs. If you find any useful features of git that aren't mentioned here, post them to the class discussion board.

Author: Peter Hornyack (pjh@cs)