Wednesday, October 19, 2011

Reading the revision number of a local copy of a SVN repository

The SVN client (TortoiseSVN) has a handy SubWcRev.exe application which is commonly used for obtaining the revision number of a local copy of given SVN repository and (optionally) rewriting text files and replace the $WCREV$ token with the retrieved version number.

This post is to document the way we mimic SubWcRev.exe for our internal purposes.

TortoiseSVN 1.6 and earlier

When TortoiseSVN 1.6 (or earlier) is used, each folder in the repository tree contains a .svn subfolder. In there, the file named entries contains some information about the local repository. The root svn subfolder can be used as the source of the revision number - the entries file located there always contains the revision number in the third line:

public class SvnManager
{
    public string FolderName { get; private set; }
 
    public SvnManager( string FolderName )
    {
        this.FolderName = FolderName;
    }
 
    const string ENTRIES = "entries";
    private string RevisionNumber
    {
        get
        {
            string SvnSubfolder = FolderName + "\\.svn";
 
            if ( Directory.Exists( SvnSubfolder ) )
            {
                string EntriesFile = Directory.GetFiles( SvnSubfolder, ENTRIES ).FirstOrDefault();
 
                if ( !string.IsNullOrEmpty( EntriesFile ) )
                {
                    string[] Lines = File.ReadAllLines( EntriesFile );
                    if ( Lines.Length > 3 )
                        return Lines[3];
                }
            }
 
            return string.Empty;
        }
    }
}

The client code would be:

SvnManager manager = new SvnManager( "local repository root path" );
string revisionNumber = manager.RevisionNumber;

Tortoise 1.7

Tortoise 1.7 no longer creates .svn subfolders in each single folder of the local copy of your repository. Instead, a SQLite database, wc.db, is created in the .svn folder at the root level of the local copy. The database is a binary file, however it seems that the revision number is stored as string value which makes it possible to match the binary data for a regular expression, /!svn/ver/[0-9]*/:

public class SvnManager
 {
     public string FolderName { get; private set; }
 
     public SvnManager( string FolderName )
     {
         this.FolderName = FolderName;
     }
     
     const string DB      = "wc.db";
     const string PATTERN = "/!svn/ver/(?'version'[0-9]*)/";
     private string RevisionNumber
     {
         get
         {
             string SvnSubfolder = FolderName + "\\.svn";
 
             if ( Directory.Exists( SvnSubfolder ) )
             {
                 int maxVer = int.MinValue;
                 string EntriesFile = Directory.GetFiles( SvnSubfolder, DB ).FirstOrDefault();
 
                 if ( !string.IsNullOrEmpty( EntriesFile ) )
                 {
                     byte[] fileData = File.ReadAllBytes( EntriesFile );
                     string fileDataString = Encoding.Default.GetString( fileData );
 
                     Regex regex = new Regex( PATTERN );
 
                     foreach ( Match match in regex.Matches( fileDataString ) )
                     {
                         string version = match.Groups["version"].Value;
 
                         int curVer;
                         if ( int.TryParse( version, out curVer ) )
                             if ( curVer > maxVer )
                                 maxVer = curVer;
                     }
 
                     if ( maxVer > int.MinValue )
                         return maxVer.ToString();
                 }
             }
 
             return string.Empty;
         }
     }     
 }