Contents:
You may be wondering why we should use version control at all. All version control systems, including git, provide the following functionality:
CSE 331 uses git to turn in assignments. At the deadline, we collect the current version of your files from your repository.
Something important to mention is the distinction between git (version control software), and services like Gitlab and Github (code hosting services). For CSE 331, your source code will be submitted via Gitlab, a place to host your code provided by the CSE department, and you will use git as a version control tool to download, update, and synchronize your work.
Git works as follows. There is a “repository” containing the master version of the files, including a history of previous versions. Each user “clones” (a.k.a. “download“) a working copy of the files, complete with a local copy of the repository and all past history.
Each user can edit his or her copy of the files arbitrarily, without affecting other users or the master version.
Over many years of teaching courses like CSE331, we have observed that on average about one student's computer will either die or be stolen during the quarter. Therefore, you should commit and push your work to the repository often. Committing often:
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 term. 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 a project from a repository that is located at https://gitlab.cs.washington.edu/cse331-17wi-students/cse331-17wi-YourCSENetID. Make sure this URL takes you to your own project!
Throughout this document, you need to do the work on the command line or in Eclipse, not both. If you plan to use Eclipse (as almost every 331 student does), you should follow the Eclipse instructions. However, you should be aware of the command-line versions, which you will find helpful because Eclipse lacks certain functionality and sometimes gets wedged. If you have trouble with Eclipse, then you should use the more reliable and versatile command-line tools. They work on all operating systems.
The following instructions will show you how to use Eclipse to check out git projects. Before doing so, please check to make sure that you have properly set up the Eclipse environment for CSE 331. We will assume that you will check out a project called cse331-17wi-YourCSENetID. When you check out cse331-17wi-YourCSENetID, Eclipse will automatically set up an Eclipse project named cse331-17wi-YourCSENetID for you.
The next dialog will ask you to enter the source of your git Repository. Ignore the URI field, and fill in the following fields:
Do not forget the .git at the end!
Execute the following commands at the Linux prompt to check out the project to ~/cse331-17wi-YourCSENetID:
cd ~/ git clone git@gitlab.cs.washington.edu:cse331-17wi-students/cse331-17wi-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 simply isn't being shown. Just type your password as normal and press enter.
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 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 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.
In the Package Explorer window, right-click on a file or directory, and select Team » Pull. If the selected item is not a directory, just that file will be updated; otherwise, everything inside the directory will be updated.
To update your local copy, go to the root of your repository (e.g. ~/cse331-17wi-YourCSENetID) and run:
git pull origin master
This will display a list of files that have been updated.
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.
The git tool built into Eclipse will allow you to add, commit, and push your files all in one prompt. To do this:
Remember to tick the checkboxes by any new files you may have added in your repository, as this is not done automatically.
Note: if you create a file or folder in an Eclipse project but via the command line (or any mechanism outside Eclipse), then you might need to “refresh” the package explorer for Eclipse to recognize the change. To do this, right click on the project name in the package explorer and select the “refresh” item.
First, enter the directory in which the file(s) you wish to commit are located. For example:
cd ~/cse331-17wi-YourCSENetID/src/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.
Git commits exist only in your local copy of your Gitlab Repository. Pushing your changes will update your original 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.
When commiting your changes, if you chose to do the Commit and Push option, then no other work is needed. But if you just hit Commit, then follow these steps to push your commits to your original repo:
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.
Normally you will only create a tag when you are done with an assignment to mark the final commit associated with that assignment, and, assuming no bugs, you will not have to remove or replace a tag. You will need to change or replace a tag if you discover problems with the tagged version and need to fix those problems and "turn in" an assignment again.
Verify that a tag has been created on Gitlab by visiting https://gitlab.cs.washington.edu/cse331-17wi-students/cse331-17wi-YourCSENetID/tags
That's it! If you change your mind and want to re-submit, just delete the tag and re-create it on a different commit.
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 runs git pull. 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 git 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! In Eclipse, simply pull as you normally would. Now you should see an automatic merge happen as follows:
Click “OK“ to accept the automatic merge, then push (not commit) your changes again.
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; git pull will produce output such as Automatic merge failed; fix conflicts, and git status will produce output such as 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.
In Eclipse, the best way to tell if you currently have a conflict is to look for the red diamond icon in the list of files.
You can resolve conflicts within Eclipse with its built-in merge tool. The merge tool allows you to see your changes (on the left) and other peoples' changes (on the right). Your job is to make the file on the left the result you actually want. Once you've done this, drag it to the staged changes panel like usual. You will notice that there is an auto-filled Commit Message about a “merge“. Go ahead and “Commit and Push“. Now, you've pushed your commits and other people can see them.
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.
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/hw2/test/SpecificationTests.java
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!
You can add a subdirectory as normal with:
mkdir dirname
Git will not recognize an empty directory as a change, so you have to populate it first with a file. Then you can add, commit, and push the file as normal.
To delete a directory or file from the repository, use the standard rm command:
rm -rf dirname
After adding or deleting a directory, you must perform a commit for the change to be reflected in the repository.
Eclipse conveniently marks files and directories that have changed since the last git pull by adding an asterisk to the file's icon the Package Explorer. In addition, there are two features that allow you to track changes between your working copy and the repository's latest copy: Compare and Synchronize.
To compare a file with its latest version, right-click it in the Package Explorer and select Compare With » HEAD revision. If the files are different, a window will appear showing a side-by-side comparison of the two files.
To see a summary of differences between the local copy and the repository, right-click a file or directory in the Package Explorer and select Team » Synchronize with Repository. A window will appear that summarizes which files (if you selected a directory) have outgoing changes (changes you've made after updating), which have incoming changes (new revisions committed by others to git), and which have conflicts. Double-clicking one of these summarized items will bring up a Compare window for that file.
To view git commit logs and previous revisions to a file, right click it in the Package Explorer and select Team » Show in History. You will see a History window in the bottom panel. Double-clicking a row will allow you to read the corresponding revision.
To see the change log, which is a list of the messages used when checking in changes:
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.
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.
Some tips on avoiding common problems while using git: