A good version control system is about more than giving you access to old versions of your files. If that was all you needed, then periodic zip files could meet your needs perfectly well. Ideally, you should be able to use Vault to answer the question "How did my source file get into this state?" Vault should help tell the story of your source code. Like I said
earlier, in this series of articles, I hope to provide Vault users with tips and utilities to help leverage the history they have in Vault to answer complex questions and to analyze that history in an automated way that answers the questions before you can ask them.
Mmmm... Donuts
Here at Sourcegear, there are strict punishments for any developer who checks in a change which causes Vault compilation errors. The law states that if you break the build, then you must bring in donuts the next morning. This means that when the automated build system sends out an email that says "Vault could not be built", the sprinkle-deprived masses have but one question on their minds... Who's bringing donuts?
Before Vault 2.0, the process went something like this:
1. Read the output log in the email to determine which file is broken.
2. Load up the Vault client and look through that file's history to see who's been changing it recently.
3. Try to catch the guilty party before they can sneak out.
With the introduction of Blame in Vault 2.0, step 2 is now a lot faster, giving the hope that you may even be able to confront the guilty party in their office instead of the parking lot. That's great, but it would be better if the email from the automated build system said who broke the build. Then, it's just a matter of whose email client has the lowest refresh time.
Blame
The most familiar mode of blame one is the GUI mode, where each line in your source file is displayed with notations for the user and version in which that line last changed. Ihe mode that I'm showing here is also available in the Vault command line client, but I'm reimplementing it here so that I can discuss the steps that are being taken.
We'll start with the template that we created earlier, and add three steps that are necessary to get the user who broke the build.
1. Parse the broken file and line number from the Visual Studio output.
2. Convert the disk file path to a repository path.
3. Login to the Vault server and request the blame for that file, looping until we find the broken line.
Parsing the Output from a failed build.
The following assumes that your automated build calls Visual Studio like this:
"c:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe" /rebuild release /out build.log Solution.sln
When a build fails, a line like this will appear in the build.log output file.
c:\blah\outputparser\class1.cs(16,4): error CS0246: The type or namespace name 'intt' could not be found ...
The two items that we care about are the first things on the line, the filename and the line number. Here's an excerpt from the
complete solution that parses the build log file to find the first line that failed.
string currentLine = null, brokenDiskFile = null;
int lineNumber = 0;
System.IO.StreamReader sr = new System.IO.StreamReader(logfile);
//This regex looks for lines like C:\Path\To\Class1.cs(16,4): error
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(@".*\([0-9]+,[0-9]+\)\: error ");
while ((currentLine = sr.ReadLine()) != null)
{
if (reg.Match(currentLine).Length > 0)
{
int ParenPos = currentLine.IndexOf("(");
//Everything before the paren is the file name
brokenDiskFile = currentLine.Substring(0, ParenPos);
//The part between the paren and the comma is the line number.
int CommaPos = currentLine.IndexOf(",", ParenPos);
lineNumber = int.Parse(currentLine.Substring(ParenPos + 1, CommaPos - ParenPos - 1));
break;
}
}
sr.Close();
Requesting the blame for the file from VaultHere's another small excerpt that converts the disk path to a Vault repository path. I'm taking a shortcut and assuming that the disk folder that is the root of the source tree and the repository folder that corresponds to that disk folder are hard coded into the app, or passed in as command line arguments.
brokenDiskFile = brokenDiskFile.ToLower();
string brokenFileInRepository = brokenDiskFile.Replace(workingFolderPath, repositoryPath);
brokenFileInRepository = brokenFileInRepository.Replace("\\", "/");
Now given the path to the broken file in the repository, we just need to ask the server for the blame output for the file and loop over the output until we find the line that is broken
VaultBlameNode[] blames = null;
//Generate the blame from version 20 versions ago to the latest version.
myClient.Connection.Blame(myClient.ActiveRepositoryID, brokenFileInRepository, -20 /*shortcut to say "20 versions before the latest one"*/, -1 /*shortcut to say "the latest version"*/, ref blames);
foreach (VaultClientNetLib.ClientService.VaultBlameNode bn in blames)
{
//Blame lines are zero based, so we have to bump the firstline for every blame
if ((bn.FirstLine + 1) <= lineNumber && ((bn.FirstLine + 1) + (bn.CountLines - 1)) >= lineNumber)
{
Console.Out.WriteLine("User " + bn.UserName + " last changed line " + lineNumber + " of " + brokenFileInRepository + " with the comment:\r\n" + bn.Comment);
break;
}
}
Now that we have an automated way of determining who broke the build, all we need is a donut store that has a web service for ordering donuts. :).