5. ISCCItem and ISCCBridge interfaces.

In the NesterovskyBros.SCCBridge namespace are defined two interfaces for accessing to a repository. The first interface is ISCCItem that defines common repository's item properties. The second one is ISCCBridge that defines set of methods supported by the repository. Listing 1 and 2  show the properties and methods of the interfaces.

Listing 1. Interface ISCCItem

/// <summary>
/// Determines common interface for repository item
/// </summary>
public interface ISCCItem
{
/// <summary>
/// Determines item's name
/// </summary>
string Name {get;}

/// <summary>
/// Determines full path in the repository
/// </summary>
string Path {get;}

/// <summary>
/// Determines whether the item is a file or a project
/// </summary>
bool IsFile {get;}

/// <summary>
/// Determines item's timestamp
/// </summary>
long Timestamp {get;}

/// <summary>
/// Determines item's label
/// </summary>
string Label {get;}

/// <summary>
/// Determines a version
/// </summary>
string Version {get;}

/// <summary>
/// Determines whether the item is shared.
/// This property applicable only for item, which
/// has IsFile=true
/// </summary>
bool IsShared {get;}

/// <summary>
/// Determines whether the item is deleted.
/// This property applicable only for item, which
/// has IsFile=true
/// </summary>
bool IsDeleted {get;}

/// <summary>
/// Determines whether the item is binary file.
/// This property applicable only for item, which
/// has IsFile=true
/// </summary>
bool IsBinary {get;}

/// <summary>
/// Determines whether the item is checked out.
/// This property applicable only for item, which
/// has IsFile=true
/// </summary>
bool IsChecked {get;}

/// <summary>
/// Determines by whom item is checked out.
/// This property applicable only for item, which
/// has IsFile and IsChecked properties equals to true.
/// </summary>
string CheckedBy {get;}

/// <summary>
/// Determines a comment to the item.
/// </summary>
string Comment {get;}

/// <summary>
/// Determines an action.
/// </summary>
string Action {get;}

/// <summary>
/// Determines whether the item is pinned.
/// This property applicable only for item, which
/// has IsFile=true
/// </summary>
bool IsPinned {get;}

/// <summary>
/// Determines user, who created or changed the actual version of item
/// </summary>
string User {get;}
}

Listing 2. ISCCBridge
interface.

  /// <summary>
Determines interface to repository. </summary>
  public interface ISCCBridge
  {
  /// <summary>
  /// Determines a path to a database
  /// </summary>
  string Database {get;}

/// <summary>
/// Login to the specified repository
/// </summary>
/// <param name="UserName">determines a user</param>
/// <param name="Password">determines the user's password</param>
/// <param name="Database">determines path to the  database</param>
void Login(string UserName, string Password, string Database);

/// <summary>
/// Logout from the repository
/// </summary>
void Logout();

/// <summary>Create a new project in the repository.</summary>
/// <param name="Path">
/// determines path of the new project in the repository
/// </param>
/// <param name="Comment">determines a comment</param>
void CreateProject(string Path, string Comment);

/// <summary>Add a new file to the repository.</summary>
/// <param name="Path">determines path in the repository</param>
/// <param name="Local">determines a local path for the file</param>
/// <param name="Comment">determines a comment</param>
/// <param name="type">determines the file type</param>
/// <returns>timestamp of the added file</returns>
long Add(string Path, string Local, string Comment, FileType type);

/// <summary>
/// Deletes the specified file or project from the repository.
/// </summary>
/// <param name="Path">
/// determines the file or projects path in the repository
/// </param>
/// <param name="Permanently">
/// determines whether the specified file or project will be
/// marked as deleted or destroyed from the repository permanently
/// </param>
void Delete(string Path, bool Permanently);

/// <summary>
/// Recovers the specified file or project that was previously marked
/// as deleted. This method is opposite to the method Delete with
/// parameter Permanently is set to false.
/// </summary>
/// <param name="Path">
/// determines the file or projects path in the repository
/// </param>
void Recover(string Path);

/// <summary>
/// Checks the specified file out of the repository. It is used
/// before beginning of modifications of the repositorys file.
/// </summary>
/// <param name="Path">
/// determines the file or projects path in the repository
/// </param>
/// <param name="Local">determines local path for the file</param>
/// <param name="Comment">determines a comment</param>
/// <returns>timestamp of the file</returns>
long CheckOut(string Path, string Local, string Comment);

/// <summary>
/// Checks the specified file in of the repository. It is used for
/// committing modifications of the file to the repository.
/// </summary>
/// <param name="Path">determines path in the repository</param>
/// <param name="Local">determines a local path for the file</param>
/// <param name="Comment">determines a comment</param>
/// <returns>timestamp of the commited file</returns>
long CheckIn(string Path, string Local, string Comment);

/// <summary>Undo check out the specified file.</summary>
/// <param name="Path">
/// determines the file or projects path in the repository
/// </param>
/// <param name="Local">determines local path for the file</param>
/// <returns>timestamp of the file</returns>
long UndoCheckOut(string Path, string Local);

/// <summary>
/// Retrieves the current version of the specified file from the
/// repository.
/// </summary>
/// <param name="Path">
/// determines a path of file in the repository
/// </param>
/// <param name="Local">determines local path for the file</param>
/// <returns>Returns timestamp of the last committed version</returns>
long Get(string Path, string Local);

/// <summary>Retrieves the list of items from the repository.</summary>
/// <param name="Path">
/// determines the path of the file or project in the repository
/// </param>
/// <param name="Version">determines a version</param>
/// <param name="Recursive">
/// determines whether the command be executed recursively or not
/// </param>
/// <param name="filter">determines filter conditions</param>
/// <returns>
/// an ArrayList object where each item implements ISCCItem interface
/// </returns>
ArrayList GetList(string Path, string Version, bool Recursive,
FilterOptions filter);

/// <summary>Retrieves the item's history.</summary>
/// <param name="Path">
/// determines the path of the file or project in the repository
/// </param>
/// <param name="Recursive">
/// determines whether the command be executed recursively or not
/// </param>
/// <returns>
/// an ArrayList object where each item implements ISCCItem interface
/// </returns>
ArrayList GetHistory(string Path, bool Recursive);

/// <summary>
/// Get the specified version of the repository item.
/// </summary>
/// <param name="Path">
/// determines the path of the file or project in the repository
/// </param>
/// <param name="Local">determines local path for the file</param>
/// <param name="Version">determines the items version</param>
/// <returns>timestamp of the specified version of the item</returns>
long GetVersion(string Path, string Local, string Version);

/// <summary>Create a label for the specified item.</summary>
/// <param name="Path">
/// determines the path of the file or project in the repository
/// </param>
/// <param name="Label">determines the label name</param>
void CreateLabel(string Path, string Label);

/// <summary>Create a branch of the specified item.</summary>
/// <param name="Path">
/// determines the path of the file or project in the repository
/// </param>
/// <param name="Comment">determines a comment</param>
void Branch(string Path, string Comment);

/// <summary>
/// Share the specified file or project to another project.
/// </summary>
/// <param name="ProjectPath">determines a parent project</param>
/// <param name="ItemPath">
/// determines the file or project to be shared
/// </param>
/// <param name="Comment">determines a comment</param>
void Share(string ProjectPath, string ItemPath, string Comment);

/// <summary>
/// Pin (fix) the specified file or project. The method shares the
/// specified version of the file or project in the repository.
/// </summary>
/// <param name="Path">
/// determines the path of the file or project in the repository
/// </param>
/// <param name="Version">determines the version to pin</param>
void Pin(string Path, string Version);

/// <summary>
/// Unpin (release) the specified file or project. This method is
/// opposite to the Pin.
/// </summary>
/// <param name="Path">
/// determines the path of the file or project in the repository
/// </param>
void Unpin(string Path);

/// <summary>Moves the specified file or project into the repository.</summary>
/// <param name="ProjectPath">determines a new parent project</param>
/// <param name="ItemPath">determines the file or project to be moved</param>
void Move(string ProjectPath, string ItemPath);

/// <summary>
/// Compares two files, one from the repository and the other sent by
/// client.
/// </summary>
/// <param name="Path">determines the path in the repository</param>
/// <param name="Local">
/// determines the local path of file was sent by the client
/// </param>
/// <returns>true when they are different</returns>
bool IsDifferent(string Path, string Local);
}

As you can see, the interfaces describe properties and methods common fashion that is used by wide spectrum of version control systems. So, it is easy to implement these interfaces almost for any of such kind of systems. The current version of SCCBridge includes two implementations of the interface: the first implementation for Visual SourceSafe and the second one for a file system. The implementation for CVS will be implemented in the next versions of SCCBridge project.

The ISCCBridge interface and its implementations for Visual SourceSafe and file system are stored in SCCBridge.dll. The SCCBridge.dll assembly uses also Interop.SourceSafeTypeLib.dll satellite assembly for accessing to Visual SourceSafe.

6. BridgeServer web service.

The crucial element of SCCBridge project is the web service. It provides freedom of version control system selection, and, at the same time it provides the common straightforward interface to any system selected by you. There is another benefit of such approach. The client can access to the repository from any place where there is access to Internet and he will obtain all benefits using of version control system. It is possible due to communication with server via HTTP without relaying on specific ports or another network protocols. By the way, the latest version of the project is implemented with using of WSE 2.0, so, there is no any restriction to implement web service standalone (without IIS) and via TCP/IP connection. This could be done even without big changes in the project infrastructure.

In the most cases the methods of the web service just wraps functionality of ISCCBridge interface, but it also implements some specific to web service properties. The following listing (Listing 3) describes methods of this web service.

Listing 3. Methods of BridgeServer web service.
/// <summary>
/// Implements web service, which provides remote access to version control
/// system.
/// </summary>
[WebService(Namespace="http://www.nesterovsky-bros.com/SCCBridge")]
public class Repository: WebServicesExtension
{
/// <summary>
/// Gets the server public key for asymmetric encryption.
/// This key will be used by client library for encrypt of user's password.
/// </summary>
/// <returns>Returns string representation of RSA public key.</returns>
public string GetPublicKey() {...}

/// <summary>
/// The method is used for test purposes only. It supports HTTP GET.
/// It can be called as "Repository.asmx/Test". The method connects to
/// the default database on behalf of "Guest" user and then diconnects
/// from it. It allows to check whether the application logic is working
/// properly or not.
/// </summary>
/// <returns>"Done" string whenever test passed.</returns>
public string Test() {...}

/// <summary>
/// Ping for maintaining of HTTP session in active state.
/// </summary>
[WebMethod(EnableSession=true, Description="Ping for maintaining of HTTP session")]
public void Ping() {...}

/// <summary>Login to the repository. Open a database connection.</summary>
/// <param name="UserName">determines a user's name</param>
/// <param name="Password">determines a user's password encrypted with
/// server's RSA public key and than encoded using Base64 encoding</param>
/// <param name="Database">determines a local path to the database on
/// the server. When this parameter is empty string then the default
/// database is used.</param>
/// <returns>An offset between current time on server and UTC time in ticks
/// (number of 100-nanosecond intervals).</returns>
/// <remarks>
/// The Web.config file in the root of the web service's folder
/// defines the kind of the repository and path to default database.
/// </remarks>
public long Login(string UserName, string Password, string Database) {...}

/// <summary>
/// Logout, remove client's session, delete all temporary files and
/// disconnect from the database.
/// </summary>
[WebMethod(EnableSession=true, Description="Logout, disconnect from the repository")]
public void Logout() {...}

/// <summary>Creates a new project into the repository.</summary>
/// <param name="path">
/// determines a path of new project in the repository. The parent
/// project of the new project should exist.
/// </param>
/// <param name="comments">determines comments to the operation.</param>
public void CreateProject(string path, string comments) {... }

/// <summary>Add a new file to the repository.</summary>
/// <param name="path">
/// determines a path of new file in the repository. The parent project
/// of the file must exist.
/// </param>
/// <param name="comments">determines comments to the operation.</param>
/// <param name="type">
/// determines the file type: AUTO_DETECT/TEXT/BINARY
/// </param>
/// <returns>Returns timestamp of the added file.</returns>
/// <remarks>The file is sent as compressed (gzip) DIME attachment.</remarks>
public long Add(string path, string comments, FileType type) {...}

/// <summary>Delete the specified file or project from the repository.</summary>
/// <param name="path">
/// determines a path of the file or project in the repository
/// </param>
/// <param name="permanently">
/// determines whether the specified file or folder just be marked as
/// deleted or deleted from the repository permanently.
/// </param>
public void Delete(string path, bool permanently) {...}

/// <summary>
/// Recover a file or project in the repository, previously marked as
/// deleted.
/// </summary>
/// <param name="path">
/// determines a path of the file or project in the repository
/// </param>
public void Recover(string path) {...}

/// <summary>
/// Check the specified file out the repository. Sends the latest
/// version of the file to the client.
/// </summary>
/// <param name="path">
/// determines a path of the file in the repository
/// </param>
/// <param name="comments">determines comments to the operation.</param>
/// <returns>timestamp of the checked out file.</returns>
/// <remarks>The file is sent as compressed (gzip) DIME attachment.</remarks>
public long CheckOut(string path, string comment) {...}

/// <summary>
/// Do the same thing as CheckOut method does, but without sending of
/// the latest file version to the client.
/// </summary>
/// <param name="path">
/// determines a path of the file in the repository
/// </param>
/// <param name="comments">determines comments to the operation.</param>
/// <returns>timestamp of the checked out file.</returns>
public long SilentCheckOut(string path, string comment) {...}

/// <summary>
/// Check the specified file in the repository. Store the retrieved from
/// the client file in the repository.
/// </summary>
/// <param name="path">
/// determines a path of the file in the repository
/// </param>
/// <param name="comments">determines comments to the operation.</param>
/// <returns>timestamp of the checked in file.</returns>
/// <remarks>
/// The file is expected as compressed (gzip) DIME attachment.
/// </remarks>
public long CheckIn(string path, string comments) {...}

/// <summary>
/// Unchecks the specified file on the repository. Sends the latest file
/// version to the client.
/// </summary>
/// <param name="path">
/// determines a path of the file in the repository
/// </param>
/// <returns>timestamp of the latest version of the file</returns>
/// <remarks>The file is sent as compressed (gzip) DIME attachment.</remarks>
public long UndoCheckOut(string path) {...}

/// <summary>
/// Unchecks the specified file on the repository without sending of
/// the latest file version to the client.
/// </summary>
/// <param name="path">
/// determines a path of the file in the repository
/// </param>
/// <returns>timestamp of the latest version of the file</returns>
public long SilentUndoCheckOut(string path) {...}

/// <summary>
/// Retrieves the current version of the file from the repository and
/// sends it to the client.
/// </summary>
/// <param name="path">
/// determines a path of the file in the repository
/// </param>
/// <returns>timestamp of the current version of the file</returns>
/// <remarks>The file is sent as compressed (gzip) DIME attachment.</remarks>
public long Get(string path) {...}

/// <summary>
/// Initializes list of repositry items (files and projects). Retrieves a
/// number of items in the list.
/// </summary>
/// <param name="path">
/// determines a path to the "root" file or project in the repository
/// </param>
/// <param name="version">
/// determines a version or label name of the root item
/// </param>
/// <param name="recursive">
/// determines whether descendants of the "root" item be collected or not
/// </param>
/// <param name="filter">
/// determines expected type of files into the list:
/// NONE - all items, except: deleted, checked out, shared or binary;
/// DELETED - deleted items;
/// CHECKED- checked out files;
/// SHARED - shared only files;
/// BINARY - binary files;
/// NOT_DELETED - all items, except deleted;
/// ALL - all items
/// </param>
/// <returns>a number of items in the list</returns>
public int OpenList(string path, string version, bool recursive,
FilterOptions filter) {...}

/// <summary>
/// Initializes an history list, retrieves a number items in the list.
/// </summary>
/// <param name="path">
/// determines a path to the "root" file or project in the repository
/// </param>
/// <param name="recursive">
/// determines whether descendants of the "root" item be collected or not
/// </param>
/// <returns>a number of items in the list</returns>
public int OpenHistory(string path, bool recursive) {...}

/// <summary>
/// Fetch list of items, created previously with OpenList or OpenHistory
/// method.
/// </summary>
/// <param name="records">
/// determines expected number of items to receive
/// </param>
/// <returns>
/// an array of ISCCItemInfo objects that describes repository items. The
/// length of array is no bigger than "expected number of items".
/// </returns>
public ISCCItemInfo[] Fetch(int records) {...}

/// <summary>
/// Retrieves the specified version of the file from the repository and
/// sends it to the client.
/// </summary>
/// <param name="path">
/// determines a path of the file in the repository
/// </param>
/// <param name="version">determines a version or label name</param>
/// <returns>timestamp of the selected version of the file</returns>
/// <remarks>The file is sent as compressed (gzip) DIME attachment.</remarks>
public long GetVersion(string path, string version) {...}

/// <summary>Creates label for the specified file or project.</summary>
/// <param name="path">
/// determines a path of the file or project in the repository
/// </param>
/// <param name="label">determines the label name</param>
public void CreateLabel(string path, string label) {...}

/// <summary>Creates a branch for the specified file or project.</summary>
/// <param name="path">
/// determines a path to the file or project in the repository
/// </param>
/// <param name="comments">determines comments to the operation</param>
public void Branch(string path, string comments) {...}

/// <summary>Share the specified file or project with the project.</summary>
/// <param name="project">
/// determines a path to the new parent project in the repository
/// </param>
/// <param name="path">
/// determines a path to the file or project in the repository
/// </param>
/// <param name="comments">determines comments to the operation</param>
public void Share(string project, string path, string comments) {...}

/// <summary>
/// Pin (fix) the specified version of the file into the repository.
/// Fixed version of the file cannot be modified.
/// </summary>
/// <param name="path">
/// determines a path to the file in the repository
/// </param>
/// <param name="version">determines a version or label name</param>
public void Pin(string path, string version) {...}

/// <summary>
/// Unpin (free) the pinned version of the file.
/// Make active the latest version of the file.
/// It's used as an opposite method to Pin.
/// </summary>
/// <param name="path">
/// determines a path to the file in the repository
/// </param>
public void Unpin(string path) {...}

/// <summary>
/// Moves the specified file or project under another parent into
/// the repository.
/// </summary>
/// <param name="path">
/// determines a path to the file or project in the repository
/// </param>
/// <param name="project">determines a new parent project</param>
/// <remarks>Parent project must exist in the repository.</remarks>
public void Move(string path, string project) {...}

The common scenario of communication with the web service is:

  1. Get the server's RSA public key to encrypt password;
  2. Encrypt user's password with the server's public key and execute Login method;
  3. After successful connection it is possible to execute any web service command;
  4. Before finishing execute Logout method.

7. BridgeConsole command line tool.

BridgeConsole is a console application. The main goal of the application is access to the BridgeServer web service, but it also can be used as a simple command line building tool. Besides commands for access to the remote repository via BridgeServer web service it supports also wide used commands like "mkdir", "copy", "remove", "exec", XSL transformations and etc. The XSD scheme of instructions file is defined by http://www.nesterovsky-bros.com/schema/BridgeConsole.xsd. Take a look at the Table 1 below to understand syntax of main commands supported by the application. BridgeConsole was used also for testing of both server and client sides of SCCBridge project.

Usage:
BridgeConsole.exe -Iinstr [[-Uuser] [-Ppassword] [-Sservice] [-Ddatabase] [-Llog]]
Available keys:
  • -I - determines path to the file-instructions, obligatory parameter;
  • -U - determines an repository user name, not obligatory parameter;
  • -P - determines an repository user password, not obligatory parameter;
  • -S - determines an URL of the web service, not obligatory parameter, by default http://localhost/BridgeServer/Repository.asmx is used;
  • -D - determines path to a database on the server, not obligatory parameter;
  • -L - determines path to the log-file, by default standard output is used.

Whenever the application cannot connect to a web service that supports BridgeServer contract due to incorrect either server URL or user's credentials, it will continue running, but all commands concerning to remote version control system will lead to exceptions. All exceptions will be caught and written out to the log or to the standard output stream.

Table 1. The list of commands supported by BridgeConsole.
<add project="/proj1" recursive="true" comment="test">
  <mask>c:\temp\proj1\*.*</mask>
</add>
Add the specified list of files/folders to the repository.
<delete>
  <item>/proj1/file.txt</item>
  <item>/proj1/proj2</item >
</delete >
Delete the specified list of files/folders from the repository.
<destroy>
  <item>/proj3</item>
</destroy>
Destroy the specified list of files/folders into the repository.
<check-in recursive="true" comment="test2">
  <item>/proj1/file1.c</item>
  <item>/proj4</item>
</check-in>
Check the specified list of files/folders in.
<check-out recursive="true">
  <item>/proj1/file1.c</item>
  <item>/proj1/file1.h</item>
  <item>/proj4</item>
</check-out>
Check the specified list of files/folders out.
<undo-check-out>
  <item>/proj1/file1.h</item>
</undo-check-out>
Performs undo check out for the specified list of files/folders.
<get-version repository="/proj1/file1.c" version="1"/> Gets the specified version of the file from the repository.
<get-latest-version recursive="true">
  <item>/proj1/file1.c</item>
  <item>/proj4</item>
</get-latest-version>
Gets the latest versions of the specified list of files/folders from the repository.
<history recursive="true" repository="/" to ="c:\temp\history1.xml"/>
Gets history list for the specified file/folder and store the result XML to the specified place.
<label
recursive="true" name="label1">
  <item>/proj1 </item>
</label>
Creates a label with the specified name in the repository.
<list recursive="true" repository="/" to="c:\temp\list1.xml" includeDeleted="true"/> Gets the current state of the repository and save it as a XML file to the specified place.
<get-label repository="/" label="label1"/> Retrieves from the repository files and folders marked with the specified label name. Operation starts from the specified position in the repository.
<move project="/">
  <item>/proj4/proj5</item>
</move>
Moves the specified list of files/folders to under another parent into the repository.
<branch>
  <item>/proj5</item>
</branch>
Creates a branch into the repository, for the specified list of files/folders.
<share project="/" comment="share1">
  <item>/proj5/file2.txt</item>
  <item>/proj5/proj6</item>
</share>
Share for the specified list of files/folders with the new parent project into the repository.
<pin repository="/proj5" version="1"/> Pin the specified version of the file or folder.
<unpin>
  <item>/proj5</item>
</unpin>
Remove pin.
<recover>
  <item>/proj1/subproj</item>
</recover>
Restore into the repository, previously marked as deleted, list of files/folders.
<cloak>
  <item>/proj5</item>
</cloak>
Mark the specified list of folders as unused.
<uncloak>
  <item>/proj5</item>
</uncloak>
Remove mark was set by previous command.
<comment>
A simple text comment.
</comment>
Write out text comment to the log or standard output stream. There is a variable substitution. Whenever you write "{$now}" to the output stream will be written out current date and time.
<copy to="c:\temp">
  <mask>c:\projects\*.*</mask>
</copy>
Copies locally files/folders according to the specified mask to another place.
<rename-move from="c:\temp\folder1" to="c:\temp\dir1"/> Renames or moves the specified file/folder to another one locally.
<remove>
  <mask>c:\temp\dir1\test.*</mask>
</remove>
Deletes files/folders locally, according to the specified mask.
<mkdir name="c:\temp\dir2"/> Creates locally a folder with the specified name.
<exec
name="c:\winnt\system32\notepad.exe">
  <arg>/p</arg>
  <arg>c:\temp\dir1\license.txt</arg>
</exec>
Executes the specified external applications with the list of arguments determined by children arg elements.
<transform xml="c:\temp\dir1\main.xml" xsl="c:\temp\dir1\processor.xsl" to="c:\temp\dir1\result.txt">
  <arg name="param1">value 1</arg>
</transform>
Performs XSL transformation.

Here is the simple example of file-instructions (e.g. stored in instr.xml) for BridgeConsole application:

      <?xml version="1.0" encoding="utf-8"?>
      <commands 
        working-folder="c:\temp" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="schema/BridgeConsole.xsd">
    
        <comment>
        ------------------------------------------------------------------
            Create folders and copy files to the working folder
        ------------------------------------------------------------------
        </comment>
        
        <mkdir name="c:\temp\folder1"/>
        <mkdir name="c:\temp\folder1\folder2"/>
        <mkdir name="c:\temp\folder3"/>
        <mkdir name="c:\temp\folder3\folder4"/>
    
        <copy to="c:\temp\folder1">
          <mask>D:\Projects\NesterovskyBros\BridgeConsole\*.*</mask>
        </copy>
    
        <copy to="c:\temp\folder1\folder2">
          <mask>D:\Projects\NesterovskyBros\BridgeConsole\tests\*.*</mask>
        <copy>
    
        <comment>
        ------------------------------------------------------------------
                      {$now}: Tests of "Add"
        ------------------------------------------------------------------
        </comment>
    
        <comment>Add the folder "c:\temp\folder1" and its content to the repository recursively:</comment>
        <add project="/" comment="append folder1 as dir1 to the repository" 
          recursive="true">
          <mask>c:/temp/folder1</mask>
        </add>
    
        <comment>
        ------------------------------------------------------------------
                  {$now}: Tests "CheckOut" and "List"
        ------------------------------------------------------------------
        </comment>
    
        <comment>Check-out "/folder1/folder2" recursively:</comment>
        <check-out recursive="true">
          <item>/folder1/folder2</item>
        </check-out>
    
        <comment>Gets the list of all items in the repository:</comment>
        <list repository="/" recursive="true" to="c:\tmp\list1.xml"/>
    
        <comment>
        ------------------------------------------------------------------
                      {$now}: Tests "CheckIn"
        ------------------------------------------------------------------
        </comment>

        <comment>Check in "/folder1" folder recursively:</comment>
        <check-in recursive="true" comment="test recursive check in">
          <item>/folder1</item>
        </check-in>
    
        <comment>
        ------------------------------------------------------------------
                      {$now}: Tests of "History"
        ------------------------------------------------------------------
        </comment>
    
        <comment>Retrieve history of whole repository:</comment>
        <history repository="/" recursive="true" to="c:\tmp\history1.xml"/>
    
      </commands>
      

To execute BridgeConsole in command line type in:   BridgeConsole.exe -Uadmin -Iinstr.xml

8. Using of ISCCBridge interface in applications.

Before using of ISCCBridge interface directly you have to do following basic steps:

  1. In Solution Explorer, select the project.
  2. On the Project menu, choose Add Reference. The Add References dialog box opens.
  3. Choose Browse, and find the SCCBridge.dll assembly on your local drive, then click OK. The component is added to the Selected Componentsfield.
  4. To add the selected reference click Add button.
  5. In your source file insert following line: using NesterovskyBros.SCCBridge.
  6. For now you can use the interface in your program.

The example below demonstrates how to retrieve a list of items from the Visual SourceSafe repository.

  using System;
using System.IO;
using System.Collections;
using NesterovskyBros.SCCBridge; // do not forget to include the namespace

namespace Test
{
class Class1
{
// The main entry point for the application.
[STAThread]
static void Main(string[] args)
{
try
{
using(ISCCBridge repository=new SourceSafeBridge())
{
repository.Login("admin", "",
@"C:\Program Files\Microsoft Visual Studio\Common\VSS\srcsafe.ini");

// Get list of all existing items exclude deleted
ArrayList items=repository.GetList(
  "/", "",  true,  FilterOptions.ALL);

foreach(ISCCItem item in items)
Console.WriteLine("{0}, {1}, {2}", item.Path,
DateTime.FromFileTime(item.Timestamp).ToString(),
item.Version));
}
}
catch(Exception e)
{
Console.WriteLine("ERROR: {0}", e.ToString());
}
}
}
}

9. Using of SCCClient library in applications.

There are two ways utilization of ISCCBridge interface remotely. The first way is to use SCCClient library from SCCClient.dll assembly. This assembly also requires another satellite assembly - SharpZipLib.dll, which is a freeware implementation of GZip, Zip and another useful compressing protocols.

This is preferable approach, since SCCClient   library does a lot of work behind the scenes. It keeps track the current states of local files, synchronizes only different files with the repository, allows to execute commands on folders (e.g. add or check out folder not only single file), supports additional functionality against the pure web service functionality (e.g. cloaking/uncloaking projects). The library also assumes control of communication with the web service. It creates DIME messages, compress and uncompress files, etc.

The second way is to use web service directly. This is rather heavy way, it guesses that you either have to use some library for GZip compressing and WSE anyway, or will communicate with the web service not from .NET application. In this case you have to implement DIME format of messages. See http://msdn.microsoft.com/msdnmag/issues/02/12/DIME/default.aspx to understand it.

Lets take a look at the first approach (utilization of SCCClient library). After creating of a new project you have to do following:

  1. In Solution Explorer, select the project.
  2. On the Project menu, choose Add Reference. The Add References dialog box opens.
  3. Choose Browse, and find the SCCClient.dll assembly on your local drive, then click OK. The component is added to the Selected Components field.
  4. To add the selected reference click Add button.
  5. In your source file insert following line: using NesterovskyBros.SCCClient.
  6. For now you can use the interface in your program.

See example below, which demonstrates how to use SCCClient library.

using System;
using System.IO;
using System.Collections;
using NesterovskyBros.SCCClient; // do not forget to include the namespace

namespace Test
{
class Class1
{
// The main entry point for the application.
[STAThread]
static void Main(string[] args)
{
try
{
User user=new User("admin", "",
"http://localhost/BridgeServer/Repository.asmx");
user.WorkingFolder=@"c:\temp";
using(ServiceProvider service=new ServiceProvider(user))
{
Command cmdAdd=new SCCClient.Add(service);
Command cmdCheckIn=new SCCClient.CheckIn(service);
Command cmdCheckOut=new SCCClient.CheckOut(service);
Command cmdHistory=new SCCClient.History(service);
Command cmdList=new SCCClient.List(service);

// Add /folder1 and subfolders to the repository
Entry entry=new DirectoryInfo(@"c:\temp\folder1");
ArrayList items=cmdAdd.Execute(entry, CommandOptions.Recursive,
"append folder1");

// Check out /folder1 recursively
items=cmdCheckOut.Execute(entry, CommandOptions.Recursive);

// Get list of all existing items exclude deleted
entry=new Entry();
entry.Path="/";
items=cmdList.Execute(entry, CommandOptions.Recursive,
(FilterOptions)62);

Console.WriteLine("List of items:");
Console.WriteLine("-----------------------------");
foreach(Entry item in items)
{
Console.WriteLine("Path: {0}, version: {1}", item.Path,
item.Version);
Console.WriteLine(" Timestamp: {0}",
DateTime.FromFileTime(item.Timestamp).ToString());
if (item.IsCheckedout)
Console.WriteLine(" Checked out by: {0}",
String.Join(",", item.CheckedBy));
Console.WriteLine("-----------------------------");
}

// Check in /folder1 recursively
entry=new DirectoryInfo(@"c:\temp\folder1");
cmdCheckIn.Execute(entry, CommandOptions.Recursive,
"check /folder1 recursively");

// Get the history
entry=Entry.GetInstance(user.WorkingFolder, "/");
items=cmdHistory.Execute(entry, CommandOptions.Recursive);

Console.WriteLine("\n\n");

Console.WriteLine("History:");
Console.WriteLine("-----------------------------");
foreach(Entry item in items)
{
Console.WriteLine("Path: {0}, version: {1}", item.Path,
item.Version);
Console.WriteLine(" Timestamp: {0}",
DateTime.FromFileTime(item.Timestamp).ToString());
if (""!=item.Label)
Console.WriteLine(" Label: {0}", item.Label);
if (""!=item.Comment)
Console.WriteLine(" Comment: {0}", item.Comment);
if (""!=item.Action)
Console.WriteLine(" Action: {0}", item.Action);
Console.WriteLine("-----------------------------");
}
}

// Wait for user's click
Console.Read();
}
catch(Exception e)
{
Console.WriteLine("ERROR: {0}", e.ToString());
}
}
}
}

The latest version of SCCClient library exposes its functionality also as a COM object. This allows also to use this library from non-managed code. The following classes are exposed to COM objects:

NesterovskyBros.SCCClient.Entry
IID: 39D87B62-E23A-3E40-954E-3BDBF9BD48C8
CLSID: 60784CA5-7747-3830-80E0-F0D5D141D05F
NesterovskyBros.SCCClient.User
IID: 60D2BEB1-5B30-375F-816B-5A2D4C2AC51C
CLSID: D4A7795D-BB44-3F50-82F2-A82C8490D354
NesterovskyBros.SCCClient.ServiceProvider
IID: B35DFC60-C7AA-36DF-B72B-DD3424C333E2
CLSID: 2D2CD3E0-6E74-3A48-84F5-456E1F7CE0FD
NesterovskyBros.SCCClient.CommandProcessor
IID: CBE48A20-DC8E-4b79-9703-328350C09B9E
CLSID :  6C6FEAAC-3435-4c83-BF61-D8A75AC9546C

See SCCClient.tlb file and sources for more details. Take a look also at the listing below that demonstrates how to use SCCClient  library from unmanaged C++:

#pragma once

#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <stdio.h>

#pragma warning (disable: 4278)

// pay attention: you have to include this TLB file
// to access to SCCClient library
#import "SCCClient.tlb" no_namespace named_guids

int main(int argc, char *argv[])
{
CoInitialize(0);

int result;
try
{
result = GetFolder("c:\\temp", "/folder1/folder2");
}
catch(...)
{
// do nothing
}

CoUninitialize();

return result;
}

int GetFolder(LPSTR localPath, LPSTR ssPath)
{
// Try to initialize SCCClient library
ICommandProcessorPtr processor = 0;
HRESULT hr = CoCreateInstance(CLSID_CommandProcessor, NULL,
CLSCTX_INPROC_SERVER, IID_ICommandProcessor,
reinterpret_cast<void **>(&processor));
if (FAILED(hr))
{
// cannot get SCCClient library
return -1;
}

// Create the User object instance
IUserPtr user = 0;
hr = CoCreateInstance(CLSID_User, NULL, CLSCTX_INPROC_SERVER,
IID_IUser, reinterpret_cast<void **>(&user));
if (FAILED(hr))
{
// cannot get IUser interface
return -2;
}

// just for example lets the name and password be
// "Guest" and "guest" respectivelly
user->Name = "Guest";
user->Password = "guest";

// set the server URI
user->Server = "http://localhost/BridgeServer/Repository.asmx";

try
{
processor->Login(user, 120000); // set login timeout to 120 sec.
}
catch(...)
{
// access denied
return -3;
}

IEntryPtr entry = 0;
hr = CoCreateInstance(CLSID_Entry, NULL, CLSCTX_INPROC_SERVER,
IID_IEntry, reinterpret_cast<void **>(&entry));
if (FAILED(hr))
{
// cannot get IEntry interface
return -2 ;
}

entry->PutLocalPath(localPath);
entry->PutPath(ssPath);

// Checks whether the specified project is under control or not
if (processor->IsControlled(entry) == VARIANT_FALSE)
{
entry->PutIsFile(VARIANT_FALSE);
try
{
int options = CommandOptions_Recursive |
CommandOptions_MarkReadOnly;
processor->Get(entry, (CommandOptions)options);
}
catch(...)
{
// access failure
return -4;
}
}
else
// project is not under control
return -5;

return 0; // OK
}

You can also use the SCCClient library from JScript or VBScript :
<html>
<body>
<script language="JScript">
var user = new ActiveXObject("NesterovskyBros.SCCClient.User");
user.Name = "Guest";
user.Password = "guest";
user.Server = "http://localhost/BridgeServer/Repository.asmx";

var processor = new ActiveXObject("NesterovskyBros.SCCClient.CommandProcessor");
try
{
processor.Login(user, 60000);

var entry = new ActiveXObject("NesterovskyBros.SCCClient.Entry");
entry.Path = "/TEST";
entry.LocalPath = "c:\\temp";

var list = processor.List(entry, true, false);
while (list.MoveNext())
{
entry = list.Current;
if (entry.IsDeleted) continue;
document.writeln("<div>"+entry.Path+"</div>");
}

processor.Get(entry, 3);
}
catch(e)
{
alert("["+e.number+"]["+e.name+"] "+e.message);
}
finally
{
processor.Logout();
}
</script>
</body>
</html>
Previous page    Next page

Copyright (c) 2003 Nesterovsky bros
Author: Arthur Nesterovsky