Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Use Subversion Revision Numbers in your Visual Studio Projects

4.52/5 (16 votes)
17 Jul 2008Ms-PL4 min read 1   974  
SVN Keywords are not sufficient for retrieving the highest revision number for your project. This simple method solves this problem.

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:

  1. Creates a template file containing keywords that are read by SubWCRev.
  2. Runs the SubWCRev.exe which produces an output text file containing the Subversion information based on the keywords from the template file.
  3. 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:

KeywordDescription
$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
VB
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#
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
VB
Dim strRev As String = ""

' Input file containing the SubWcRev keywords
Dim strTemplateFile As String = "svntemplate.txt"

' Output file that will contain the SVN revisions after calling SubWcRev.exe
Dim strRevOutputFile As String = "svnrev.txt"


' If we are running as an exe (i.e. not in the Visual Studio IDE) 
' then we dont want to create the revisions but read what is 
' already there. This assumes that the exe is being used on a 
' non-subversion machine

' Check for the IDE, True is it is or false if it is not 
' (assume false is running as an exe)
If System.Diagnostics.Debugger.IsAttached Then

    ' Get the working directory of the application exe - not the most 
    ' glamourous way to do this so need to rethink this!!!
    Dim dirInfo As New DirectoryInfo(Application.ExecutablePath)
    Dim dirinfoSourceWorkingDir As DirectoryInfo
    dirinfoSourceWorkingDir = dirInfo.Parent().Parent().Parent()
    Dim strSourceWorkingDir As String = dirinfoSourceWorkingDir.FullName

    ' The template file is a text file containing some 
    ' keyword that are recognised by SubWcRev.exe when it 
    ' is run against the root project directory.
    ' Creates the template file is it is not already there
    SvnCreateTemplate(strTemplateFile)

    ' Now create a process to run the SubWcRec.exe
    Try
        Dim p As New Process

        With p.StartInfo
            .UseShellExecute = True
            .FileName = "subwcrev.exe"
            .Arguments = """" + strSourceWorkingDir + """ """ + _
                         strTemplateFile + """ """ + _
                         strRevOutputFile + """"
            .UseShellExecute = False
            .RedirectStandardOutput = True

        End With

        ' Execute the process and wait for it to exit
        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)
        ' Probably because Subversion not installed 
        ' and SubWcRev.exe was not found
    End Try

End If
C#
C#
string strRev = "";
    
// Input file containing the SubWcRev keywords
string strTemplateFile = "svntemplate.txt"

// Output file that will contain the SVN revisions after calling SubWcRev.exe
string strRevOutputFile = "svnrev.txt";


// If we are running as an exe (i.e. not in the Visual Studio IDE) 
// then we dont want to create the revisions but read what 
// is already there. This assumes that the exe is being used 
// on a non-subversion machine

// Check for the IDE, True is it is or false if it is not 
// (assume false is running as an exe)
if (System.Diagnostics.Debugger.IsAttached) {
    
    // Get the working directory of the application exe - not the most 
    // glamourous way to do this so need to rethink this!!!
    DirectoryInfo dirInfo = new DirectoryInfo(Application.ExecutablePath);
    DirectoryInfo dirinfoSourceWorkingDir;
    dirinfoSourceWorkingDir = dirInfo.Parent().Parent().Parent();
    string strSourceWorkingDir = dirinfoSourceWorkingDir.FullName;
    
    // The template file is a text file containing some keyword that are ~
    // recognised by SubWcRev.exe when it 
    // is run against the root project directory.
    // Creates the template file is it is not already there
    SvnCreateTemplate(strTemplateFile);
    
    // Now create a process to run the SubWcRec.exe
    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;
        }
        
        
        // Execute the process and wait for it to exit
        if (p.Start()) {
            string output = p.StandardOutput.ReadToEnd();
            Console.WriteLine(output);
            
            p.WaitForExit();
        }
    }
    catch (Exception ex) {
        Console.WriteLine(ex.Message);
        // Probably because Subversion not installed and SubWcRev.exe was not found
    }
  
}

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
VB
' The revison should now be available in the output file - 
' reading this will get the revision
' There are three lines (Rev, Modifed status and Rev Date) 
Dim strContents As String = ""
Dim objReader As StreamReader
Try

    objReader = New StreamReader(strRevOutputFile)
    strContents = objReader.ReadToEnd()
    objReader.Close()

    ' Get the revision from the contents of the file
    aRevData = Split(strContents, vbCrLf)

Catch Ex As Exception
    Console.WriteLine(Ex.Message)
    ' Probably because Subversion is not installed or this 
    ' project has not been checked into
    ' subversion yet. You muct make sure it is checked in to create a revision
    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#
C#
{
    // The revison should now be available in the output file - reading this 
    // will get the revision
    // There are three lines (Rev, Modifed status and Rev Date) 
    string strContents = "";
    StreamReader objReader;
    try {
        
        objReader = new StreamReader(strRevOutputFile);
        strContents = objReader.ReadToEnd();
        objReader.Close();
        
        // Get the revision from the contents of the file
        aRevData = Strings.Split(strContents, Constants.vbCrLf);
    }
    
    catch (Exception Ex) {
        Console.WriteLine(Ex.Message);
        // Probably because Subversion is not installed or this project 
        // has not been checked into
        // subversion yet. You muct make sure it is checked in to create a revision
        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
VB
' Create the revision data array - initialise to initial values - date will be 
' overwritten if reading the svn file is ok
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", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}

' Read and write to the svnrevisions text file so that it forces subversion to update 
' the file with the latest revision - there are no global revisions keyword so this 
' is an alternative!
Try
    SvnGetlatestRev(aRevData) ' Create and read the svn revision file
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#
C#
// Create the revision data array - initialise to initial values - date will be 
// overwritten if reading the svn file is ok
string[] aRevHeaders = {"$WCREV$", 
                        "$WCDATE$", 
                        "$WCNOW$", 
                        "$WCRANGE$", 
                        "$WCMIXED$", 
                        "$WCMODS$", 
                        "$WCURL$", 
                        "$WCNOW$", 
                        "$WCINSVN?Versioned:Not Versioned$", 
                        "$WCNEEDSLOCK$", 
                        "$WCISLOCKED$", 
                        "$WCLOCKDATE$", 
                        "$WCLOCKOWNER$", 
                        "$WCLOCKCOMMENT$"};

string[] aRevData = {"0", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
    
// Read and write to the svnrevisions text file so that it forces subversion to update 
// the file with the latest revision - there are no global revisions keyword so this 
// is an alternative!
try {
    SvnGetlatestRev(aRevData);
    // Create and read the svn revision file
}
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

  • pbRevsion: v1.0.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)