Very simply, this article covers the basics of how to roll back an entire project hierarchy to a particular label. This is accomplished by using a combination of the SourceSafe API (SourceSafeTypeLib) and the SS.exe command line utility.

I have been programming for a short 4 years. Over the last 1/2 year, I have "discovered" CodeProject and the wonderful resources this site has to offer. This is my first CodeProject article, written with the intention of:

learning to write articles on CP;

passing some information back to this community that has given me help;

offer some insight on a task in Visual Source Safe that doesn't seem to have a lot of help around it.

I was trying to upgrade my version of Microsoft Pattern and Practices team's Enterprise Library code. I had the January release and had made modifications to that; I then wanted to merge in the June release, keeping my changes. Of course, I did it wrong the first time, and wanted to roll the entire project back to a label (which I had thankfully thought to put on before I tried to merge).

I researched on Google and found that what I wanted to do could not be done, at least not in the way I expected to do it. You can use the command line tool (described later in this article) to roll back all files in a project; however, you could not recurse a project tree. And if any of you have seen Enterprise Library, there are about 4-6 levels in the hierarchy. There are about 38 projects or so. Yikes!

So, as any developer would, I decided to come up with my own solution, and pass that on to you. Here are a couple of ways you may find this article useful.

Recursive rollbacks are not supported by the VSS Explorer

In the Visual Source Safe Explorer, you cannot roll back a project or a project tree (i.e. recurse). You are forced to roll back file-by-file, which gets tedious even in small projects. My sample code can be used as a lightweight tool that will help you to automatically recurse through a project tree and roll back all the files.

It is important to note that behind-the-scenes we are still rolling back file-by-file; however, we are automating this by looking at the project tree in VSS, so you do not have to roll back each project or file yourself.

Automation tutorial

This article also serves as a very basic VSS automation and SS.exe command line tutorial. There are actually quite a few things you can do with VSS automation (that are not covered in this article) such as:

Automated builds

Automated labels

Advanced reporting

Automated change logs or email notification

See the References section at the bottom of this article for some handy articles that discuss the many uses of SourceSafe automation.

I am trying to pass on what I have learned, but I have only been programming for a short time. It only stands to reason that there are bigger and better coding practices out there that I do not follow, or technology that I am not aware of.

I encourage you to use the comments section at the bottom of the article to ask questions, and to let me know about ways in which I can improve this article.

This tool will roll back versions of your SourceSafe files to a certain label or version number. To my knowledge, there is no way to recover these files once you have rolled back. I strongly recommend that you back up your SourceSafe database before using this or any tool that interacts with SS. The author will take no responsibility for lost or destroyed files, or any loss of business resulting from the loss of said files. By downloading the sample project, you agree to abide by these terms.

Please look at this code first; it would be better if you first try this on a test SourceSafe database. I do not provide the executable as a download, as a small precaution that will encourage you to open the project (I hope).

This article assumes you have Visual SourceSafe 6.0 and Visual Studio .NET 2003. (I have not tried this with VS 2002). I am running this code on a Windows XP Professional machine with service pack 1, and have not tested with other platforms. I would appreciate any feedback from anyone using VS 2002 or running the code on other platforms.

The two files we will be working with are SS.exe and SSAPI.dll. If you have installed SourceSafe using the default settings, they should both be located in this directory:

Below is an explanation of the business problem, some approaches I took to solve it, and some explanation of the actual code. To see how to use this program from a UI perspective, see the section "Using the Sample Program".

In June, they announced a new release which fixed some bugs and which also had some support for Visual Studio 2005. I wanted to merge this new release into my old changed release, and frankly I didn't really know a "good" way to do it. So, taking a precaution, I put a label on my updated January 2005 version that was in SourceSafe.

Good thing I did.

Don't ask me what I did wrong (how could someone mess up copying and pasting files?) but I had the distinct impression that I had messed up my projects. No problem, I thought: I had just put a label on the old, good, version; I'll just completely roll back to that label, and start over. That should be quick. [Insert evil chuckle here]

I went in to SourceSafe Explorer, and immediately realized I could not roll back a whole project to a label or version. I could only roll back file-by-file. So I went looking on Google and finally came to an article which mentioned the following:

While the rollback command does not have a recursive option, you can at least use it to specify all files in a given project. Therefore, you'd have to walk through each project in your database yourself. Here's an example:

ss rollback $/proj1/* -vlbeforechange -i-y

I found out that this person was referring to a command line tool, SS.exe, which I already had in the SourceSafe directory. The problem was - well take a look at this project hierarchy:

Each one of those folders you see has several levels such as the expanded Caching/Configuration project shown. If I wanted to roll back the entire project hierarchy using this methodology, I would have to run it for each folder.

I figured I could just use the System.IO.DirectoryInfo object to get a starting directory on my file system, and then use directoryInfoObject.GetDirectories() in a recursive manner to get all of the sub-folders. The problem with this is that I was relying on the Working Directory to contain everything that was in the SourceSafe tree. In other words, I would be using my file system directory structure to get the hierarchy, and not the actual SourceSafe project structure.

I then started to research if there was a way to get the hierarchy right out of SourceSafe using the command line utility. As I was searching, CodeProject's Marc Clifton turned me on to an article about the SourceSafe API.

I figured, now, that I'd just use the SourceSafe API and trash the command line utility. I could get all of the projects and files using the API. All I had to do was recurse through all of the files, and roll back each file individually, right?

I started happily coding down this path, and when I got all the way through setting up my recursive function, checking for errors, setting up an acceptable UI, and setting up confirmations for the user, I then went to code the next line to do the actual rollback: I was thinking it would be something like vssItemObject.Rollback("LabelName");. The VssItemObject has the following methods:

After pounding my head on the desk for a few minutes, I decided to take a bit of both worlds: I could use the API to get the projects and files, and I could use the command line utility to actually perform the rollback. In the next section, I will very briefly discuss some of the code I ended up using in my "final" (beta?) version that I've included here.

Recurse - true if you want to recurse every file in the selected project's hierarchy.

RollbackType - Roll back to a label or to a version number.

ConfirmOption - Confirm rollback once only, every time, or not at all.

RollbackCriteria - The actual label or version number to roll back to. If you wish to roll back to label 'My_Label', enter My_Label without quotes. If you wish to roll back to each file's version 4, enter 4.

GetLatest - true (default) if you want to replace your working folder's copy of the file after you roll the file back.

We could have used a struct here, but that would force you to set each and every property when instantiating a new VSSOptions object, which looks a bit messy in code. Code maintainability won out over the miniscule (if any?) performance increase. We've already lost tons of performance on building the project TreeView, which you'll see later in this section.

On another note, I originally had all of the properties in this class listed as public variables. I was advised not to do that, and to always use public properties, for the following reasons:

Public properties are a part of what is required to support data binding.

Extensibility

Supports declarative instantiation

Promotes encapsulation

Avoids entanglement and dependencies

I don't pretend to understand all of this, but I had never heard good arguments before against using public variables in place of "simple" getters and setters.

OpenSourceSafeDatabase method

This method is responsible for picking a srcsafe.ini file (which represents a SourceSafe database), gathering login credentials, opening the actual database, and populating the TreeView you see on the right-hand side of the UI.

The SetOpenFileDirectory() method is a helper method which searches a registry key to see if you have opened up a SourceSafe database before. If you have, the openFile component's InitialDirectory property is set to that path. If you have not, we try to default to C:\Program Files\Microsoft Visual Studio\VSS. Finally, if all else fails, default to the Program Files directory.

The Open File dialog is then opened, and if the user cancels or enters a blank file name, we simply stop and don't do anything.

//////////////////////
// Next, Get the login information for the database
frmLogon loginForm = new frmLogon();
if (loginForm.ShowDialog(this) != DialogResult.Cancel)
{

frmLogon is a very simple form which collects the credentials for the SourceSafe database. It exposes two public properties which retrieve what the user types in.

The VSSDatabaseClass is the key class here from the SourceSafe API that allows you to work with the database. We provide the credentials and open the database, and then we store the database you just opened so that we can try to open the same file next time.

BuildTreeView() populates the TreeView control on the right of the UI with the hierarchy of the database you just opened. We will discuss that function shortly. When the TreeView is properly populated, we show the panel that allows the user to actually run the rollback.

Finally, we catch exceptions. We trap specifically for two particular exceptions, "User Name Not Found" and "Bad Password", because that is a bit of a bad practice - we do not want to give the user that information. We can also trap here for any other error thrown specifically by the SourceSafe API, and then we trap for any other exception resulting from our code.

In any exception case, we don't show the main panel, and we update the status bar showing that we're not logged in.

BuildTreeView method

The intention of the BuildTreeView method is to populate the TreeView control, on the right hand side of the UI, with the hierarchy of the database just opened. We have to show folders (projects) first and then files, so I have chosen to build a data table with all of the information necessary for building and sorting the tree.

We use the vssDB.get_VSSItem method to retrieve the Root item of the database, and then the vssProj.get_Items method to retrieve all files and projects of the Root. Using the BuildNode helper method, all levels of projects are traversed recursively and added to the data table, which has been passed in By Ref. Finally, we sort the data table, cycle through the sorted rows, and add nodes to the TreeView accordingly.

The problem I've run in to is that this method seems to take far longer than expected with a database of any significant size. I have tried a methodology where I did not return a data table and instead returned nested TreeNodes directly; however, taking this approach, I'd have to cycle through the VSSItem twice, once looking for projects, and a second time looking for files. It seems that the vssProj.get_Items method itself is very slow.

Because this hierarchy takes so long to build, I have given the user the option to "turn off" the TreeView feature. See the Using the sample program section below.

RecursiveRollback and RollbackNode methods

When the user clicks the cmdRollback button, we first set up our VSSOptions object with their choices, such as label name or version number, and whether to get the latest version after rollback. After the options are set, the RecursiveRollback method is called, which does the actual work, and returns a string which can be displayed in the message box. This string will hold error messages, for instance in a case where a file could not be rolled back.

The first step, after confirming the rollback to the user, is to get the project that is in the TextBox from the user having typed it in, or from the user having selected the project from the TreeView.

// Get the project as listed in TextBox
VSSItem vssProj =
vssDB.get_VSSItem(options.SelectedProject, false);

We set up a global Boolean variable that will catch if the user wants to cancel the rollback, and we call the helper method RollbackNode which recursively performs the rollback.

The method checks to see if rollback is cancelled, and then sets up a StringBuilder variable to hold the current iteration's error messages. The type of the VSSItem is checked, to see if we are working with a project or a file.

If the item type is 0, then the object we are on is a folder/project. We take no action on the project itself other than to recurse through all of its children. If the user has specified that they do not want to recurse, we simply return an empty string. (Not recursing pretty much defeats the purpose of the project, but the option is there anyway.)

If the user has specified that they want confirmation on each and every file, we construct a confirmation dialog for the current file. The user can choose to roll the file back or not to roll the file back, or to cancel completely out of the rollback operation. It is important to note that any rollbacks already performed will remain in effect.

A command line string is constructed to run the SS.exe program, giving it the proper settings to roll back the file with the user's options. If the rollback type is "Label", a "V" and an "L" must be prepended to the argument whereas if it is a version number, just the "V" gets prepended. Also, spaces in the label name must be accounted for.

For example, to roll back MyFile.cs to the version labeled "Release31" and get the latest version of the file to its working directory, the command line would look like this:

rollback "MyFile.cs" -VLRelease31 -I-Y

To roll back MyFile.cs to the version number 5 and not get the latest version of the file to its working directory, the command line would look like this:

rollback "MyFile.cs" -V5 -G- -I-Y

Finally, to roll back MyFile.cs to the version labeled "Release Version 2.5" and get the latest version of the file to its working directory, the command line would look like this:

rollback "MyFile.cs" "-VLRelease Version 2.5" -I-Y

You will notice that when a label has spaces, we put quotes around the entire argument including the switch indicator (-).

Lastly in the RollbackNode method, we capture (and ignore) extra warnings thrown by the process, and return any other error string returned from the Process object.

Type in the label name or version number in the Roll Back To text box.

To roll back to a labeled version (default), click the radio button marked Label. To roll back to a version number, click the radio button marked Version Number.

To confirm the rollback operation once in the beginning of the rollback, select Confirm Once in the Confirmation Mode dropdown. To confirm every file before rolling back, select Confirm Every File. To forgo confirmation (silent rollback) [not recommended], select Do Not Confirm.

To get the latest version of a file into the working directory of the project after rollback (default), check the checkbox marked Get Latest Version.

Press the Roll Back button when you are ready to roll back your files.

Results of the rollback appear in the text box at the bottom of the screen.

In summary, we end up with a rather slow and clunky program, but a program that can recursively roll back a project hierarchy using the SourceSafe project structure directly.

While I wouldn't recommend using this for simple rollbacks, we also see by looking at the API and command line utilities that there are other possibilities for exploring advanced capabilities when the SourceSafe Explorer does not meet our needs. Automated change log creation, based on labels or comments, for example, might be a good application of the API. You could create automated bug-fix logs, you could link SourceSafe checkin versions to bug tickets; the possibilities are certainly there.

I'd be interested in finding out if the next version of SourceSafe and/or Team System makes source control any easier. Do these systems address some of the problems we've covered here? Do they provide any advanced features or functionality that we don't see today in SourceSafe 6.0?

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Comments and Discussions

I have try to run this program to rollback my files to the previous version. However, it is not working. The version remain the same even I have click hundreds of times on the rollback button. I need help here.

I have answered the most recent comments by replying directly to them via CP email. Thanks to everyone who reads the article and posts comments. As I mentioned to the recent posters, this article is probably obsolete by now as the technology has come a long way.

I had switched jobs and thus lost track of the article. I can now be reached under this new codeproject ID. Thanks for reading.

I am not an expert programmer, but a quick crack at the treeview brought me from 11 min and 37 sec on my database to 2 min 20 secs.

It was with removing the datatable and just building it recursevely on a treeview. That it is still not satifying, but removing the datatable gives a whole lot of performance. Plus i haven't figured out, if there actually is stuff that the program needs to do, that can't be done in a treeview.

My soulution is fare from good enough, that is why I would probarly change the tree view to only open the project folder and showing the internals, when it is clicked, so that there is not recursed through something, that is not needed to be shown in the user view.

I'm not sure, but I think you can just rollback checked-in files.When one file was not changed (not checked-in) its latest label is not changed, unless you rollback to the previous version of the check-in. In this case, you might get different labels assigned to the same project files ;^)

If I were in this situation (I, too, don't believe in destroy/rollback in a revision control system), I would simply check-out the label; copy the files over-top then check the files in. What this does is compare the file based on the version that was checked out (by label) then merges the changes as if they part of the history after then label--informing you of any conflicts.

To do this:* Show history of project (make sure "Include Labels" is checked; uncheck "Include file histories" and check "Labels only", if your label might not be near the top of the list)* In the History dialog, Select the label you want to use as the base.* Then press the Check Out button.* Copy the new files over-top the existing ones.* Then check-in the changes (by selecting the project and selecting check-in ensure "Recursive" is checked)

This merges the new files with the versions at the label, then merges all the changes since that label.

Yes, the concept you're talking about is discussed as a "Virtual Rollback". I put a link to this in my references section (Virtual Rollback[^]) but I think I should have made a note of it in the article.

Thanks very much for putting the step-by-step instructions here. With your permission, I would like to incorporate this into the article upon my next revision.

I think I probably didn't read your post carefully enough . I think what I was trying to get at by saying "Virtual Rollback" was something of this nature (which seems very close to what you are saying):

- Check out All files, recursively- View History, labels only- Click on version in question, and select "Get"- Choose Recursive, Make Writable- [examine files if necessary]- Check in all files, recursively, with optional comment.

I wasn't thinking of any Merging in my case, because the point of this particular example, anyway, was to get exactly back to the state where you were at a certain time.

In any case, it's always great to have several different ways of doing things, to suit everyone's need!

That was a very informative and well written article. I give you an "A". However, I have been recently tasked with designing a recursive VSS rollback algorithm in C++, and found that your SS.EXE approach for performing the actual programmatical VSS rollback operation VERY PAINFUL to implement. After an exhaustive search all over the internet, I discovered that Microsoft VSS COM Automation does support rollback in a very limited way. Could it somehow be related to the Action property of the IVSSVersion object? It's well hidden but it's there. Check out this MSDN link.

It would be great if all that clunky code that formats the SS.EXE command could be replaced by a couple of lines of COM automation code. My recursive algorithm that walks the VSS database tree was already generating the pVersion object, so I added the following two lines of C++ code. CComBSTR bszAction("Rollback to version"); pVersion->get_Action(&bszAction);

But too bad it doesn't perform the actual rollback! There has got to be a better way.

I am writing to you in response to a comment you put on an article in CodeProject back in June of 07. I had switched jobs and lost track of this "old" article!

Yes - the thing I noticed about this whole implementation was that the whole thing was painful . Thanks so much for looking into it, reading the article, and responding. Here's hoping that you're working now with newer technology that you won't have to go through these pains again!

Nice to hear from you. Unfortunately I have been working on a project in which I am forced to use Visual SourceSafe Automation. In the end my rollback implementation looked quite similar to yours. Thanks again. Visual SourceSafe is a sad reality for us, but it can be made to work. We are hoping one day to migrate our source control database to something better, like perhaps SubVersion, but right now it's only a pie in the sky.

You're correct, a rollback of that magnitude might be a last resort - but I also wonder what else I can do with the automation. For instance, do you use the Report feature when you call for history of an item? It is messed up big time - it's textual and fixed width, and a pain to "import" into Excel.

And funny you mention it, because Jelly Belly is the one who owns Bertie Botts[^]!

Source Safe have a bad history of bugs, and data corruption. VSS tends to corrupt the database, and there is better and more portable solutions like CVS or SVN, with good integration with Visual Studio.

I have found that there are cases in which you just have to do the best with the tools that you have. Not being able to switch systems is not always a matter of money or any kind of resistance to change; sometimes it's just that when you weigh where your time is best spent for larger projects, sometimes changing non-broken systems gets put by the wayside.

Susan, for your own sake better don't use SourceSafe any more. Your consideration about weighting your time is just wrong. You always waste your time with Source Safe. Let's count some of your time. I don't think your projects are about 2 days long (otherwise you might choose not using any Revision Control system). Switching to Subversion will hardly take more than two days, after which you will remember VSS as a nightmare. VSS may also force you wasting even more time; it is not just bad, it's a sort of crime. Just one thing -- submissions are not transactional -- should close the topic.