Introduction
I have been using TortoiseSVN for a while now, and it has proven a great way to use Subversion in the Windows / Visual Studio environment.
I recently discovered how to use SVN Keywords in my projects. If you enable SVN Keywords, then every time you check in the project, Subversion scans your files for certain "keywords" and replaces the keywords with some information. For example, at the top of my source files, I would create a header containing the following keywords:
'$Author:$
'$Id:$
'$Rev:$
When I check this file into Subversion, these keywords are replaced with the following:
'$Author: paulbetteridge $
'$Id: myfile.vb 145 2008-07-16 15:24:29Z paulbetteridge $
'$Rev: 145 $
I thought that if I take the $Rev$
keyword, I could use this as part of my software version so that I could keep track of the version and the build when it is released. I would have a version that would look like 1.0.0.145.
The limitation of using the $Rev$
keyword is that it only gives you the revision of the file and not the entire project, so using $rev$
keyword cannot work as part of my software versioning.
Reading the Tortoise help files, I found a tool supplied with Tortoise SVN, called SubWCRev.exe.
SubWCRev is Windows console program which can be used to read the status of a Subversion working copy and perform keyword substitution in a template file.
This article shows you how you can use SubWCRev to embed the top level revision number into your projects.
Using the Code
The code performs three tasks:
- Creates a template file containing keywords that are read by SubWCRev.
- Runs the SubWCRev.exe which produces an output text file containing the Subversion information based on the keywords from the template file.
- Reads the output file and returns values from the file into an array, for use by your project.
The project attached is written in VB.NET, but I have also provided the C# implementation in this article.
Template File
The template file is created by this code and will contain all available commands that are read by SubWCRev:
Keyword | Description |
---|
$WCREV$ | Replaced with the highest commit revision in the working copy. |
$WCDATE$ | Replaced with the commit date/time of the highest commit revision. By default, the international format is used: yyyy-mm-dd hh:mm:ss. Alternatively, you can specify a custom format which will be used with strftime() , for example: $WCDATE=%a %b %d %I:%M:%S %p$ . For a list of the available formatting characters, look at the online reference. |
$WCNOW$ | Replaced with the current system date/time. This can be used to indicate the build time. Time formatting is as described above. |
$WCRANGE$ | Replaced with the update revision range in the working copy. If the working copy is in a consistent state, this will be a single revision. If the working copy contains mixed revisions, either due to being out of date or due to a deliberate update-to-revision, then the range will be shown in the form 100:200. |
$WCMIXED$ | $WCMIXED?TText:FText$ is replaced with TText if there are mixed update revisions, or FText if not. |
$WCMODS$ | $WCMODS?TText:FText$ is replaced with TText if there are local modifications, or FText if not. |
$WCURL$ | Replaced with the repository URL of the working copy path passed to SubWCRev. |
$WCNOW$ | Replaced with the current time and date. |
$WCNOW= | Replaced with the current time and date in standard format. |
$WCINSVN$ | $WCINSVN?TText:FText$ is replaced with TText if the entry is versioned, or FText if not. |
$WCNEEDSLOCK$ | $WCNEEDSLOCK?TText:FText$ is replaced with TText if the entry has the svn:needs-lock property set, or FText if not. |
$WCISLOCKED$ | $WCISLOCKED?TText:FText$ is replaced with TText if the entry is locked, or FText if not. |
$WCLOCKDATE$ | Replaced with the lock date. |
$WCLOCKOWNER$ | Replaced with the name of the lock owner. |
$WCLOCKCOMMENT$ | Replaced with the comment of the lock. |
VB.NET
Dim strTemplateFile As String = "SvnTemplate.txt"
Dim bAns As Boolean = False
Dim objReader As StreamWriter
Dim strData As String
strData = "$WCREV$" + vbCrLf + _
"$WCDATE$" + vbCrLf + _
"$WCNOW$" + vbCrLf + _
"$WCRANGE$" + vbCrLf + _
"$WCMIXED?Mixed update revision:Not mixed$" + vbCrLf + _
"$WCMODS?Modified:Not modified$" + vbCrLf + _
"$WCURL$" + vbCrLf + _
"$WCNOW$" + vbCrLf + _
"$WCINSVN?Versioned:Not Versioned$" + vbCrLf + _
"$WCNEEDSLOCK?Lock Required:Lock not required$" + vbCrLf + _
"$WCISLOCKED?Locked:Not Locked$" + vbCrLf + _
"$WCLOCKDATE$" + vbCrLf + _
"$WCLOCKOWNER$" + vbCrLf + _
"$WCLOCKCOMMENT$" + vbCrLf
Try
objReader = New StreamWriter(strTemplateFile)
objReader.Write(strData)
objReader.Close()
Catch Ex As Exception
Console.WriteLine(Ex.Message)
End Try
C#
string strTemplateFile = @"SvnTemplate.txt"
bool bAns = false;
StreamWriter objReader;
string strData;
strData = "$WCREV$" + Constants.vbCrLf +
"$WCDATE$" + Constants.vbCrLf +
"$WCNOW$" + Constants.vbCrLf +
"$WCRANGE$" + Constants.vbCrLf +
"$WCMIXED?Mixed update revision:Not mixed$" + Constants.vbCrLf +
"$WCMODS?Modified:Not modified$" + Constants.vbCrLf +
"$WCURL$" + Constants.vbCrLf +
"$WCNOW$" + Constants.vbCrLf +
"$WCINSVN?Versioned:Not Versioned$" + Constants.vbCrLf +
"$WCNEEDSLOCK?Lock Required:Lock not required$" + Constants.vbCrLf +
"$WCISLOCKED?Locked:Not Locked$" + Constants.vbCrLf +
"$WCLOCKDATE$" + Constants.vbCrLf +
"$WCLOCKOWNER$" + Constants.vbCrLf +
"$WCLOCKCOMMENT$" + Constants.vbCrLf;
try {
objReader = new StreamWriter(strTemplateFile);
objReader.Write(strData);
objReader.Close();
}
catch (Exception Ex) {
Console.WriteLine(Ex.Message);
}
Create the Revision Information
Once you have your template file, you need to run the command line tool SubWCRev.exe, passing in three parameters - the working directory of your source code, the template file location, and the location you want to store your output file containing your revision information.
VB.NET
Dim strRev As String = ""
Dim strTemplateFile As String = "svntemplate.txt"
Dim strRevOutputFile As String = "svnrev.txt"
If System.Diagnostics.Debugger.IsAttached Then
Dim dirInfo As New DirectoryInfo(Application.ExecutablePath)
Dim dirinfoSourceWorkingDir As DirectoryInfo
dirinfoSourceWorkingDir = dirInfo.Parent().Parent().Parent()
Dim strSourceWorkingDir As String = dirinfoSourceWorkingDir.FullName
SvnCreateTemplate(strTemplateFile)
Try
Dim p As New Process
With p.StartInfo
.UseShellExecute = True
.FileName = "subwcrev.exe"
.Arguments = """" + strSourceWorkingDir + """ """ + _
strTemplateFile + """ """ + _
strRevOutputFile + """"
.UseShellExecute = False
.RedirectStandardOutput = True
End With
If p.Start() Then
Dim output As String = p.StandardOutput.ReadToEnd()
Console.WriteLine(output)
p.WaitForExit()
End If
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End If
C#
string strRev = "";
string strTemplateFile = "svntemplate.txt"
string strRevOutputFile = "svnrev.txt";
if (System.Diagnostics.Debugger.IsAttached) {
DirectoryInfo dirInfo = new DirectoryInfo(Application.ExecutablePath);
DirectoryInfo dirinfoSourceWorkingDir;
dirinfoSourceWorkingDir = dirInfo.Parent().Parent().Parent();
string strSourceWorkingDir = dirinfoSourceWorkingDir.FullName;
SvnCreateTemplate(strTemplateFile);
try {
Process p = new Process();
{
p.StartInfo.UseShellExecute = true;
p.StartInfo.FileName = "subwcrev.exe";
p.StartInfo.Arguments = "\"" + strSourceWorkingDir +
"\" \"" + strTemplateFile + "\" \"" +
strRevOutputFile + "\"";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
}
if (p.Start()) {
string output = p.StandardOutput.ReadToEnd();
Console.WriteLine(output);
p.WaitForExit();
}
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
You now have the output file created, so next, you need to read this file into your application so you can start using the revision information.
VB.NET
Dim strContents As String = ""
Dim objReader As StreamReader
Try
objReader = New StreamReader(strRevOutputFile)
strContents = objReader.ReadToEnd()
objReader.Close()
aRevData = Split(strContents, vbCrLf)
Catch Ex As Exception
Console.WriteLine(Ex.Message)
If System.Diagnostics.Debugger.IsAttached Then
MessageBox.Show(Ex.Message + vbCrLf + vbCrLf + _
"This is probably because this project is not " + _
"under subversion revision control yet or the " + _
"output file was not created by ""subwcrev.exe""")
Else
MessageBox.Show(Ex.Message + vbCrLf + vbCrLf + _
"This is probably because the output file " + _
"""svnrev.txt"" has not been created yet - need " + _
"run this using the Visual Studio IDE first to " + _
"create the svnrev.txt file containing the SVN values!")
End If
aRevData(0) = """svnrev.txt"" file not Found"
End Try
C#
{
string strContents = "";
StreamReader objReader;
try {
objReader = new StreamReader(strRevOutputFile);
strContents = objReader.ReadToEnd();
objReader.Close();
aRevData = Strings.Split(strContents, Constants.vbCrLf);
}
catch (Exception Ex) {
Console.WriteLine(Ex.Message);
if (System.Diagnostics.Debugger.IsAttached) {
MessageBox.Show(Ex.Message + Constants.vbCrLf + Constants.vbCrLf +
"This is probably because this project is not under subversion " +
"revision control yet or the output file was not " +
"created by \"subwcrev.exe\"");
}
else {
MessageBox.Show(Ex.Message + Constants.vbCrLf + Constants.vbCrLf +
"This is probably because the output file \"svnrev.txt\" " +
"has not been created yet - need run this using the " +
"Visual Studio IDE first to create the svnrev.txt file " +
"containing the SVN values!");
}
aRevData(0) = "\"svnrev.txt\" file not Found";
}
}
I return the contents of the file into a string array called "aRevData
".
I pre-initialized this array first with values in case the template file is not created, or there was a problem somewhere. This would then allow you to use the pre-initialised values in your project if you are unable to get hold of the SVN values (an example is if you don't have Subversion installed or you haven't checked your project into Subversion yet).
Using the Revision Data
Wrapping this article up below shows you how I would use the code - please take a look at the project posted.
I use the variable "AppVersion
" in my About forms / Splash Screens etc., and as my software version for official releases of the application.
VB.NET
Dim aRevHeaders() As String = {"$WCREV$", _
"$WCDATE$", _
"$WCNOW$", _
"$WCRANGE$", _
"$WCMIXED$", _
"$WCMODS$", _
"$WCURL$", _
"$WCNOW$", _
"$WCINSVN?Versioned:Not Versioned$", _
"$WCNEEDSLOCK$", _
"$WCISLOCKED$", _
"$WCLOCKDATE$", _
"$WCLOCKOWNER$", _
"$WCLOCKCOMMENT$"}
Dim aRevData() As String = {"0", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}
Try
SvnGetlatestRev(aRevData)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
Dim strVersion As String() = Split(Application.ProductVersion, ".")
Dim AppVersion As String = String.Format("{0}.{1}.{2}.{3}", _
strVersion(0), _
strVersion(1), _
strVersion(2), _
aRevData(0))
C#
string[] aRevHeaders = {"$WCREV$",
"$WCDATE$",
"$WCNOW$",
"$WCRANGE$",
"$WCMIXED$",
"$WCMODS$",
"$WCURL$",
"$WCNOW$",
"$WCINSVN?Versioned:Not Versioned$",
"$WCNEEDSLOCK$",
"$WCISLOCKED$",
"$WCLOCKDATE$",
"$WCLOCKOWNER$",
"$WCLOCKCOMMENT$"};
string[] aRevData = {"0", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
try {
SvnGetlatestRev(aRevData);
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
string[] strVersion = Strings.Split(Application.ProductVersion, ".");
string AppVersion = string.Format("{0}.{1}.{2}.{3}", strVersion(0),
strVersion(1), strVersion(2), aRevData(0));
I have seen in the Tortoise help files that I could make use of the COM interface - this is something for another day.
Thank you for reading this article. Please leave your comments on ways I can improve this.
History