Extending Version Control

Visual Studio Team Foundation Server 2010 represents a change in the architecture of Team Foundation Server. Before you read the sample code in this topic, you should understand the architecture of Team Foundation Server at least at a very high level, and the following information should help you to understand the purpose of team projects and team project collections within a project collection. This organizational change allows you to perform version-control operations on related items within the context of a team project collection.

The primary organizational structure in Team Foundation Server 2010 is the team project collection. The team project collection is a grouping of team projects into an organizational structure that you can use to define and manage a group of projects that share resources or a code base. The benefit of this type of organizational hierarchy is that management of team projects becomes more efficient when you group them together and assign resources to them. Operations such as branching or merging of code, backing up and restoring data, and reporting project information become easier because they are specific to a database. For more information about team project collections in Team Foundation Server 2010, see Organizing Your Server with Team Project Collections.

In this topic

Note

You can extend Team Foundation version control by accessing and updating items in the version-control repository and in a workspace on the local computer to create custom check-in policies and apply them to a team project. By taking advantage of inheritance, you replace existing functionality with your own implementation of policy as it applies to version control. For more information, see the following page on the Microsoft website: How to: Create Custom Check-in Policies.

Example: Accessing Items in the Version-Control Repository

The following sample uses the VersionControlServer object to list every version of each .xaml file in the version-control repository.

To use this example

  1. Create a console application, and add references to the following assemblies:

  2. Replace the contents of Program.cs (or Module1.vb) with this example.

using System; 
using System.Text; 
using Microsoft.TeamFoundation.Client; 
using Microsoft.TeamFoundation.Framework; 
using Microsoft.TeamFoundation.VersionControl.Client; 

namespace VCSample
{
    class Program
    {
        static void Main(string[] args) 
        {
            // Connect to the team project collection and the server that hosts the version-control repository. 
            TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(
               new Uri("https://Server:8080/tfs/DefaultCollection"));
            VersionControlServer vcServer = tpc.GetService<VersionControlServer>(); 

            // List all of the .xaml files.
            ItemSet items = vcServer.GetItems("$/*.xaml", RecursionType.Full); 
            foreach(Item item in items.Items) 
            {
                Console.Write(item.ItemType.ToString());
                Console.Write(": ");
                Console.WriteLine(item.ServerItem.ToString());
            }
        }
    }
}
Imports System
Imports System.Text
Imports Microsoft.TeamFoundation.Client
Imports Microsoft.TeamFoundation.VersionControl.Client

Module Module1

    Sub Main()
        ' Connect to the team project collection and the server that hosts the version-control repository.
        Dim tfsUri As Uri
        tfsUri = New Uri("https://Server:8080/tfs/DefaultCollection")

        Dim tpc As New TfsTeamProjectCollection(tfsUri)

        Dim vcServer As VersionControlServer
        vcServer = tpc.GetService(Of VersionControlServer)()

        ' List all of the .xaml files.
        Dim items As ItemSet
        items = vcServer.GetItems("$/*.xaml", RecursionType.Full)
        Dim item As Item
        For Each item In items.Items
            System.Console.Write(item.ItemType.ToString())
            System.Console.Write(": ")
            System.Console.WriteLine(item.ServerItem.ToString())
        Next
    End Sub

End Module

Example: Update Items in a Workspace

You can work with files in a workspace by performing operations such as get, check out, and check in programmatically. The following example gets the most recent version of the files in a workspace.

To use this example

  1. In the previous example, find the section of code that lists the .xaml files, and replace it with the following code.

  2. Replace WorkspaceName with the name of the workspace, which is typically the same as the name of the computer that contains the workspace.

  3. Replace UserName with the fully qualified name of the user who owns the workspace.

For more information, see WorkstationGetLocalWorkspaceInfo and Manage Your Workspaces.

// Get the workspace that is mapped to c:\BuildProcessTemplate
WorkspaceInfo wsInfo = Workstation.Current.GetLocalWorkspaceInfo(
   vcServer, @"WorkspaceName", @"UserName");
Workspace ws = vcServer.GetWorkspace(wsInfo); 

// Update the workspace with most recent version of the files from the repository.
GetStatus status = ws.Get();
Console.Write("Conflicts: ");
Console.WriteLine(status.NumConflicts);
' Get the workspace that is mapped to c:\BuildProcessTemplate
Dim wsInfo As WorkspaceInfo
wsInfo = Workstation.Current.GetLocalWorkspaceInfo(vcServer, "WorkspaceName", "UserName")
Dim ws As Workspace
ws = vcServer.GetWorkspace(wsInfo)

' Update the workspace with the most recent version of the files from the repository.
Dim status As GetStatus
status = ws.Get
Console.Write("Conflicts: ")
Console.WriteLine(status.NumConflicts)

You can also get a workspace that is mapped to a folder on the local computer by passing the full path of that folder to WorkstationGetLocalWorkspaceInfo.

WorkspaceInfo wsInfo = Workstation.Current.GetLocalWorkspaceInfo(@"c:\MyWorkspace");
Dim wsInfo As WorkspaceInfo
wsInfo = Workstation.Current.GetLocalWorkspaceInfo("c:\MyWorkspace")

Example: Adding Items to the Version-Control Repository

You can create a file and place it under version control by using add and check-in methods.

Warning

The code will throw an exception if you do not have permissions in the workspace to read and check in files. These permissions are the Source Control Permissions for Read and Checkin in Source Control Explorer.

In this sample, you will call the following methods:

  • First, you identify project collections in Team Foundation Server by calling [M:Microsoft.TeamFoundation.Client.RegisteredTfsConnections.GetProjectCollection()] or [M:Microsoft.TeamFoundation.Client.RegisteredTfsConnections.GetProjectCollections()].

  • After you identify the project collections, you then identify each team project collection by calling [M:Microsoft.TeamFoundation.Client.TfsConfigurationServer.GetTeamProjectCollection()].

  • Within the team project collection, you identify individual team projects by calling [M:Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer.GetAllTeamProjects()].

  • For each team project, you get the associated workspace by calling [M:Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer.GetWorkspace()] or [M:Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer.CreateWorkspace()], and then you map the workspace to the local drive by calling [M:Microsoft.TeamFoundation.VersionControl.Client.Workspace.CreateMapping()].

  • To copy the files to a local drive, you call [M:Microsoft.TeamFoundation.VersionControl.Client.Workspace.Get()] for the workspace.

You can then add a file to version control if you have the appropriate permissions.

To use this example

  1. Create a C# console application, and then add references to the following assemblies:

  2. Replace the contents of Program.cs with the example code.

  3. Change the value of Uri to the name of the application-tier server in your environment.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace VControl
{
    class Program
    {
        static void Main(string[] args)
        {
            List<RegisteredProjectCollection> projectCollections;

            if (args.Count() == 0)
            {
                // Try the default URI as the name of a registered project collection.
                projectCollections = new List<RegisteredProjectCollection> { RegisteredTfsConnections.GetProjectCollection(new Uri("https://Server:8080/tfs/DefaultCollection")) };   
            }
            else
            {
                // Get all registered project collections
                projectCollections = new List<RegisteredProjectCollection>(RegisteredTfsConnections.GetProjectCollections());
            }
            
            foreach (var registeredProjectCollection in projectCollections)
            {
                TfsTeamProjectCollection projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(registeredProjectCollection);
                
                Workspace workspace = null;
                Boolean createdWorkspace = false;
                String newFolder = String.Empty;
                try
                {
                    VersionControlServer versionControl = projectCollection.GetService<VersionControlServer>();
                    
                    var teamProjects = new List<TeamProject>(versionControl.GetAllTeamProjects(false));
                    
                    if (teamProjects.Count < 1)
                        continue;
                    String workspaceName = String.Format("{0}-{1}", Environment.MachineName, "Test");
                    try
                    {
                        workspace = versionControl.GetWorkspace(workspaceName, versionControl.AuthorizedUser);
                    }
                    catch (WorkspaceNotFoundException)
                    {
                        workspace = versionControl.CreateWorkspace(workspaceName, versionControl.AuthorizedUser);
                        createdWorkspace = true;
                    }
                    var serverFolder = String.Format("$/{0}", teamProjects[0].Name);
                    var localFolder = Path.Combine(Path.GetTempPath(), "Test");
                    var workingFolder = new WorkingFolder(serverFolder, localFolder);

                    // Create a workspace mapping.
                    workspace.CreateMapping(workingFolder);

                    if (!workspace.HasReadPermission)
                    {
                        throw new SecurityException(
                            String.Format("{0} does not have read permission for {1}", versionControl.AuthorizedUser, serverFolder));
                    }
                    
                    // Get the files from the repository.
                    workspace.Get();

                    // Create a file
                    newFolder = Path.Combine(workspace.Folders[0].LocalItem, "For Test Purposes");
                    Directory.CreateDirectory(newFolder);
                    String newFilename = Path.Combine(newFolder, "Safe To Delete.txt");

                    // Determine whether the user has check-in permissions.
                    if (!workspace.HasCheckInPermission)
                    {
                        throw new SecurityException(
                            String.Format("{0} does not have check-in permission for workspace {1}", workspace.VersionControlServer.AuthorizedUser,
                            workspace.DisplayName));
                    }

                    try
                    {
                        // Create the file.
                        using (var streamWriter = new StreamWriter(newFilename))
                        {
                            streamWriter.WriteLine("Revision 1");
                        }

                        workspace.PendAdd(Path.GetDirectoryName(newFilename), true);

                        //  Create a list of pending changes.
                        var pendingAdds = new List<PendingChange>(workspace.GetPendingChanges());
                        
                        //  Enumerate the pending changes
                        pendingAdds.ForEach(add => Console.WriteLine("\t{0}: {1}", add.LocalItem,
                            PendingChange.GetLocalizedStringForChangeType(add.ChangeType)));
                        
                        // Check in the items that you added.
                        int changesetForAdd = workspace.CheckIn(pendingAdds.ToArray(), "Initial revision");
                        Console.WriteLine("Checked in changeset {0}", changesetForAdd);
                    }
                    catch (IOException ex)
                    {
                        Console.Error.WriteLine("Error writing {1}: {0}", ex.Message, newFilename);
                        throw;
                    }
                    catch (VersionControlException ex)
                    {
                        Console.Error.WriteLine("Error adding file: {0}", ex.Message);
                        throw;
                    }
                }
                finally 
                {
                    if ((workspace != null) && createdWorkspace)
                    {
                        workspace.Delete();
                    }
                    if (!String.IsNullOrEmpty(newFolder) && Directory.Exists(newFolder))
                    {
                        Directory.Delete(newFolder);
                    }
                }
                break;
            }
            
        }
    }
}

Example: Editing Items

You can modify an existing file under version control by using the following code, which calls the PendEdit and CheckIn methods. This example shows how to use the object model to edit and check in an existing file. You will modify the code sample for creating a file and then replace some lines of code in that example with the code in this example. In addition, this example introduces item specifications, which you use to add a custom property to a file.

To use this example

  • Open the C# console application that you created in the Add a File to Version Control example, and then replace the inner try block with the following code:
try
{
    // Check out and modify a file.
    workspace.PendEdit(newFilename);

    using (var streamWriter = new StreamWriter(newFilename))
    {
        streamWriter.WriteLine("Revision 2");
    }

    // Get the pending change, and check in the new revision.
    var pendingChanges = workspace.GetPendingChanges();
    int changesetForChange = workspace.CheckIn(pendingChanges, "Modified file contents");
    Console.WriteLine("Checked in changeset {0}", changesetForChange);
}

You can add a property to the file that you created in this example. By calling the SetVersionedItemProperty method, you can set a property of your choosing on the file. In the example, you will use the itemSpec parameter to specify a path to files and folders. In this case, you will specify the local path, although you can also use this parameter to specify a path on the repository. You will also define a property and a value for it.

Warning

You must be careful when you use a local path for an item specification. An exception will be thrown if you specify a mapping that does not also exist in the repository.

//Add a custom property to this file.
versionControl.SetVersionedItemProperty( new ItemSpec(“$/proj/Safe To Delete.txt”),VersionSpec.Latest,
    DeletedState.Any, ItemType.File,”MyProperty”, 24);

Example: Creating Branches

You can branch an existing file under version control by using the following code, which calls the PendBranch and CheckIn methods. This example builds on the Add a File to Version Control sample and demonstrates how to use the object model to create and check in a branch of an existing file. You can modify the code sample for creating a file and replace some lines of code in that example with the code in this example. After you apply these changes, you can then create a branch of a file in version control.

To use this example

  • Open the C# console application that you created in the Add a File to Version Control topic, and then replace the inner try block with the following code:
String branchedFilename = Path.Combine(Path.GetDirectoryName(newFilename),
    Path.GetFileNameWithoutExtension(newFilename)) + "-branch" + Path.GetExtension(newFilename);

workspace.PendBranch(newFilename, branchedFilename, VersionSpec.Latest, LockLevel.Checkin, true);

var pendingChanges = workspace.GetPendingChanges();
int changesetForBranch = workspace.CheckIn(pendingChanges, "Branched file");
Console.WriteLine("Branched {0} to {1} in changeset {2}", newFilename, branchedFilename, changesetForBranch);

You can add a version specification when you create a branch instead of just using the most recent version of a file. For example, you can specify a changeset ID and a user name when you call PendBranch. Because multiple classes are derived from VersionSpec, you can use a version specification as a parameter to get all files that match a changeset ID or that have a specific date or label. For more information, see ChangeSetVersionSpec, DateVersionSpec, or LabelVersionSpec.

In the following example, you specify a changeset ID to associate with the branched file. After you commit the changes, the changeset ID of the branched file will match the value that you specified.

VersionSpec versionSpec = VersionSpec.ParseSingleSpec(changesetId, username);
String branchedFilename = Path.Combine(Path.GetDirectoryName(newFilename),
    Path.GetFileNameWithoutExtension(newFilename)) + "-branch" + Path.GetExtension(newFilename);
// Use the version spec in the method call.
workspace.PendBranch(newFilename, branchedFilename, versionSpec, LockLevel.Checkin, true);

Example: Deleting Folders

You can delete a folder from version control by using the following code, which calls the PendDelete and CheckIn methods. This example builds on the Add a File to Version Control sample and demonstrates how to use the object model to delete a folder and then check in that change. You can modify the code sample for creating a file and replace some lines of code in that example with the following example. After you apply those changes, you can then delete a folder from version control.

To use this example

  • Open the C# console application that you created in the Add a File to Version Control topic, and then replace the inner try block in the original example with the following code:
try
{
    // Delete the items
    workspace.PendDelete(workspace.GetServerItemForLocalItem(newFolder), RecursionType.Full);
    var pendingDeletes = workspace.GetPendingChanges();

    if (pendingDeletes.Length > 0)
    {
        workspace.CheckIn(pendingDeletes, "Clean up!");
    }
}
catch (VersionControlException ex)
{
    Console.Error.WriteLine("Error deleting file: {0}", ex.Message);
    throw;
}

See Also

Concepts

Extending Team Foundation

Other Resources

Using Version Control