This handout describes how to perform common Java development tasks on the command line. If you prefer to use an IDE (as you probably should), read the instructions on using Eclipse or IntelliJ instead.

Contents:

Using the Unix Command Line

A few CSE 331 tasks require connecting to a Linux machine and running some commands from the command-line. Understanding the instructions below may require some basic understanding of how to use a shell to perform command-line actions. That is described (in more depth than we need) in CSE390A, which has materials posted online.

Running Automated Tasks with Gradle

Gradle is a tool that can be used to automate many common tasks, such as compiling, testing, and running code. The instructions for Gradle are stored in a file named build.gradle in the root directory of your repository.

The buildfile specifies a set of tasks that it can perform, such as build and test. The “help” task will output more information about how to use gradle and the options available with this build file.

All of the gradle tasks described below should be performed in the root directory of your local repository, which you set up by cloning your repository from gitlab.

Compiling Java Source Files

You must compile your source code before running it. The javac compiler is used to transform Java programs into bytecode form, contained in a class file. Class files are recognized by their .class extension. The bytecode in class files can be executed by the java interpreter.

To compile all your source files, type this command on the command-line:

./gradlew classes

This will run a gradlew script in that directory, which uses the instructions provided in build.gradle to compile the appropriate .java files in your repository into corresponding .class files. Note that if one or more of your files do not compile, you will receive error messages and no .class files will be generated for the files that do not compile properly.

Running Java Programs

Once you have compiled your source code into class files, you can execute it with the Java interpreter.

Here is how to run Java programs from the command-line:

java -cp build/classes/java/main -ea hwN.theClassYouWantToRun

The -cp flag tells java where to find the .class files produced by javac, which contain the compiled source code. And the -ea flag enables assertions.

For example, if you wish to run the RandomHello class from hw3, you would run:

java -cp build/classes/java/main -ea hw3.RandomHello

Note that you do not include .java or .class at the end of the class name.

Testing Java Programs with JUnit

JUnit is the testing framework that you will use for writing and running tests.

For more information, visit:

The test gradle task can be used to run the tests with the command:

./gradlew test

Using javadoc to generate specs

Oracle's Java Development Kit includes javadoc, a tool that produces specifications from source code annotated with special comments. The comments may include "tags", which are introduced by an at-sign (@). (In addition to the standard javadoc tags, CSE 331 includes some custom tags used only for this course.)

The javadoc gradle task can be used to run the tests with the command:

./gradlew javadoc

This will produce HTML documentation, which you can view by opening the file build/docs/javadoc/index.html (Note that running this task will completely overwrite the existing documentation in that folder.)

You may see a BUILD FAIL message. This could be because of serious syntax errors in your files that cause the code not to compile. This may also be the issue if you see a BUILD SUCCESSFUL message but incorrect API in the doc folder. To fix these problems, look at the error messages and fix up the most serious problems reported there. Javadoc can produce documentation for partially implemented files (that is one of its strengths), but it can't compensate for some serious problems in the source code.

After running the ant doc target, you should check the output. You may find that you need to add line breaks (<br>) or paragraph breaks (<p>) to your javadoc comments for readability. Also, if you omit certain tags, subsequent text may fail to appear in the output. Finally, since much of the text of javadoc comments is inserted in a HTML document, you must be careful with text that can be interpreted as HTML markup, such as the less-than (<) and greater-than (>) characters. For instance, if you write:

@effects Adds <x> and <y>

then <x> and <y> will be interpreted as HTML tags in the output (and won't be displayed by a browser). It's usually better to just write

@effects Adds x and y

Report any weird behavior or complaints about javadoc to cse331-staff@cs.washington.edu.

Cloning the project from GitLab

You always edit your own personal copy of files that are under git control. Before you can make such edits, you must “clone” your own copy of the repository's master files. This means that you are copying code from the master branch from another source (GitLab in our case) into your own local, personal copy. You need to do this step only once at the beginning of the quarter. If you are tempted to use this command at other times, you most likely want git's “pull” command, which incorporates changes from the repository into your existing local copy. In the case that you plan to work at both UW CSE and home, you will need to do these setup steps both while logged into a department machines and from your home computer so you have a local copy on both machines.

The following instructions assume you wish to clone your personal Gitlab repository that is located at https://gitlab.cs.washington.edu/cse331-19wi-students/cse331-19wi-YourCSENetID. Make sure this URL takes you to your own project!

Execute the following commands at the command line to check out the project to the directory cse331-19wi-YourCSENetID:

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

Note that, 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 simply isn't being shown. Just type your password as normal and press enter.

Pulling Changes from Gitlab

Git's "pull" command updates your local copy of files to reflect changes made to the 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 commit and push on each machine when you are done on one machine and then pull when you start on another machine. That will propogate your changes to local machine.

Git usually does a good job of merging changes made to multiple copies (say, the copy on your home computer and the copy on attu) 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.

To update your local copy on the command line, go to the root of your repository (e.g. ~/cse331-19wi-YourCSENetID) and run:

git pull origin master

or just

git pull

which defaults to using master. This command will display a list of files that have been updated.

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.

Be aware that in git, committing changes does not change the remote copy of your repository on GitLab. To do this, you must “push“ a single (or series) or 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.

To perform a commit on the command line, first, enter the directory in which the file(s) you wish to commit are located. For example:

  cd ~/cse331-19wi-YourCSENetID/src/main/java/hwN

Next, to list all the changes you have made since the last git pull, type:

  git status

This will print some output saying either you have no changes, or show your staged (to be committed), unstaged (not to be committed), and untracked (files not added to git) files. Untracked files show up when you create a new file in a directory which is under git control. Git will not automatically add these files to the repository to avoid extraneous files from being added.

Below is an example of output you might see in a git status:

  Changes to be committed:
    (use "git reset HEAD ..." to unstage)

    modified:   Ball.java

  Changes not staged for commit:
    (use "git add ..." to update what will be committed)
    (use "git checkout -- ..." to discard changes in working directory)

    modified:  Box.java

  Untracked files:
    (use "git add ..." to include in what will be committed)

    NewFile.java

Now you can add a file (or directory) as follows:

  git add NewFile.java

Then, you can commit your staged files to the local git repository by running:

  git commit -m "a descriptive log message"

Alternatively, you can stage (add) and commit all of your unstaged commits in one command as follows:

  git commit -am "a descriptive log message"

If you omit the message flag -m "a descriptive log message" from your commit command, git will throw you into an editor where you can enter a message. Remember, at any time you can check the git status and see which files are staged, unstaged, and untracked.

Adding and committing files only stores the changes in the local (working) copy of your repository. To update the remote GitLab copy of your repository and store all of your locally committed changes there, use the push command after you have committed the changes you want to save.

  git push

You must be sure to push your changes, or they will not be stored in the remote GitLab copy of the repository and, in particular, will not be available when the CSE331 staff pulls files from your repository for grading.

Pushing Changes to Gitlab

Git commits exist only in your local copy of your Gitlab Repository. Pushing your changes will update your master repository on Gitlab. Make sure to do this! Grading your homework assignments requires the work you have to done to exist in the original repository. If it does not, when the TAs check out your repo for grading, they will be unable to see the work you have done.

To push your last comitted local changes to the repository on the command line, go to the root of your repository (e.g. ~/cse331-19wi-YourCSENetID) and run:

git push origin master

or just

git push

which defaults to using master.

Tagging Commits

In git, commits can be “tagged“ with a short phrase without spaces. This can be used to mark important versions of the repository, like an alpha-1.0 or release-version. For CSE 331, tags are required to mark final versions of assignments that are ready to be turned in. The correct tag to use for a final version of an assignment in your repository is hwN-final, where N is the homework number (e.g., hw3-final).

To tag the most recent committed version at the command line, go to the root of your repository (e.g. ~/cse331-19wi-YourCSENetID) and run:

git tag -a TAG_NAME -m "MESSAGE"

where TAG_NAME is a name for the tag (usually, something like hw3-final in our case) and MESSAGE is any commment you want to leave for yourself about the tag (e.g., "final version of hw3").

(It is important to use exactly this command for creating tags. Some of the other commands you can find in the Git documentation create "lightweight" tags that cannot be pushed to the server.)

The command above creates the tag in your local repository, but you also need to push it to Gitlab. That is particularly important when turning in your homework because the staff uses the hwN-final tags to figure out which version of your code to grade. If your tag is not pushed to Gitlab along with your code, the staff will not know which is your final version.

You can push tags just by adding the --tags command to git push:

git push origin master --tags

This command should display that the names of the tags that were pushed, so if you create a tag and then push but do not see the tag listed, you may have done something wrong. (You can always double check by cloning another copy of your master repository and seeing if the tag shows up there.)

Resolving Conflicts

When you pull changes into your local repository and you have local changes that have not yet been pushed, git tries to merge the changes together. Usually, it 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 pull 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.

Usually, git can perform these merges without any difficulties. 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 pull such as Automatic merge failed; fix conflicts or you may see the notice that You have unmerged paths.

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.

On the command line, 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, i.e., whether that files contains local changes that conflict with the changes it is trying to integrate.

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 have made these edits, then you can tell git that you have resolved the conflicts by staging the file as you normally would:

  git add src/main/java/hw4/test/SpecificationTests.java

Adding and Removing Directories

Git does not track directories like regular files. It only keeps track of the individual files and creates the directories that they reside in as necessary. So, if you create a directory, with the usual command

  mkdir dirname

git will not notice its existence until you add a file from that directory into your commit.

To delete a directory or file from the repository, use the standard rmdir or rm command. To remove a directory and all of its contents, use:

  rm -rf dirname

After adding or deleting a directory, you must perform a commit for the change to be reflected in the repository.

Tracking Changes

To see the change log, which lists all of the changes that have been comitted, use the command:

  git log

To see differences between the working copy and the repository's latest copy:

  git diff filename(s)

Omit filename to see differences for all files. Each commit is associated with a long hash value, which you can see in git log as commit 2d03d7...

To see changes compared to a particular commit version, enter:

  git diff REVISION filename(s)

Where REVISION is either a commit hash, like 2d03d7... or HEAD for the repository's most recent master version.

Viewing an old version of a file

You can show an old version of a file under git control with commands like the following:

  git show HEAD:MyFile.java
  git show 7ff8dc80:MyFile.java
  git show HEAD@{2013-03-14}:MyFile.java

As seen in the first command, HEAD means the most recent commit on your local copy of the repository. The second command uses a commit hash and the third uses a date.

You can also revert to an older version of a file under git control:

  git checkout HEAD MyFile.java

This command changes your working copy — that is, your local directory — but it does not change the repository. Do not attempt to edit the old version of the file in your working copy. Doing so will result in nasty merge conflicts and confusion.

To reset all of the files under git control to a previous version, use the following command:

  git reset HEAD

Note, checkout and reset are complicated and can have adverse affects on your local repository. You MAY lose data playing with these commands, and you should only use them with full understanding of what they do.

You should save a copy of the file (e.g. git stash) somewhere, then git pull your working copy to the current version of the file. Now, edit the current version in whatever way you like, possibly copying some or all of the differences from the old version that you saved. You can discard the old version when you are done; there is no need to check it in into GitLab.