Lab 8: Source Control with SVN

In this lab you will familiarize yourself with the Subversion (or SVN) source control system. While many people interact with SVN using a GUI interface (often part of their development IDE), we can access SVN with command line parameters on bluenose.

All development companies use some form of source control, so becoming familiar with the basic notions of source control is useful. You won't be a master of source control after this lab, but you will have at least seen some of the basic concepts and commands. Subversion is relatively popular (as far as free software goes). A more recent source control system is called "git" while Perforce and ClearCase are two popular commercial offerings. Older source control systems include SCCS, RCS, and CVS (in case you hear about them).

In this lab you will learn how to

  1. checkout projects in SVN
  2. add a subproject to SVN
  3. add and delete files to SVN
  4. change files in SVN
  5. retrieve previous versions of your code
  6. identify what has changed between versions of your code
  7. identify what changes other people have done to your code

Before you Begin the Main Lab

  1. Before the lab itself, direct a web browser to https://svn.cs.dal.ca and sign in to claim your personal subversion (svn) repository. You don't need to do this step if you have claimed your svn repository previously.
  2. Log into bluenose in two different windows. Call one window your primary window and the other window your secondary window. The lab will be working in two different directories, so you can have each window open to a different directory to avoid changing directories all the time.
  3. Create a lab8 directory in 2132. In the lab 8 directory, create two subdirectories called primary and secondary.
  4. Execute the following commands, replacing "your_id" with your bluenose name, and providing your bluenose password when prompted, and saying "no" when asked to cache your password:
    svn mkdir https://svn.cs.dal.ca/your_id/2132
    svn checkout https://svn.cs.dal.ca/your_id/2132
    
    The rest of your lab will be done within the 2132 directory that gets created. I suggest that you make the 2132 directory that is created your main directory for your course material; you will be asked to submit assignments to the course using SVN and having them in this subdirectory will be the easiest way for you to accomplish that task.
  5. In your primary window, make 2132/lab8/primary the working directory. In your secondary window, make 2132/lab8/secondary the working directory

Note that in the commands for this lab, I include my username (mcallist). Whenever you see that name, substitute in your own bluenose username.

Each time that you enter an SVN command in this lab, you will be asked for your bluenose password. Enter it. When asked if you want to store the password, answer "no" each time. While it becomes troublesome in the lab, the password isn't stored safely for now so we don't want you to store it. Tech support is working to fix this problem.

SVN

SVN helps you manage a remote directory system where you can store files. That directory system is called a repository. The repository keeps a history of all the changes that are made to its files so that you can review how the files changed and retrieve previous versions of your files. Repositories are often shared among people as a way for all the people to get access to the same set of files.

SVN uses the notion of a working copy of the repository. So, when you put files into the SVN repository, you don't edit the files directly there. Instead, you retrieve a working copy of the files, modify your working copy, and then send (or commit) the files back to the SVN repository when you are satisfied with their state. So, you generally just commit files from your working copy to the SVN repository once the files are in some kind of stable (though possibly incomplete) state. For example, when using a repository to store programs, you generally do not commit files until they compile, at the least. In most cases, you will just commit the program files after they pass your unit tests.

The make_svn script from the preparation step created a project in your personal SVN repository called SVN. It has no subprojects. Our first step is to create a subproject for this lab in your repository.

Adding and deleting files

  1. In your primary window, retrieve a working copy of the whole 2132 project using SVN's checkout command. The repository is located on a separate machine, called svn.cs.dal.ca, that you can't log into. You interact with svn.cs.dal.ca through the svn command on bluenose. In the following command, replace "mcallist" with your own bluenose username:
     svn checkout https://svn.cs.dal.ca/mcallist/2132
    Notice that the checkout command uses a web address, so you can get to the machine from anywhere on the Internet. Also notice that the start of the web address is https and not http.
    You now have a directory called 2132 in your primary window that has the entire contents of your 2132 repository (which is nothing so far). Make this 2132 directory your working directory in your primary window.
  2. List all files (visible and hidden) in this 2132 directory
     ls -a 
    While there are no visible files, SVN created a hidden subdirectory called .svn. This directory contains all the SVN state for the directory, including the location of your repository. While you are in this directory, you don't need to specify the repository name anymore since SVN will get that information from the .svn subdirectory
  3. Create a subdirectory in your primary window called lab8 This will be our new "subproject" to add to SVN.
     mkdir lab8 
    We will now add this directory to the SVN repository with the SVN add command:
     svn add lab8 
     cd lab8
    You should see the output line
     A         lab8
    where SVN is telling you that lab8 will be added (the "A") when you next commit your changes to SVN. The lab8 subdirectory is not in your repository yet. It is just ready to go. To create the lab8 subdirectory in your repository, you must commit the add command using the SVN commit command:
     svn commit -m "Creating a project for my lab 8 work" 
    And you should see back
     Adding        lab8
    
     Committed revision 2.
    This subdirectory is now available in your repository with the web address https://svn.cs.dal.ca/mcallist/2132/lab8
    The -m "xx" part of the command adds a message to the repository to explain what you are doing. When you or other people review your repository later, the log messages will be used to find out what changed, in a high-level view, in the code.
    All commit commands require a log message. If you forget to include the -m part then SVN will start your default editor to ask you for a log message.
  4. Make the lab8 directory your working directory in the primary window. Copy the following program into a file called sample.c
    #include <stdio.h>
    
    #define UNIX_ALL_OK (0)
    
    int 
    main( int argc, char **argv )
    {
      int status = UNIX_ALL_OK;
      int i;
    
      for (i = 0; i < 10; i++) {
        printf ("*");
      }
      printf ("\n");
    
      return status;
    }
    
    and make sure that it compiles, leaving the a.out compile file in your directory. Create a second copy of the file as sample2.c
     cp sample.c sample2.c 
    We will now add both files to your repository, again with the SVN add command:
     svn add sample.c sample2.c 
    with output
    A         sample.c
    A         sample2.c
    
    Notice that we can add any number of files at the same time to the repository. Are these files in the repository yet? No, since we haven't issued a commit command to send them from your working copy to your repository.
  5. We can compare the state of our working directory with the SVN repository with the SVN status command. In your primary window, enter
     svn status 
    Your output should look like
    ?       a.out
    A       sample.c
    A       sample2.c
    
    The "A" for the two sample files indicates that these files are waiting to be added to the repository. The "?" at a.out indicates that SVN knows nothing about the a.out file and will do nothing with the file.
    Send the two files to SVN with the SVN commit command.
     svn commit -m "Adding the first version of my sample files.  Both compile but are untested."
    If you re-issue the SVN status command, you should now get as output
    ?       a.out
    
    SVN doesn't report on sample.c and sample2.c since the copy in your working directory is now identical to what is in the repository.
  6. Suppose now that we didn't mean to add sample2.c to the repository and want to remove it. We use the SVN delete command:
     svn delete sample2.c 
    that outputs
     
    D       sample2.c
    
    The leading "D" indicates that the file is now set for deletion. Re-issue the SVN status command to get the output
    ?       a.out
    D       sample2.c
    
    again, showing that SVN doesn't know about a.out and is ready to delete sample2.c. The change doesn't happen until you commit it to the repository:
     svn commit -m "File sample2.c was added by mistake earlier.  It was just a backup copy of sample.c" 
    While sample2.c is no longer active in the SVN repository, we can still retrieve past copies of it later if we conclude that we shouldn't have deleted the file. Notice that the sample2.c file is no longer in our working directory.
  7. We will now start to work with two copies of your code. You might do this in real life if you are trying out two different solutions or if two different people were working on the same code.
    In your secondary window, checkout a copy of just the lab8 code, remembering to replace the "mcallist" part with your own bluenose username:
     svn checkout https://svn.cs.dal.ca/mcallist/2132/lab8 
    You should now have a lab8 directory in your secondary window with a sample.c file in it. Make that lab8 directory your working directory in the secondary window. Notice that there is no sample2.c file in this directory. We did the SVN delete command on the file, so we don't retrieve the file when we checkout fresh copies from the repository.
    By default, SVN returns the most recent copy of the code that is in the repository.

Changing files

  1. In your primary window, edit your copy of sample.c and change the for loop to iterate 15 times rather than 10 times. Once you exit your editor, issue the SVN status command again. You should have output that looks like
    ?       a.out
    M       sample.c
    
    The "M" with sample.c indicates that your working copy of the file has modifications that are not in the repository. You can view the modifications with the SVN diff command:
     svn diff sample.c 
    The command will show you the lines where your version and the repository version of the file are different along with a few lines before and after the difference.
    Let's send this modification to the repository with the SVN commit command again
     svn commit -m "Use a longer loop iteration" 
    You should get a message to show that the change was sent to the repository. Re-issue the SVN diff command for the sample.c file. Does it report any differences? Why?
  2. In your secondary window, look at the contents of sample.c. It doesn't include the change that you just sent to the repository. This is because changes to the repository are not automatically sent to your working copy of the code so that this working copy remains stable for you.
    To retrieve this newer copy, issue the SVN update command to update the working copy
     svn update 
    with output
    U    sample.c
    Updated to revision 16.
    
    The leading "U" means that there is a more recent copy of the code in the repository than what is in the working copy of the code.
    What happened to the number of iterations in sample.c in the working copy?
  3. What happens if we delete a file in a working copy? In the secondary window, delete the sample.c file:
     rm sample.c 
    Confirm that the file is gone with the UNIX ls command.
    Executing the SVN status command will show you that the file sample.c is missing by the ! symbol in its output:
    !    sample.c
    
    Issue the SVN update command one more time. What happens to the contents of the working directory?
  4. Let's now introduce changes in our two copies of sample.c. In sample.c of your primary window, add a printf statement before the for loop:
     printf ("I am about to start the loop\n");
    In sample.c of your secondary window, add a printf statement just before the return statement:
     printf ("I have finished the loop\n");
    Now each copy of your file has a different change to sample.c. In each of the directories, use the SVN status command to confirm that the sample.c file is different from what is in the repository (look for the leading "M" tag beside the file names).
  5. In your primary window, issue an SVN commit command to send the change to the repository. Don't forget to include a -m "xx" log message in the command.
    In your secondarywindow, try to commit the change to sample.c The command will fail since someone (you) committed a change to sample.c in the repository after the working copy in the secondary window was retrieved. To do the commit, you must first ensure that you have the most up-to-date version of the code from the repository. Use the SVN update command to get this copy
     svn update 
    with output
    G       sample.c
    Updated to revision 17.
    
    The "M" tag on sample.c from before has now changed to a "G". The "G" indicates that your changes and those from the repository have been automatically merged in the file. SVN was able to merge the changes since the changes in the primary and secondary windows didn't overlap.
    Edit the sample.c file to confirm that it now has both changes. We are satisfied with this change, so re-issue the SVN commit command in the secondary window to send the update to the repository (again, don't forget to include a comment for the commit command).
  6. Bring both directories up to date. In both the primary and secondary windows, issue the SVN update command
     svn update 
  7. We will now see what happens if the separate changes conflict with one another. In your primary window, edit sample.c, change the for loop to iterate 20 times, and commit the change to SVN. In your secondary window, edit sample.c, change the for loop to iterate 5 times, and try to commit the change to SVN.
    Like the last time, the commit in your secondary window failed because your secondary window didn't have the most recent copy of the code. Again, like before, use the SVN update command to try and get the most recent version of code from the repository. Unlike last time, SVN isn't able to merge the two changes since they are on the same line. Instead, the update returns with the following message:
    Conflict discovered in 'sample.c'.
    Select: (p) postpone, (df) diff-full, (e) edit,
            (mc) mine-conflict, (tc) theirs-conflict,
            (s) show all options: 
    
    SVN is giving you the opportunity to fix see the differences (df) or fix the problem (edit the file). If you choose to edit the file, SVN will show you the file in your default editor. Since we haven't defined that editor, let's select the postpone option. Enter p and let SVN continue with the update (if there were other files).
    In the secondary directory, SVN has now left several files. In addition to your sample.c, it has the files sample.c.mine, and two sample.c files with an ending of .rX where the "X" is the version number of each file. At this point, you can either change sample.c to merge both changes (still having the other versions around as a reference) or copy one of the other versions to overwrite sample.c with what you want the final copy to be.
  8. In the secondary window, re-issue the SVN status command. Rather than seeing that sample.c is modified, you will now get the output
    C       sample.c
    
    where the leading "C" indicates that sample.c has a conflict that you need to resolve before it is sent to the repository.
  9. Before fixing the conflict, we should find out what the changes were. A first step is often to look at the comments from the commit to find out why someone else changed the repository. Use the SVN log command in the secondary window to get the log of messages for a file:
     svn log sample.c
    You will get output to show each of the versions of the file along with the commit messages. If the message on the last commit for sample.c isn't giving you enough information, you can use the SVN diff command again to see where your file differs from what is in the repository
     svn diff sample.c
  10. Given this information, choose a fix to sample.c (for the lab, it doesn't matter what the "fix" is). If you try to commit the file, SVN will still refuse since it doesn't know yet that you have resolved the conflict. To do so, issue SVN's resolved command on the file
     svn resolved sample.c 
    After issuing this command, the sample.c.mine, and sample.c.rXfiles will be removed from your working directory. You can now issue the SVN commit command to send the changes to the repository.

Retrieving copies of previous work

  1. You sometimes make changes to a program that you later regret and want to undo. Many of you probably make a copy of a file once it is working and this "undo" step means copying that old file to overwrite your current file. We want to use a repository to automate that process better.
    Each time you reach a stable point in your development, it is worthwhile to do an SVN commit to put that stable point into your repository.
    In your secondary window, edit sample.c and introduce a compile error (like removing the declaration of variable i). Save the file to disk (but not to the repository).
  2. Suppose that you now want to undo that change and return to the last "saved" copy in the repository. Three options that you already know how to do are: Each of these involves a bit more work than is necessary. If you want the last version of the file, SVN has a command called revert to retrieve that copy:
     svn revert sample.c
    After issuing the command, look at sample.c again to notice that you now have the version of the file that precedes your latest editing.
  3. It is possible that you might want to retrieve a much older version of your file. You can use the -r option the update command. The -r lets you specify which revision of the file that you want.
    Try
     svn update -r 5 sample.c 
    What is the content of this sample.c file?
    If you changed this earlier version of the file, SVN will not let you commit the change directly to the repository since it is based on an out-of-date version of the file. You would need to do an SVN update command to return to latest copy (inducing a conflict with the file) where you then need to resolve any conflicts with the latest version.
  4. Sometimes you don't know the exact revision number that you want to retrieve, but you know when the directory was last stable. You can specify a time, rather than a revision number, in the SVN update command.
    Suppose that I knew that the file was stable on October 27th, 2010 at 1pm. I could issue the following command to retrieve the version of the file that existed in the repository at that time:
     svn update -r {"2010-10-27 13:00:00"} 

Your own work

Add assignments 0, 1, 2, and 3 to your 2132 SVN repository, each as its own project.