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:

Setup: Cloning the project (creating your working copy)

You always edit your own personal copy of files that are under git control. Before you can make such edits, you must “clone” to create your local repository (which is stored in a hidden directory) and your working copy (where you will do your programming). You need to do this step only once at the beginning of the term. If you plan to work at both UW CSE and home, you need to do these setup steps both while logged into a department machine and from your home computer, so you have a local copy on both machines.

  1. Ensure that your repository exists, by browsing to https://gitlab .cs.washington.edu/cse331-19su-students/cse331-19su-YourCSENetID (be sure to change the YourCSENetID part!).
  2. Setup an ssh key. Do this on each computer you plan to use. Follow the instructions to create an RSA Key and the instructions to add the key to GitLab. You should use an empty passphrase, which is less secure but perfectly reasonable in this scenario.
  3. Run ssh -T git@gitlab.cs.washington.edu to ensure that your key is correctly setup. You should receive a welcome message and should not be prompted for a password.
  4. Follow the below instructions for cloning the repository, either from the command line or from IntelliJ (not both). You should use the IntelliJ clone instructions when you're working on a normal development computer (like your personal computer or a lab machine.) You should only use the command line instructions when cloning your repository onto attu.

IntelliJ

NOTE: Occasionally, Windows users have had trouble cloning their repository within IntelliJ. These Windows users had success cloning their repository from the command line using the "git bash" tool, then doing all their work within IntelliJ after the initial clone step. Please try that if you have trouble.

First, set up an ssh key.

Follow the IntelliJ instructions on how to clone a repository.

Be sure to import as a Gradle Project, pick Java 11 as your Project SDK, turn on Auto-Import, and ensure that your project uses the recommended Gradle Wrapper option. Failure to set these options correctly may cause issues when working on later homework assignments.

The URL to use when cloning your repository is git@gitlab.cs.washington .edu:cse331-19su-students/cse331-19su-YourCSENetID.git .

Command Line

Use these instructions to clone your repository when you're signed into attu or another computer that only has a command line interface or doesn't have IntelliJ. Execute the following commands at the command prompt to clone the project and create a working copy in ~/cse331-19su-YourCSENetID:

cd
git clone git@gitlab.cs.washington.edu:cse331-19su-students/cse331-19su-YourCSENetID.git

Note for those who are new to the command line: When you try to type passwords in the command line, you may be alarmed that you can't see any text entered. To protect your password, your typing isn't being shown. Just type your password as normal and press enter.

You can run any git command in any directory of your working copy.

Updating 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. This 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.

The IntelliJ Documentation has instructions on how to pull changes from a repository. You probably won't have to change most settings from the defaults in the "pull" dialog box.

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.

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

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.) If you forget to pull, git will abort and remind you to pull first.

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.

The IntelliJ Documentation has instructions on how to commit changes. See below for details.

In general, you can use the VCS > Commit popup to determine what files you want to commit and make your commit. In Git, you can choose exactly which files you want to commit. In the commit window, IntelliJ will show you all files that have changed since your last commit. Check the boxes next to the files you want to commit. In the middle, enter a short, descriptive message of what changes you made in the commit (e.g. "fix the bug in HolaWorld"). At the bottom, you can see exactly what changes you made in the file you currently have selected. On the right, you probably will want to disable all the "Before Commit" options. These are just additional checks that IntelliJ does, but can sometimes get in the way of making a successful commit.

Pushing Commits to GitLab

Making a commit stores changes in your local repository (i.e. on your computer only). You need to 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 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 TA's 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.

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 — 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 they 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: