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/s25/kirkpams/labs/lennonjw
$ git clone --bare /cs/students/cs361/s25/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/s25/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/s25/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/s25/kirkpams/labs/lennonjw/lab1-ptrs.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to '/cs/students/cs361/s25/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/s25/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/s25/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/s25/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/s25/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