Version Control Reference

Contents:

Introduction

CSE 331 uses git to distribute starter code and to turn in assignments. At the deadline, the staff collects a tagged current version of your files from your repository.

A note about terminology: git is a version control system that lets software engineers backup, manage, and collaborate on software projects. GitLab is a hosting service — a place to store git repositories for students in CSE 331.

Git stores the history of all versions of your files in a “repository.” Each user “clones” the repository, making a local copy of it. Cloning also creates a “working copy” of the latest version of the files. Each user can edit his or her working copy, without affecting other users or the master version.

Please read "Version control concepts and best practices." Even people who have used git before often find that it clarifies concepts.

Why use version control?

Every serious software project uses version control — even single-person projects. CSE 331 gives you practice with version control.

All version control systems, including git, provide the following functionality:

Over many years of teaching courses like CSE331, we have observed that on average about one student's computer will either crash unrecoverably or be stolen during the quarter. Therefore, you should commit and push your work to the repository often. Committing often:

Pulling to Update Files

Git's "pull" command updates your local copy of files to reflect changes made to the remote repository by other people (or by you when working on a different computer system). The only changes made by people other than you will be made by the CSE 331 staff when we are adding new homeworks to your repositories. If you work at home and at UW CSE, you will need to use commit, push, and pull to propagate your changes between the two locations.

Git usually does a good job of merging changes made to multiple working copies (say, by different people or by you on your home computer and you at UW CSE), even if those changes are to different parts of the same file. However, if both people change the same line of a file, then git cannot decide which version should take precedence. In this case, git will signal a conflict during git pull, and you must resolve the conflict manually which can be an unpleasant task.

To minimize the possibility of conflicting changes being made simultaneously, you should pull frequently and commit/push frequently. ALWAYS pull before you make any commits.

To pull from GitLab in IntelliJ, use the Git » Pull menu. Do not change any of the settings from the defaults in this window.

Committing Changes

After making changes to, adding, or removing files, you must “commit” your changes to git. This step will cause git to record your changes to the repository, so that your changes are backed-up and available to other people working on the repository, or to you when working on a different computer system. It is a good idea to commit your changes frequently. It backs up your work, thus enabling you to revert to an earlier version of your code if you find yourself going down a wrong path. Also, when you are working with others, it minimizes conflicts.

In general, you should “pull” any new files or changes before committing your latest code. (And, if the pull results in any conflicts, you should resolve them before committing.)

To commit your changes, use the Git » Commit menu in IntelliJ. In the commit window, make sure you always do the following:

  1. In the top area, check the boxes next to all files you want to include in your commit. You need to double-check this every time you commit, or you might risk forgetting to include something in your homework submission.
  2. Enter a commit message. A good commit message is short (<15 words usually) and describes briefly what changes you made since your last commit. For example: "Completed part 1 of the assignment."
  3. Uncheck all the options in "Before Commit" within the gear icon window - they're unnecessary and can get in the way of a successful commit.
  4. You can see all the changes that will be included in this commit at the bottom. It is good practice to review these and make sure that you're committing what you intend to.
  5. When you've double-checked the above, click "Commit"

In git, committing changes to your local repo does not change the remote copy of your repository on GitLab. To do this, you must “push“ your commits to the remote repository. See the below instructions for pushing your local commits to GitLab.

Pushing Commits to GitLab

Making a commit stores changes in your local repository (i.e. on your computer only). You need to push by following Git » Push to propagate those changes to GitLab. Make sure to do this! TAs will grade the version of your work that appears in GitLab.

You may commit multiple times before pushing all those changes to GitLab. Or, you might choose to push every time you commit, to avoid forgetting.

The IntelliJ Documentation has more detailed instructions on how to push changes to the repository. In this menu, IntelliJ shows you all the commits being included in the push. If you have any new tags in your local repository, make sure you check the "Push Tags" box at the bottom right of the Push window. When you're ready, click "Push" to push your changes to GitLab.

When you push a commit that has a hwN-final tag on it, GitLab will run some validation steps on your code. Usually, this consists of making sure all your code compiles in the same ways that the TAs will compile your code. It'll also make sure the javadocs for your code build successfully, and any provided tests or test you wrote run successfully. (GitLab doesn't run staff tests, that happens after the turn-in deadline for an assignment.)

If you get an email from GitLab saying that "Your pipeline has failed", that means that one or more validation steps have failed - your code isn't ready to be turned in! This can be a lifesaver in catching little mistakes you might have missed before turning your code in. Make the appropriate changes to fix the issue, then move your final tag to the new commit and check that the re-run pipeline succeeds. You can use the links in the email to learn more about why the pipeline failed. See the assignment submission reference handout for more details.

Resolving Conflicts

When multiple people (or the same person on multiple machines, such as the lab machines and your own computer) are working on the same file concurrently, git tries to merge the changes made by each person together as each person pulls. Usually, git succeeds.

The most common case of this is when the staff pushes out new sets of starter files for each homework. After this happens, you may be unable to push any changes until your local copy is all up to date with the new starter files. This is especially likely if you have made commits that you haven't pushed to GitLab yet. If you see an error that mentions '[rejected - non-fast-forward]', don't fret! Simply pull as you normally would and an automatic merge should happen.

Once the automatic merge completes, push to ensure the changes are passed on to GitLab.

However, sometimes git is unable to merge the files together when there are two different changes to the same line of a file. In this case, git will signal a conflict during the update; IntelliJ will let you know that the Automatic merge failed; fix conflicts.

Git conflicts are rare — most students will never encounter one in this course — but if you do get a git conflict, you need to resolve it. This is a very brief primer about resolving conflicts; you can read the git documentation to get the full story. Also, it's better to prevent a merge conflict than to have to resolve it later on.

In the IntelliJ terminal window, to see the status of all your files, run git status. This will tell you, for each file and directory, whether it is currently in a conflicted state or not.

When git detects a file conflict, it changes the file to include both versions of any conflicting portions (yours and the one from the repository), in this format:

  <<<<<<< filename
  YOUR VERSION
  =======
  REPOSITORY'S VERSION
  >>>>>>> 4e2b407... -- repository version's revision number

For each conflicting file, edit it to choose one of the versions (or to merge them by hand). Be sure to remove the <<<<<<<, =======, and >>>>>>> lines. (Searching for "<<<" until you've resolved all the conflicts is generally a good idea.)

Once you've fixed the conflicts, you can signal to git that you've resolved the issue by committing the files that you've fixed, as normal.

Preventing merge conflicts

The text above showed how to fix a merge conflict if one occurs. It's better to prevent them in the first place. Conflicts are possible even when you are working by yourself.

The remainder of this section gives tips for preventing merge conflicts when working with teammates.

Git is no replacement for management! Coordination of work is important, even if you're working separately. You should minimize working on the same file at the same time if possible. If you do work on the same file, work on different portions. Modularizing code into multiple files often makes parallelizing work more efficient. You should always pass major design decisions by your teammates before implementing them, particularly if they involve interfaces that will affect their code.

When and how often should you commit? If you commit too often without sufficient testing, you may introduce bugs into the repository that will affect your teammates' work. However, if you commit too rarely, your teammates will be using outdated code, which may cause wasted effort and merge conflicts later.

There is no hard and fast rule, but one good rule of thumb is to make sure everything at least compiles before you commit and push. If you push non-compiling code, your teammates will be very annoyed when they update (which is good practice) and cannot compile the code any longer.

Another good rule of thumb (though this one is far more malleable) is that you should minimize leaving something uncommitted when you quit for the day. A lot can happen while you're not coding, and it's generally better to get your changes in working order and commit it before you leave. Large amounts of uncommitted code being committed all at once will result in much more conflicts than small amounts of code being committed often. Since the previous rule (of never pushing non-working code) is more important, this can be hard to accomplish if you're making big changes. Thus, it's often good to tackle one feature at a time, so you can finish each piece quickly and keep the repository up-to-date.

Coordinating your efforts with your teammates is, of course, the true key to minimizing merging hassles. Again, git is no replacement for management!

Tracking Changes

It's sometimes useful to see what changes have been made since your last commit. The IntelliJ Documentation has instructions on how to track changes.

Git Pitfalls

Some tips on avoiding common problems while using git: