Submitting with Git

All code must be submitted on the command line using Git, a popular revision control system.


Preliminaries

Understanding how to use Git properly is a key development skill, both in this class and in industry. Atlassian has an in-depth tutorial that serves as a quick reference for many common tasks. Here are some very useful topics that align with a lot of what you'll need in this class. To avoid frustration and save yourself headaches, you should read all modules listed here.

  • git clone - necessary for basics of setting up your submission directories
  • git add and git commit - necessary for the basic use of git and understanding revision control
  • git remote, git push, and git pull - necessary for understanding how you actually submit your code or share with a project partner
  • git branch and its sub-topics - proper use of branches and merging will significantly reduce your frustration when collaborating
  • git log - very useful tool for understanding the state of your code and how versions have changed
  • Feature Branch Workflow - useful summary of how branches, remotes, and push/pull fit together

This document illustrates the standard workflow of commands that you will need in this class. In all cases in the examples on this page, we will use the username lennonjw. You should substitute your username as appropriate. Similarly, these examples are based on setting up Lab 1 using the name lab1-ptrs. Other labs and projects will use a different name.

Recommendation: Throughout this document, there are references to using SSH to connect to student.cs.jmu.edu (known as stu for short). For this class, we recommend that you use the VS Code Remote SSH extension. After you set it up once, you will automatically connect to the stu command line whenever you pull up the terminal.

Account configuration

Before you can use Git, you must set up a configuration file in your account on stu.cs.jmu.edu. To do this, use ssh (see CS 361 Development Basics) to log into stu and run the following commands:

$ git config --global user.name "John W. Lennon"
$ git config --global user.email "lennonjw@dukes.jmu.edu"
$ git config --global receive.denycurrentbranch updateInstead

Note: You may be familiar with GitHub, a popular web-based interface for publishing code. Git and GitHub are not the same thing. One of the goals of the CS 261-361 Computer Systems sequence is to give you the opportunity to explore how systems like GitHub work. As such, we will be using Git specifically on the command-line, which will give you important experience with understanding how to use this tool more effectively.


Repository Setup

CAUTION: If you have not set up your configuration file as described above, you will NOT be be able to submit because you will not be able to push. Make sure that you have done everything there.

All labs and projects must adhere exactly to these instructions. Submissions that do not adhere to these instructions will not be graded.

As a first step, you must create a repository that is a fork of the distribution. Each lab or project will have its own distribution repository, so you will need to do this for each lab or project. It is very important that you set up your repository correctly. If you set up your repository incorrectly, you may find yourself dealing with some bizarre errors with incredibly unhelpful messages. Or you may just get a 0 because your submission could not be retrieved and graded.

To set up your repository, you must ssh to stu.cs.jmu.edu and execute the following commands. Remember that you do not type the $ (which is used to distinguish command prompt lines from sample output) and you use your e-ID instead of lennonjw. Note that other labs will have a different name instead of lab1-ptrs.git. Similarly, projects will be in projects/lennonjw instead of labs/lennonjw.

$ cd /cs/students/cs361/s24/kirkpams/labs/lennonjw
$ git clone --bare /cs/students/cs361/s24/kirkpams/src/lab1-ptrs.git
Cloning into bare repository 'lab1-ptrs.git'...
done.
$ cd lab1-ptrs.git
$ ls
branches  config  description  HEAD  hooks  info  objects  packed-refs	refs
$ git log --graph --oneline
* 1ea3136 (HEAD -> main, origin/main, origin/HEAD) Set up Spring 2024 distro

The ls and git log lines are just for reference so that you can determine it was set up correctly. Note that the 1ea3136 value is based on a SHA-1 hash value. If you are working on a different lab or the sample distribution used here has been updated, you will see a different value there.

This directory is now set up as a bare repository. You are not intended to work in this directory and you should not modify any files. The purpose of a bare repository is to create a central place that you will submit your code (using git push) and we will retrieve your submissions (using git pull).

If you are working with a partner on the projects, only one of you needs to set up this shared repository. Your directories are set up so that both e-IDs point to the same location.


Normal Workflow and Submission

Assuming your repository is set up as described above, you can set up a clone to use as a working directory. If you are working with a partner on a project, you will both create your own clones so that you have distinct workspaces. This will allow you to work on parts of the code independently without interfering with each other. However, it also means you need to learn to use git properly so that you don't find yourself frustrated with merge conflicts.

Creating a local clone

When working on any lab or project, you'll want to set up an individual workspace (called a clone in your account on stu. To keep all of your work organized, we recommend that you create a cs361 directory in your account and create all of your lab/project clones there. You can do this using the following steps, substituting your e-ID for lennonjw:

$ cd $HOME
$ mkdir cs361
$ cd cs361
$ git clone /cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git
Cloning into 'lab1-ptrs'...
done.

These steps will create a new subdirectory as $HOME/cs361/lab1-ptrs (note this directory name does not end with ".git"). You can then cd into this directory and begin working. If you then run git log --graph --oneline in that directory, you should see the same line of output as above that begins with * 1ea3136.

You will need to create a clone for each lab or project, just as you will create a separate bare repository for each. Creating the separate repositories and clones allows you to keep each assignment separate so nothing interferes with each other. Also note that you will only need to create the $HOME/cs361 directory (using mkdir above) once.

Implementing a feature as a branch

At this point, you can begin editing and submitting the code in your clone, using git commit and git push as appropriate. However, that's not the proper way to use Git. We strongly recommend that you get in the habit of implementing features as distinct branches. Doing so will really help you to organize your work and avoid nasty headaches caused by merge conflicts.

To start working on code in a branch, you'll need to both create and checkout the branch. (The git checkout command is used to switch between branches by their names.) You can do both steps in a single command:

$ git checkout -b fix-struct
Switched to a new branch 'fix-struct'
$ git branch
* fix-struct
  main

The second command here is used to show all branches (fix-struct and main) and to indicate which branch you are using with the *. You do not have to do git branch here, but you can use it to double-check that you are on the right branch.

At this point, you can start implementing the feature that you are working on, compiling and testing the code as you go. The idea is that this feature should be an independent piece of code that can be developed without interfering with other features. Once you've been working on this feature and you want to save your work, you can confirm what files you've modified:

$ git status
On branch fix-struct
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   movies.h

no changes added to commit (use "git add" and/or "git commit -a")

These messages indicate that you've changed movies.h but you haven't indicate that you want Git to save them. You do this by committing the changes with the following commands (the exact output will vary based on what you've changed). In this example, we've only changed one file, so we mention it by name on the git add line. If you've changed multiple files, you can use git add . to refer to anything that has changed in the current directory or any of its subdirectories.

$ git add movies.h 
$ git commit -m "Fixed the struct header"
[fix-struct d740d1f] Fixed the struct header
 1 file changed, 3 insertions(+)
$ git log --graph --oneline
* d740d1f (HEAD -> fix-struct) Fixed the struct header
* 1ea3136 (origin/main, origin/HEAD, main) Set up Spring 2024 distro

Notice there is a new log entry (d740d1f). Whatever you put in quotes on the git commit line becomes the log message. You should use meaningful log messages that describe the work you've done.

For many features, you may go through several steps, creating separate commits for each step. The purpose of a commit is to give you a checkpoint that you can revert to if you mess up your code. Once you've committed everything for this particular feature and you are done with it, you'll switch back to the main branch, merge the fix-struct branch into it, and delete the branch since you no longer need it:

$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
$ git merge fix-struct
Updating 1ea3136..d740d1f
Fast-forward
 movies.h | 3 +++
 1 file changed, 3 insertions(+)
$ git log --graph --oneline
* d740d1f (HEAD -> main, fix_struct) Fixed the struct header
* 1ea3136 (origin/main, origin/HEAD) Set up Spring 2024 distro
$ git branch -d fix-struct
Deleted branch fix-struct (was d740d1f).

Pushing your code for submission

Now that you've created a branch and used it to implement a feature, you will need to push these updates back to your repository. If you do not push your code, it is not submitted and will not be graded. If you're not sure whether or not you've pushed your code, you can check with the following command:

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

The second line of output indicates that you have "1 commit" that has not yet been pushed. That means you have not submitted your code for grading. To submit your code, run the following command:

$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 112 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 342 bytes | 171.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
To /cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git
   1ea3136..d740d1f  main -> main

There are two common error messages that you might see at this point. The first includes the following line:

remote: error: You can set 'receive.denyCurrentBranch' configuration variable to

If you see this message, then you did not set up your account configuration as described at the top of this document. Follow those instructions and try again. In particular, check for common typos (e.g., misspelling "recieve" instead of "receive").

Another common error starts with a line that says ! [rejected]. There are several things that could cause this, but the most common reason is that your repository contains additional commits (probably pushed by a partner) that you need to merge. Read on to the next section.

Merging without panicking

Assume that you are working with a partner (starkeri) who also tried to implement the same feature you did. They followed the same instructions for creating a branch, committing, etc. After deleting the branch, they try to push their code and see the following:

$ git log --graph --oneline
* a566075 (HEAD -> main) Added the year
* 1ea3136 (origin/main, origin/HEAD) Set up Spring 2024 distro
$ git push
To /cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to '/cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

This means there is code in the shared repository that starkeri did not have in their clone before they started working on their branch. To fix this, starkeri will need to retrieve the missing code (git pull). There are two possibilities that can happen at this point. In the simpler case, starkeri would see messages like the following:

$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 322 bytes | 24.00 KiB/s, done.
From /cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git
   1ea3136..d740d1f  main       -> origin/main
Merge made by the 'recursive' strategy.

This message means that nothing conflicted between what was in the repository and what starkeri had implemented in their branch. That is, Git confirmed that the changes from starkeri and lennonjw affected different parts of the file (or different files). When this happens, Git automatically opens a text editor asking for a commit message:

Merge branch 'main' of /cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git into main
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

Saving this file and exiting the text editor will add and commit the merged files. Make sure to push after this merge. Once starkeri pushes the merged code, lennonjw can pull the merged version and everyone is looking at the same code version again.

The more complicated case occurs when lennonjw and starkeri changed the same lines of code and Git cannot figure out which is the correct version to keep. In this case, you would see a message like the following:

$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 322 bytes | 24.00 KiB/s, done.
From /cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git
   1ea3136..d740d1f  main       -> origin/main
Auto-merging movies.h
CONFLICT (content): Merge conflict in movies.h
Automatic merge failed; fix conflicts and then commit the result.

Note: Instead of those last three lines, you may see a number of hints:

hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.


The Atlassian tutorial explains merging vs. rebasing. For our purposes in this class, we recommend you configure your code to merge:

$ git config pull.rebase false

Assuming that starkeri sees this message, they should edit and fix the files mentioned on the CONFLICT line. Opening movies.h in a text editor, they might see something like this in part of the code:

<<<<<<< HEAD
// A change from starkeri
=======
// A change from lennonjw
>>>>>>> d740d1f2baadf6f36315ad2683b1c37bee0176de

The changes made by starkeri (committed locally) are in between the <<<<<<< HEAD and ======= lines. Then the changes made by lennonjw (pulled from the shared repository) are between that and the >>>>>>> d740... line. At this point, starkeri should decide what to keep (and what to throw away) then delete the special Git format lines (the <<<<<<< HEAD, =======, and >>>>>>> d740... lines).

After doing this, the previous commit from lennonjw is discarded and lennonjw must make a new commit:

$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add ..." to mark resolution)
	both modified:   main.c

no changes added to commit (use "git add" and/or "git commit -a")
$ git add movies.h
$ git commit -m "Merged"
[main 501b8f7] Merged
$ git push
Enumerating objects: 10, done.
Counting objects: 100% (10/10), done.
Delta compression using up to 112 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 625 bytes | 156.00 KiB/s, done.
Total 6 (delta 4), reused 0 (delta 0), pack-reused 0
To /cs/students/cs361/s24/kirkpams/labs/lennonjw/lab1-ptrs.git
   d740d1f..501b8f7  main -> main

The merged code has now been pushed to the main repository. At this point, lennonjw needs to do git pull to get the merged code. To confirm that everyone is up-to-date, use git log to check that you both see the same history:

$ git log --graph --oneline
*   501b8f7 (HEAD -> main, origin/main, origin/HEAD) Merged
|\  
| * d740d1f Fixed the struct header
* | a566075 Added the year
|/  
* 1ea3136 Set up Spring 2024 distro

Final suggestions

These instructions are just a basic introduction to Git. If you need more help, please work through the tutorials mentioned at the top of this page. You can also find others by searching for "git tutorial" in your favorite Internet search engine.

Can you check if I submitted my code?

I often get asked this question (particularly when dealing with bare repositories). If you want to confirm that your code has been successfully submitted, create a new clone using git clone. The code that you see there will be exactly what I get when I go to grade your submission. You do not need me to check.



James Madison University logo


© 2011-2024 Michael S. Kirkpatrick.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.