Our universities don’t teach people how to do source control [Well, at least CS242 is trying!]. Our employers don’t teach people how to do source control. SCM tool vendors don’t teach people how to do source control. We need some materials that explain how source control is done.
For this class, we would like to expose you to using version control (sometimes called source code control or just source control) while developing your projects. For those of you unfamiliar with the concept of source control, you can think of it as an infinite undo stack. All major changes you made to your files can be undone1.
Most of us are used to the idea of creating multiple copies of our source code while developing. We tend to call them mp1.cpp.ver1, mp1.cpp.ver2, and so on.
Scenario one. mp1.cpp.ver1 could be something that works but we need to clean it up by adding comments or optimizing some loops. mp1.cpp.ver2 is the file that we mess with to get things all tidied up. Well, sometimes as we mess with things, we might also break things. So it is handy to keep mp1.cpp.ver1 around at least until everything is working. Source control can keep those versions for you so that you always have the pristine copy of mp1.cpp without resorting to messy naming conventions and a plethora of temporary files.
Scenario two. We could have function1() working in mp1.cpp.ver1 and function2() working in mp1.cpp.ver2. Now, when we want to hand-in the assignment, we will have to merge the changes by hand. Source control, if used properly, can intelligently merge those changes for you.
Most of us have enough trouble getting the projects done that we don’t have the time to do all the tedious book-keeping to keep multiple copies of the same file around.
In this class, our focus will be getting you started with version control. We will not be using some of the more advanced features like branching, but you are free to use that on your project if you think you need it.
For more information, you can read Eric Sink’s introduction to version control. Or for an introduction to Subversion itself, you can read this chapter from the official website.
This semester, we have gotten a subversion repository for each of you at https://csil-projects.cs.uiuc.edu/svn/cs242/<netid>. Replace <netid> with your netid. So for me, I will access my repository at https://csil-projects.cs.uiuc.edu/svn/cs242/nchen.
Before using Subversion, you need to make sure that you the client installed on your system. You can grab the necessary installers from the Subversion homepage. You might need to navigate a few pages to get to the Windows version. You should get the latest version (currently version 1.3.2 but version 1.1.4 should work fine as well). Linux distros might already come with Subversion. You can follow the instructions in the following sections to check.
There are some GUI applications for Subversion. For windows you have TortoiseSVN and for Mac OS X you have svnX
Nonetheless, for this tutorial we will be using the command line since it is easier to describe the steps.
Go to the command line (command prompt in Windows and terminal in OS X and Linux) type svn --version
svn, version 1.3.0 (r17949) compiled Jan 2 2006, 08:56:49 Copyright (C) 2000-2005 CollabNet. Subversion is open source software, see http://subversion.tigris.org/ This product includes software developed by CollabNet (http://www.Collab.Net/). The following repository access (RA) modules are available: * ra_dav : Module for accessing a repository via WebDAV (DeltaV) protocol. - handles 'http' scheme - handles 'https' scheme * ra_svn : Module for accessing a repository using the svn network protocol. - handles 'svn' scheme * ra_local : Module for accessing a repository on local disk. - handles 'file' scheme
It should report back telling you the version of Subversion that you installed. If not, you have to go back and check your installation.
Type svn list https://csil-projects.cs.uiuc.edu/svn/cs242/<netid> --username <netid> with your netid. If this is the first time using it, it will ask if you want to accept the certificate. You should type ‘p’ to permanently accept it.
It will then prompt you for your password. Use your AD password to login. This is the password that you use to login to the machines in Siebel.
There should be no output at this point since there is nothing in your repository yet.
Create a subdirectory in your current workspace. To see where you are currently in the filesystem type pwd. To create the subdirectory type mkdir grocery. Change into that directory using cd grocery.
Create two files in the grocery directory.
list1.txt
apples
oranges
peach
strawberries
bananas
list2.txt
tomatoes
celery
potatoes
cucumber
lettuce
Now we add the grocery project to the repository. Make sure that you are in the grocery directory before issuing this command!
svn import . https://csil-projects.cs.uiuc.edu/svn/cs242/<netid>/grocery -m "importing grocery project" [There is a dot after import]
It should reply with:
Adding list1.txt Adding list2.txt Committed revision xx. where xx is some number.
Notice that you do not need to type your username or password anymore. Subversion stores this information in a encrypted file in ./subversion.
The -m “some text here” is the text that you describes what you are doing. Ideally, whenever you make any changes, you can leave a short text telling yourself why you make that change. Do not list the changes since Subversion can automatically infer that.
This would be a good time to talk about the xx number. Subversion maintains a global revision number. That means that any time you add files to repository (whether it is for the first time like we did here or when we check in a modified version of the file) Subversion gives the entire repository a new number. This number helps you revert back to a particular state of the repository.
And this can be a bane for Subversion in this class. Since we have about 40 students, each time a student adds a project2, the revision number is going to increase. This can make it very hard to find the revision that you actually want.
You can go to https://csil-projects.cs.uiuc.edu/svn/cs242/<netid> in your web browser to see that you indeed have those files.
At this point, your files are in the repository. You can safely delete the grocery directory and the files list1.txt and list2.txt within it. In fact, do that now. Since deleting files is risky business, I suggest you go do it in Windows Explorer or the Finder. If you already know the command for doing it in the command line then do it.
Checking a project out is taking out all the files that you have added from a particular directory in the repository. We will be checking out the files from the grocery directory.
svn checkout https://csil-projects.cs.uiuc.edu/svn/cs242/<netid>/grocery localgrocery
Subversion will reply with:
A localgrocery/list1.txt A localgrocery/list2.txt Checked out revision xx.
In this case, we checked out list1.txt and list2.txt into the localgrocery folder.
Go to the localgrocery directory using cd localgrocery to make sure that the files are there.
In the localgrocery directory, you are going to modify list1.txt. Open list1.txt in your text editor and edit it so that it looks like this:
apples
oranges
peach
strawberries
bananas
grapes
melons
Now in the command line, type svn status. You should see:
M list1.txt
The ‘M’ stands for modified and it means that Subversion knows that our local copy (the one on our computer, not the repository) has been modified.
In fact you can inspect the changes using svn diff list1.txt
Index: list1.txt =================================================================== --- list1.txt (revision xx) +++ list1.txt (working copy) @@ -2,4 +2,6 @@ oranges peach strawberries -bananas \ No newline at end of file +bananas +grapes +melons \ No newline at end of file
Depending on how you type you list, the text displayed will probably be slightly different.
Now we want to checkin the modified copy of list1.txt into the repository.
In the localgrocery directory, type svn commit -m "Need to buy more fruit "
And Subversion will reply with:
Sending list1.txt Transmitting file data . Committed revision xx.
The commit function is used to save any changes we’ve made back to the repository. As before while we were adding the files to the repository, the -m option is used to attach a meaningful message to the changes. If you omit the -m, then an editor (usually vim in unix based machines) will pop up for you to write the message. It is not a good habit to leave the message blank.
You can go to https://csil-projects.cs.uiuc.edu/svn/cs242/<netid>/grocery in your web browser to see that you indeed have those files.
Or you can use the log command to see the list of changes made so far by typing svn log list1.txt
------------------------------------------------------------------------ rxx | <netid> | 2006-09-05 23:28:27 -0500 (Tue, 05 Sep 2006) | 1 line importing grocery project ------------------------------------------------------------------------
For this part, we are going to have two copies of the grocery project on our computer. The new one would be called localgrocerydup. We will make changes to list2.txt in both localgrocery and localgrocerydup.
First we need to checkout localgrocerydup from the repository. Make sure that you are not in the localgrocery directory. You can go to your home directory by typing: cd ~. Then do: svn checkout https://csil-projects.cs.uiuc.edu/svn/cs242/<netid>/grocery localgrocerydup
Subversion will reply with:
A localgrocerydup/list1.txt A localgrocerydup/list2.txt Checked out revision xx.
In localgrocery make the following changes to list2.txt
tomatoes
celery
potatoes
cucumber
lettuce
endives
asparagus
The commit those changes: svn commit -m "Adding more vegetables"
Now, in localgrocerydup, type: svn status --show-updates
Subversion tells you that:
xx list2.txt Status against revision: xx
So that tells you that there are some difference between list2.txt in localgrocerydup and the latest file in the repository (that is what HEAD stands for). To see the actual difference, issue this command: svn diff -rHEAD list2.txt. Here is a sample output
Index: list2.txt =================================================================== --- list2.txt (revision xx) +++ list2.txt (working copy) @@ -2,6 +2,4 @@ celery potatoes cucumber -lettuce -endives -asparagus \ No newline at end of file +lettuce \ No newline at end of file
In localgrocerydup, do svn update.
Subversion will reply with:
U list2.txt Updated to revision xx.
The ‘U’ stands for updated. If you open list2.txt in localgrocerydup you notice that ‘endives’ and ‘asparagus’ has been added.
Here we see the abilities and limitations of Subversion.
Assuming that you have done the steps above, we are going to simulate the situation where two developers are working on the same file at the same time.
In localgrocery, edit list2.txt so that it looks like this:
carrots
tomatoes
celery
potatoes
cucumber
lettuce
endives
asparagus
In localgrocerydup, edit list2.txt so that it looks like this:
tomatoes
celery
potatoes
cucumber
lettuce
endives
asparagus
cabbage
Now, in this simulation, the changes made in localgrocery gets checked in first. So, in localgrocery do svn commit -m "Added more important vegetable"
Subversion tells you that it has received the changes:
Sending list2.txt Transmitting file data . Committed revision xx.
Now in localgrocerydup, do this svn commit -m "Added less important vegetable"
Subversion replies with this verbose message:
Sending list2.txt subversion/libsvn_client/commit.c:873: (apr_err=160024) svn: Commit failed (details follow): subversion/libsvn_ra_dav/commit.c:570: (apr_err=160024) svn: Your file or directory 'list2.txt' is probably out-of-date subversion/libsvn_ra_dav/util.c:389: (apr_err=160024) svn: The version resource does not correspond to the resource within the transaction. Either the requested version resource is out of date (needs to be updated), or the requested version resource is newer than the transaction root (restart the commit).
Basically, it tells us that our copy of list2.txt in localgrocerydup is out of date. So what we do is svn update. Here is the result:
G list2.txt Updated to revision xx.
The ‘G’ tells us that Subversion successfully merged the difference between our local copy and the version in HEAD. This is a case of automatic merger.
Now, remember that even though it has been merged, we have not yet committed the changes in the list2.txt file in localgrocerydup. So do svn commit -m "Added less important vegetable".
Next we are going to simulate a case where it is not possible for Subversion to perform an automatic merge.
Go back to localgrocery and do svn update. This ensures that both localgrocery and localgrocerydup have the latest version. You should verify that this is indeed so before proceeding. The contents of list2.txt in both localgrocery and localgrocerydup should look like this:
carrots
tomatoes
celery
potatoes
cucumber
lettuce
endives
asparagus
cabbage
Now, in localgrocery, change carrots in list2.txt to CARROTS. Now do svn commit -m "Put emphasis on carrot"
In localgrocerydup, change carrots in list2.txt to Carrots. Now do svn commit -m "Emphasize importance of carrot" and watch it balk with the following message:
Sending list2.txt subversion/libsvn_client/commit.c:873: (apr_err=160024) svn: Commit failed (details follow): subversion/libsvn_ra_dav/commit.c:570: (apr_err=160024) svn: Your file or directory 'list2.txt' is probably out-of-date subversion/libsvn_ra_dav/util.c:389: (apr_err=160024) svn: The version resource does not correspond to the resource within the transaction. Either the requested version resource is out of date (needs to be updated), or the requested version resource is newer than the transaction root (restart the commit).
Now, if you try doing a svn update in localgrocerydup like we did before, you get:
C list2.txt Updated to revision xx.
The ‘C’ means that there is a conflict. Files marked as ‘C’ cannot be checked in until the conflict has been resolved. Here are the necessary steps to resolve the conflict.
Open up list2.txt in localgrocerydup:
<<<<<<< .mineThe lines with the <<<<<<< and >>>>>>> show where the conflict occurred. Between them we can see both our change and the conflicting change in the repository.
We decide that we like the change to be ‘Carrot’ instead of ‘CARROT’. So we edit the file above to look like this:
CarrotHaving removed the conflict markers, we can tell Subversion we’ve resolved the conflict and then commit the file: svn resolved list2.txt and then svn commit -m "Emphasize importance of carrot". You have now successfully committed the file.
It is unlikely that you will only be working with the files that you imported into the repository at the beginning of the development cycle. You will be constantly adding files as you develop your application. Subversion has a command for that: svn add.
In our example, we want to add list3.txt to the repository. Create list3.txt with the following contents:
beefNow you need to tell Subversion that you want it to start keeping track of list3.txt. Type svn add list3.txt. Subversion replies with:
A list3.txt
The 'A' stands for added. Now, you need to commit the file into the repository. You do it using the normal command that we have been using: svn commit -m "Added list of meats". And you get:
Adding list3.txt Transmitting file data . Committed revision xx.
You can always add a list of file using the wildcard * operator. For instance, svn add *.cpp will add all the current cpp source code to the list of files that Subversion has to take care of.
Sometimes, we do not want Subversion to keep track of certain files anymore. This could be because we decided to remove that source code from our project or because we realize that we have moved the contents of the file to some other file.
For this example, we are going to remove the file we just created. Type svn delete list3.txt. You will get:
D list3.txt
The 'D' stands for deleted. However, you have only deleted the file from your working directory. To make the change to the repository, you need to type svn commit -m "Discovered that I want to be a vegetarian". And you get:
Deleting list3.txt Committed revision xx .
At this point, it is useful to clarify what we mean by removing. Say that we have list3.txt in revision 25. Now we remove list3.txt from the repository using svn delete list3.txt. What this does is remove list3.txt from all future revisions. Revisions 25 (and previous revisions up to the time list3.txt was added) will still have list3.txt inside it. So if we do a svn checkout -r25 https://csil-projects.cs.uiuc.edu/svn/cs242/nchen/grocery/ . we will still get that file back. Subversion never forgets what you put into it.
If you really want to delete a file you need to go through an elaborate scheme as described here.
Sometimes, you need to change the name of your repository. For instance, like in our example, I decided that I really do not like the name grocery for the repository. What I want is to call it shopping instead. Now, a naive way of doing it would just be to checkout everything and recommit as a new repository. This is not a good idea for two reasons:
What you want to do is actually copy everything in the grocery repository to the new shopping repository AND delete the old grocery repository. Fortunately, Subversion has one command that does both of that: svn move.
Here is how we use it for our example:
svn move https://csil-projects.cs.uiuc.edu/svn/cs242/<your_neitd>/grocery https://csil-projects.cs.uiuc.edu/svn/cs242/<your_neitd>/shopping -m "Moved grocery to shopping for clarity"
This time, Subversion just replies with a simple
Committed revision xx.
It is a good practice to not add binary files to the repository. Binary files cannot really be “versioned” since there is nothing to be compared except their time stamps. Also, they just take up space in the repository. But it is not just binary files that should be ignored. Log files and all temp directories should also be ignored since they do not contribute to the development process for other people.
In our shopping example, let’s create a new file called list3.txt with the following line:
This is a lousy list that I do not want anyone to see
Save the file and do svn status. Subversion replies with:
? list3.txt
So far Subversion reports that it is a new file and there are no previous revisions of that file yet. So we can do this to have Subversion ignore it: svn propset svn:ignore list3.txt . Then you can commit your file using svn commit -m "Ignored the list3.txt from all future commits".
You can see the properties that svn propset has set on the current repository by doing svn proplist .. You get this in return:
Properties on '.': svn:ignore
There can be multiple properties from the above command. To view the files affected by the svn:ignore property, you do: svn propget svn:ignore .. This is what it replies with:
list3.txt
What if you want to add list3.txt back to the repository? Meaning that you do not want to ignore it anymore? You can do that with svn propdel like so: svn propdel svn:ignore list3.txt .. And you see this in the console:
property 'svn:ignore' deleted from '.'.
Now when you do svn status, you see that Subversion reports that:
? list3.txt
svn propset svn:ignore has some convenient functions such as pattern matching for file names so that you can tell it to just ignore files with extension .bin. You can find more information from here.
If on the other hand, you accidentally added a file to the repository already, you can do a svn delete first on the file and then do the svn propset command above to ignore it in future revisions. The nice about svn:ignore is that there is less “noise” when you do a svn status on the repository. Subversion does not report the status of the files that you plan on ignoring.
svn propset is a pretty useful command for setting other “metadata” on your files as well. This helps you perform nice things when you need to intelligently select files.
Some IDEs allow you right-click on a file in the navigator pane to set the svn:ignore property on it. This is pretty convenient as well but at least now you know how to do it from the command line as well.
When you turn your work in, you do not want to include all the hidden subversion files. What you want to do is commit your final changes and then do:
svn export -rHEAD https://csil-projects.cs.uiuc.edu/svn/cs242/<netid>/<project> <new directory name>. You can also export an earlier revision by specifying the revision number instead of HEAD.
svn help <command> in the command line.svn export before you hand your code to someone else.svn status—show-updates to see what has changed 1 Maybe that is too optimistic. It only allows undo if you tell it to remember a particular file. Otherwise it will not keep a list of all the modifications to the file.
2Well actually any form of commit will increase the revision number.