CS61C Fall 2013 Lab 1

Registering your Account

Your TA will give you a sheet of paper with your class login (cs61c-XX) and initial password.

Log into one of the lab machines.

Add a shortcut to the Terminal program to the dock on the left hand side of the screen (bottom if you are in 200 SDH)

* Click on "Dash home" (the icon at the top of the dock)
* Type "Terminal" into the search field.
* Drag the Terminal icon onto the Dock.

Now start a Terminal session and change your password by typing ssh update and following the on-screen prompts.

Once you've changed your password type register and answer the following questions to associate your class account with your name and student ID number. (If the terminal asked you to register already and you've done so, you don't need to do it again!)

Initializing your working repository

Execute the following commands, where it is assumed that you will be developing your projects and homeworks in subdirectories of work (the name is inconsequential, name it something else if you like).

mkdir work
cd work
git init
echo "This is the top level of cs61c-XX's working directory" > README
git add -A
git commit -m "First commit."

Now let's walk through what we just did. The first two commands just made a work directory and moved us into it.
The third command, git init, places the current working directory under version control by placing the hidden directory .git inside of it. Now that work is version controlled you'll be able to return to pervious versions of your work if you muck something up while developing, among other things.
The fifth command, git add -A tells git that you think all of the changes to the repository since the last commit are worth tracking (git only tracks the files you tell it to). The jargon for the set of files whose changes are worth tracking is the index. So another way of expressing what git add -A did is to say that it placed everything in the directory in the index.
The final command you executed, git commit -m "First commit.", told git that this was a version of your code that you want to be able to get back to. The -m option told git to associate the message "First commit." with this version. On a side note, git will refuse to make a commit if there are no indexed changes, which is why we have the fourth command creating a README.

Pitfall Prevention: DO NOT RUN git init OR git clone FROM INSIDE A VERSION CONTROLLED DIRECTORY, INCLUDING ONE OF ITS SUBDIRECTORIES. This will cause "git in git" problems, which will almost definitely make someone's head hurt before they are resolved.

Pulling from another repository

Git provides the capacity for source materials to be shared between repositories. There are two main ways of doing this, the first of which is git pull, the other being git push, which we will cover later. git pull goes to the repository specified, at the specified branch or tag (line of development or labeled commit, respectively), and merges it into your repository. Basically it takes the code at the remote repository and sticks it in yours. Let's try it. We've placed the files for this lab in a git repository at ~cs61c/labs/fa13/01/. You can pull the files into your repository with these commands:

git remote add lab01 ~cs61c/labs/fa13/01
git pull lab01 master

So what does that do? The first instruction tell git to add ~cs61c/labs/fa13/01 to its list of remote repositories under the alias lab01. The second line goes to the lab01 repository, and merges its master branch into your currently checked out branch. Of course, you have no idea what all this business with branches is, which is why we're covering that next.

Pushing to another repository

Pushing is the opposite of pulling. Where git pull took code from another repository and stuck it in yours git push takes your code and sticks it in another repository. In the real world it's not uncommon for teams to share work by pushing to a master repository which everybody else then pulls from.

Quick note on gcc and makefile

gcc is the c compiler that we use to compile our code in this class. You can compile a program by doing gcc PROGRAM.c -o PROGRAM, where -o specifies the name of the binary to be made. Makefile is a common utility tool used to compile various programs cleanly, and can have variables and various functions. You run makefile by typing make, and you can clean the binaries by typing make clean assuming that the makefile has a clean function made by the programmer. Once you compile the program by using gcc or make, you can then run the program by doing ./PROGRAM_BINARY.

Branching and Merging

OK, so this is going to get a little complicated, but the bare bones of it are pretty simple. If you find yourself getting lost, try to make sure you don't forget these two things. First, branches represent lines of development, like trains of thought. Second, a merge takes two of these lines of thought, and incorporates one into the other.

You can think of version control as being a bit like a singly linked list. Every time you make a commit a new version of your code gets prepended to your list, and has as its child the next most recent version. That way if you want to back track to an old version you just look back through the list until you get to the version you want. What branches do is allow for your list to take on a more general graph structure, with nodes having multiple parents (ie, spawning multiple newer versions of code). When we give a node(version) more parents(successors) we call it branching, because we are splitting, or branching, the code into separate development paths. Merging is a little bit more complicated than just spawning a single parent for two children. It would be a more accurate depiction to say that we copy the nodes that the mergee (branch being merged into) doesn't have, but the merger (branch merging into the mergee) does, and inject those into the mergee. After we do that we look at the differences between the head of the merger and the head of the mergee before we injected the nodes from the merger. If these differences are "good" we'll combine the two automatically and make a new commit to the mergee. Otherwise we'll ask the user to perform conflict resolution manually, and then commit the changes.

Now that you have some idea of how branching and merging works at a high level, you're going to try using it on this lab. You'll notice that the code you've pulled in has a Makefile and a hello world program, but that the code doesn't compile. There are two ways of getting the program working. Either you can change the Makefile to make the compiler less picky, or you can fix the C code to be compliant with stricter standards. You're going to do both. First you'll make a branch for each.

git branch lab01c
git branch lab01make

Next you'll switch to the lab01c branch (ie, you will checkout lab01c) and fix the C code. Be sure to commit all your changes when you finish, and also to delete the binary files that were made when you were doing testing.

git checkout lab01c
/* Whatever you need to do to make the C code work. */
git commit -a -m "You should write a more meaningful commit message than this."

Then the same idea with the Makefile. By the way, the "-a" flag for commit lets the commit know to check all of the files you are currently tracking and commit them. It will NOT commit any new files you made since you ran git add. You can combine the flags and just type "-am" as well.

git checkout lab01make
/* Whatever you need to do to make the Makefile code work. */
git commit -a -m "You should write a more meaningful commit message than this."

Last merge in the improvements of both branches into master.

git checkout master
git merge lab01c
git merge lab01make
git commit -am "Combined both branches"

And that's it, you've used branches to explore different lines of development. This example may seem contrived (it was), but for more complicated problems this approach can make your life a lot easier. (The last command of commit might not do anything, if git merge did a commit automatically)

Tagging important commits

git tag, in essence, applies a label to a given commit. In the real world you may want to tag the versions of the code which were used as releases, or perhaps just mark a commit which made a major difference in the way the code behaved. Apply the tag "lab01" to your most recent commit using the following command.

git tag lab01

Reverting to previous commits

Now this is one of the most useful parts of git, being able to revert back to older versions or at least being able to look at them again. Now let's say you accidentally did something that deleted your code. For example, if you try to compile something yourself and accidentally type

gcc hello.c -o hello.c

what will happen? Try it out.

When you look at hello.c after doing this, you'll see that you have overwritten the program with the executable! You probably won't make this specific mistake too often, but you can see how such things can occur. Delete the bad hello.c and commit this change, or you will not be able to pull the old version properly.

Now how do we go about accessing the old code? One way to go about it is to use git log and then find a working commit. Another way to do it is by using the tag we made earlier. If you do git show TAG_HERE, then you can see information on what happened in that commit, as well as the commit id. Either case, obtain the commit id, and then type out this command:

git checkout -b BRANCH_NAME COMMIT_ID

This command will give you a new branch named BRANCH_NAME that is your git commit respective to the COMMIT_ID.

In this case, let's name your branch "unbroken". After you run the previous command with the appropriate input, your directory will have the working version of hello.c!
Unfortunately, with how git works, you will need to make a minor change to this file before you can merge it to the master branch again. Therefore, add a comment somewhere in hello.c .
After you modify this hello.c a bit, commit this branch, then switch to master. Now, merge the branch unbroken to the master branch. That will allow you to do work on the master branch again. It will complain about conflicting copies of hello.c, which makes sense because you deleted hello.c in the main branch, and you have a new one in the unbroken branch. That is perfectly fine. Now, just remember, you should ALWAYS have the final form of your work in the master branch! That's why we are merging it back to the master instead of just working from the new branch:!

Checkoffs

Other things with git

Here is a quick git notes link (also available in the main 61c page) that is a summary of the commands we went through in this lab! Git Notes.
If this lab wasn't enough to give you a clear idea on what git can do, here's also a useful gui-interactive git guide available online. Interactive Git.
Good luck with the semester, and remember, the TAs are always around to help you!