CSE 373, Summer 2019: Git intro and submitting

Table of Contents

  1. Submission process

  2. Git preface

  3. How does Git work?

  4. Committing changes

  5. Pushing changes

  6. GitLab runners

  7. GitLab commit history

  8. Pulling changes

  9. Other features

Submission process

Code for all projects will be submitted to GitLab via commiting and pushing; the steps to do so are described below. There's no need to do anything else if you're submitting on time, but if you're submitting late, you must fill out the late submission form to tell us the number of late days you'll be using. If you do not, we will assume that your submission is on time and grade according, and you will not be able to change this after we've finished grading

For groups with more than one person, only one person needs to fill out the form, and a late day will be deducted from each member. Also, submitting an individual write-up late does not require filling out this form, and will count against only the members who submit late on Canvas.

A note about branches: make sure you push to the master branch, as that is the one we will grade.

Git preface

We mentioned before that Git is very flexible; this has a couple important implications:

  1. We won't be able to cover everything you can do with Git. Git has many features, and they can be combined in many different workflows; for simplicity, we'll be describing only the bare minimum set of features necessary to get started with Git. You may use other Git functionality, but we will not be providing guides for those. You can check out the official Git documentation for more information or search for other resources. (GitHub, GitLab, and Atlassian tend to have some pretty nice stuff, since they all have repo hosting services.)

  2. You should be careful when using Git. Git is a powerful tool, and some of the more powerful functionality may destroy your local repository if used incorrectly. None of the functionality we go over should allow you to do this, but you should definitely understand what you're doing before running random Git commands you find on the internet.

Fortunately, GitLab should prevent you from destroying the remote repo, so in the worst case, you can just download the code from the remote repo again. You can also come in to office hours for help with Git. (The discussion board tends not to work too well for debugging issues with Git though.)

How does Git work?

You may have noticed that after you clone a repository using Git, the resulting directory on your machine contains the current version of the repo's files, but with an extra .git directory inside. (If you're using Mac or Linux, this folder may be directory by default.) This .git directory contains all the extra data Git stores, such as the history of changes—in fact, this directory is the actual local repository, a local version of what's stored on GitLab. Meanwhile, we refer to the directory containing this directory and the other, regular files as the working directory. (However, typically, whenever we refer verbally to the directory or path of a Git repository on your machine, we mean the working directory and not the .git directory inside; this is a minor inconsistency in terminology, so do ask if it's not clear which we're referring to.)

This means that there are really two versions of code on your machine: the working directory will contain the copy of the code that you're actively working on as real files, whereas the local repository will store the entire history of the project as a series of changes between sequential versions.

Regular Git usage involves syncing changes between the working directory and the local repository, and between the local repository and the remote repository. Syncing changes in Git always works in a single direction at a time: new changes get copied from one place to another. Here's a diagram with the names for basic commands that move changes in each direction:

As you can see, there are three main commands:

  • Committing adds changes from the working directory to the local repo.
  • Pushing copies changes from the local repo to the remote repo.
  • Pulling is a little more complicated: it first copies changes from the remote repo to the local repo, then applies those changes to the working directory.

Committing changes

Committing is the main way that changes enter Git repos (and the only way in this basic workflow) since pushing and pulling involve moving changes between repos or from the local repo out and into the working directory. Whenever you commit, Git compares the working directory to the local repo and adds the set of changes to the local repo. This set of changes is referred to as a commit; at the same time, since a commit also stores a reference to the previous/parent commit (kind of like a linked list), we can trace the commits all the way back to the beginning of the commit history; in essence, this means that a commit also specifies a particular version of code.

Now, referring to commits by their full list of changes is pretty awkward, and referring to them by the contents of all files is even worse, so Git lets us assign a commit message when we're committing to briefly describe the changes made. Also, internally, Git generates a commit hash for each commit—a string of hexadecimal characters that acts as an ID for the commit. The commit hash is usually not very useful for humans, but in this course, we include it in our project feedback to indicate which commit we graded.

There's actually a little bit more to committing than this: Git allows us to choose exactly which changes in the working directory to commit, which involves keeping another version of code between the working directory and the local repo; however, we'll be using a GUI that manages that for us, so we'll ignore it.

Before we begin, note that IntelliJ marks local changes right in the editor:

The colors to the right of the line numbers and @ symbol denote changes in your working directory (compared to your local repository). The green highlight marks an added line, the gray right-pointing triangle marks removed lines, and the blue highlight marks a changed line.

These markers are very useful for checking that you haven't accidentally modified code that you were not supposed to modify. You can also list all changed files by opening the "Version Control" tool window and selecting the "Local Changes" tab:

(If you're using the single-project workflow, you may want to use the "Group By:" selector in the left-side toolbar of that tool window to group changes by module.)

It may be useful to show the diff pane in this tool window, which will show a side-by-side comparison of the file before and after your local changes. (The button for this is in the left-side toolbar of the tool window, at the very bottom—it's actually hidden in the overflow menu in the screenshot below.)

To commit changes:

  1. Open the "Version Control" tool window and select the "Local Changes" tab. Then, click the green check mark button on the left-side toolbar in that tool window.

  2. A "Commit Changes" window should open. In this window, select the files you wish to commit and enter a commit message to describe your changes. This window also includes a diff viewer that allows you to view local changes.

  3. At this point, you may want to review the "Before Commit" options in this window. These options control what automatic checks and changes IntelliJ will make before committing your code. The relevant options for our class are enabled in the image below.

    You aren't required to enable any of these, but...

    • We strongly recommend enabling at least the Checkstyle scan.
    • The "Reformat code" can also be useful for ensuring that your code conforms to our style, but could also ensure that your code doesn't conform to our style if your IDE is configured incorrectly.
    • The "Perform code analysis" option will also run Checkstyle, but it will group those errors with any others in the project, so it's easy to accidentally ignore them. (Also, this option is just annoying in general since it will pop up a dialogue box if there are any warnings in the code.
    • The "Check TODO (Show All)" option may help make sure there aren't any TODO's left in code.
  4. When you're done, click the "Commit" button in the bottom right to finish committing. If you want to commit and push at the same time (which is reasonable for small projects like ours, but is less useful in large projects), you can also use the dropdown arrow and choose "Commit and Push" instead.

Pushing changes

Pushing is relatively simple, since it just involves copying commits from the local repo to the remote repo.

  1. Click "VCS" > "Git" > "Push..."

  2. The window that opens will show a preview of all local commits that you will push to, the remote repository. In this window, click "Push".

Once you've pushed your code, you can check GitLab with the following tools to make sure it's been submitted successfully (it should show up on GitLab). You may push (basically submit) any amount of times while the assignment turnin time has not yet passed. To read more about the grading and feedback, check out the next section of this homework.

Warning: if you want that code to be submitted, make sure you've pushed to master and not some other branch!

Gitlab runners

After the first few days the project has been released, the GitLab repos will have runners set up to automatically check that the code in your repository compiles and passes checkstyle and that your tests pass. If everything is fine and all the tests pass, you'll get a green ✓ on your repo homepage; otherwise, you'll get a red ×. In general, should probably check the GitLab runner status after you push code and make sure the results meet your expectations. Check out this gif for a demo of how to find the runner's output in this case to see what went wrong.

When you submit, there will be at least 2 runner jobs. The first job is very important - it checks two things: that your code compiles and that it compiles for grading, in particular. If your submission does not pass 'compile' or 'compile' for 'compile for grading' - we cannot grade your assignment and you will receive a 0 by default. So, be sure that when you make your submissions you pass 'compile' and 'compile for grading'. The second runner job is often just using the unit tests we gave you to run locally, but ran on the GitLab machines. This should give you the same results as when you run the tests locally, and is just another sanity check.

Note that the runners will not be used during grading. They serve mainly as a sanity check (to make sure your code is pushed to GitLab properly) and as a benchmark for efficiency tests (the machine we use for grading should be more powerful than the ones the runners use, so you should aim to make your efficiency tests pass on the runners).

Additionally, the runner machines are shared with other courses, so we cannot guarantee that the test results are always accurate. In general, they tend to run slower towards deadlines, when everyone is pushing their code, so we recommend that you start early if you wish to make full use of the runners. This also means that you should NOT depend on the runners instead of running tests or Checkstyle locally.

If functionality tests sometimes fail on the runners, but consistently pass locally (and you've correctly pushed all your code to the runners), it's safe to assume that your code is fine and that the runners just are running slowly. However, if efficiency tests sometimes fail on the runners, you may want to consider optimizing your code.

Also, the runners will only check your code if you push a commit that changes your code; changes to other files will not trigger the runners, which means you will not see a ✓ or ×.

WARNING: the course staff does their best to maintain the correctness of what the GitLab runners are checking for, but cannot guarantee anything about the behavior of the runners. The safest way to think of the the GitLab runners is as a sanity check that nothing has gone horribly wrong in student code. Even if the GitLab runners pass, it is still possible there are still things broken in the student code. Please run Checkstyle locally, run the tests locally, and don't use the runners as an end-all-be-all guide. They're just a small sanity check.

GitLab commit history

Git is a version control system, so it stores versions of your code and lets you control them. So, one thing you can do is view these old versions, by commit, and potentially revert back to them. This might be useful if you introduced a new bug and can't figure out what it is, or just want to figure out when your code was in a particular code. To see your commit history on GitLab, go to the repository whose history you want to view. Then, click on the Commits tab (circled in red in the screenshot below)

You will be brought to a page that shows a list of your commit history; your most recent commits are at the top and your furthest back commits are at the bottom. Here you can access some useful information: going from the red circles from left to right:

  • Clicking on the commit name: will bring you to a page that shows the differences between this commit and the previous. Basically, it'll show you the new things that were added/deleted/changed in this specific commit.
  • Commit Hash: this is basically an ID for each commit. When you get your feedback on projects, we will say at the top which commit was graded.
  • Clicking on the folder ('Browse Files'): will bring you to a folder explorer view where you can click through all your files as they were at that commit.

If you want to revert a file or multiple files back to an old commit version, one way you can do this is just by copy pasting the file text from GitLab into your file locally. This is probably the easiest for some, but you can also do this through IntelliJ (single file, entire working directory) and through the command line (if you want to use the command line you can probably do the googling yourself :^) ).

Pulling changes

Pulling is fairly simple for the same reason that pushing is: usually, it just copies new commits from the remote repo to the local repo, then applies the changes from those commits to the working directory.

  1. Click "VCS" > "Git" > "Pull..."

  2. In the window that opens, click "Pull".

    If you're using the single-project workflow and you already have multiple modules in your project, you may need to first select the proper "Git Root" (i.e., the directory containing the assignment repository).

Other features

Again, there are many other Git features not covered here for the sake of brevity. You can see the IntelliJ documentation for more details.

One particularly nice feature is the Log tab in the Version Control tool window, which displays a visual representation of the commit history in your Git repositories: see this page for an overview or this page for the full documentation

There's also a bit more to learn about Git once we start working on partner projects, but we'll leave that information for later.

Note: be careful when you're playing around with IntelliJ's Git features, since Git allows you to completely mess up your local repository, and if you accidentally you push your changes to GitLab, the remote repository as well. In general, if you're not sure what something does and it doesn't seem to be a visual/display option, you should consult IntelliJ/Git documentation before trying it.)