Volání TFS Source Control ze C#

Jan Holan       14. 7. 2011       TFS       6553 zobrazení

Součástí Team Foundation Serveru je .NET rozhraní (API), které umožňuje používat jeho funkce programově. V tomto článku si ukážeme příklady pro volání základních operací TFS Source Controlu.

Do projektu musíme pro práci s TFS nejprve přidat potřebné reference, pro TFS 2010 se jedná o následující Assembly:
Microsoft.TeamFoundation.Common.dll
Microsoft.TeamFoundation.Client.dll
Microsoft.TeamFoundation.VersionControl.Client.dll
Microsoft.TeamFoundation.VersionControl.Common.dll

které jsou po instalaci Visual Studia 2010 dostupné v adresáři:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0

Základním objektem, se kterým budeme pracovat je Microsoft.TeamFoundation.VersionControl.Client.Workspace z assembly Microsoft.TeamFoundation.VersionControl.Client.dll.

Tento objekt umožňuje pracovat s TFS Source Control pomoci metod jako je např. Get, PendEdit (check out), CheckIn, GetPendingChanges , PendBranch, Merge, PendDelete atd. Napřed ale musíme objekt Workspace získat. To můžeme provést např. následujícím kódem (kód je pro TFS 2010):

using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.Client;

private Workspace GetWorkspace(string workspaceName = null)
{
    var tfsName = new Uri("http://server:8080/tfs/defaultcollection");

    if (string.IsNullOrEmpty(workspaceName))
    {
        workspaceName = Environment.MachineName;
    }

    var credentials = System.Net.CredentialCache.DefaultCredentials;    //new System.Net.NetworkCredential(userName, password, domain);
    var projects = new TfsTeamProjectCollection(tfsName, credentials, new UICredentialsProvider(this));
    projects.EnsureAuthenticated();

    var versionControl = (VersionControlServer)projects.GetService(typeof(VersionControlServer));
    try
    {
        return versionControl.GetWorkspace(workspaceName, versionControl.AuthorizedUser);
    }
    catch (WorkspaceNotFoundException)
    {
        return versionControl.CreateWorkspace(workspaceName, versionControl.AuthorizedUser);
    }
}

Nejprve je vytvořen objekt TfsTeamProjectCollection, ve volání konstruktoru se určuje URI připojení na TFS server, credentials a credentialsProvider (příklad využívá zabudovaný UICredentialsProvider, který v případě neověření dodanými credentials zobrazí windows dialog pro zadání username a hesla uživatem). Poté je získán objekt VersionControlServer, s jeho pomoci pak získáme Workspace metodou GetWorkspace pokud již existuje, nebo metodou CreateWorkspace vytvoříme novou. Jako výchozí jméno workspace je použito jméno aktuálního počítače.

Nyní uvedu třídu SourceControlClient, ta obsahuje trochu jiný způsob jak Workspace načíst, třída totiž nezískává workspace podle jména, ale načte aktuálně dostupnou lokální workspace. Tento způsob je vhodný na vývojovém počítači, kde již bylo připojení na TFS vytvořeno (např. z Visual Studia). Dále třída obsahuje metody pro operace GetLatestVersion, CheckOut, CheckIn a UndoCheckOut. Její zdrojový kód je následující:

/// <summary>
/// Source Control helper class working on TFS current local workspace
/// </summary>
internal static class SourceControlClient
{
    #region member varible and default property initialization
    private static Workspace Workspace;
    #endregion

    #region private member functions
    static SourceControlClient()
    {
        var workspaceInfo = GetCurrentWorkspace();
        Workspace = GetWorkspace(workspaceInfo);
    }
    #endregion

    #region action methods
    /// <summary>
    /// Update workspace with the latest version of specified item if item is not currently checked out.
    /// </summary>
    /// <param name="item">The path to the file to update.</param>
    /// <param name="recursive">Recurse to the last child.</param>
    /// <returns><c>false</c> if file is currently checked out.</returns>
    public static bool GetLatestVersion(string item, bool recursive = false)
    {
        var checkinChanges = Workspace.GetPendingChangesEnumerable(item, recursive ? RecursionType.Full : RecursionType.None);
        if (!PendingChange.IsIEnumerableEmpty(checkinChanges))
        {
            //File is checked out.
            return false;
        }

        Workspace.Get(new string[] { item }, VersionSpec.Latest, recursive ? RecursionType.Full : RecursionType.None, GetOptions.Overwrite | GetOptions.GetAll);
        return true;
    }

    /// <summary>
    /// Performs a check-out of the specified item.
    /// </summary>
    /// <param name="item">The path to the file to check out.</param>
    /// <param name="lockLevel">The lock level to apply to each file checked out.</param>
    /// <param name="recursive">Recurse to the last child.</param>
    /// <returns><c>false</c> if file was already checked out.</returns>
    public static bool CheckOut(string item, LockLevel lockLevel = LockLevel.Unchanged, bool recursive = false)
    {
        var checkinChanges = Workspace.GetPendingChangesEnumerable(item, recursive ? RecursionType.Full : RecursionType.None);
        if (!PendingChange.IsIEnumerableEmpty(checkinChanges))
        {
            //File is already checked out.
            return false;
        }

        Workspace.PendEdit(new string[] { item }, recursive ? RecursionType.Full : RecursionType.None, null, lockLevel, true,
                    Microsoft.TeamFoundation.VersionControl.Common.PendChangesOptions.GetLatestOnCheckout);

        return true;
    }

    /// <summary>
    /// Performs a check-in of the specified item.
    /// </summary>
    /// <param name="item">The path of the file to check in.</param>
    /// <param name="comment">Check-in comment.</param>
    /// <param name="checkinNotes">Check-in notes.</param>
    /// <param name="recursive">Recurse to the last child.</param>
    /// <returns>The result of the evaluation.</returns>
    public static CheckinEvaluationResult CheckIn(string item, string comment = null, CheckinNote checkinNotes = null, bool recursive = false, bool overridePolicyFailures = true)
    {
        var checkinChanges = Workspace.GetPendingChangesEnumerable(item, recursive ? RecursionType.Full : RecursionType.None);
        if (PendingChange.IsIEnumerableEmpty(checkinChanges))
        {
            throw new InvalidOperationException("There are no pending changes!");
        }

        var checkinOptions = CheckinEvaluationOptions.Notes | CheckinEvaluationOptions.Policies;
        var checkedWorkItems = new WorkItemCheckinInfo[0];

        var result = Workspace.EvaluateCheckin2(checkinOptions, null, checkinChanges, comment, checkinNotes, checkedWorkItems);
        if (result.Conflicts.Length > 0 || result.NoteFailures.Length > 0 || result.PolicyEvaluationException != null ||
            (result.PolicyFailures.Length > 0 && !overridePolicyFailures))
        {
            return result;
        }

        PolicyOverrideInfo policyOverrideInfo = null;
        if (result.PolicyFailures.Length > 0)
        {
            policyOverrideInfo = new PolicyOverrideInfo("PolicyFailures override!", result.PolicyFailures);
        }

        var checkInParameters = new WorkspaceCheckInParameters(checkinChanges, comment);
        checkInParameters.CheckinNotes = checkinNotes;
        checkInParameters.AssociatedWorkItems = checkedWorkItems;
        checkInParameters.PolicyOverride = policyOverrideInfo;
        Workspace.CheckIn(checkInParameters);

        return result;
    }

    /// <summary>
    /// Undo the pending changes for the specified item.
    /// </summary>
    /// <param name="item">The path to the file to check out.</param>
    /// <param name="recursive">Recurse to the last child.</param>
    public static void UndoCheckOut(string item, bool recursive = false)
    {
        Workspace.Undo(item, recursive ? RecursionType.Full : RecursionType.None);
    }
    #endregion

    #region private member functions
    private static WorkspaceInfo GetCurrentWorkspace()
    {
        WorkspaceInfo[] allLocalWorkspaceInfo = Workstation.Current.GetAllLocalWorkspaceInfo();
        if (allLocalWorkspaceInfo.Length == 1)
        {
            return allLocalWorkspaceInfo[0];
        }

        string currentDirectory = CurrentDirectory;
        WorkspaceInfo localWorkspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(currentDirectory);
        if (localWorkspaceInfo != null)
        {
            return localWorkspaceInfo;
        }

        WorkspaceInfo[] localWorkspaceInfoRecursively = Workstation.Current.GetLocalWorkspaceInfoRecursively(currentDirectory);
        if (localWorkspaceInfoRecursively.Length != 1)
        {
            throw new InvalidOperationException("Unable to determine Workspace!");
        }

        return localWorkspaceInfoRecursively[0];
    }

    private static string CurrentDirectory
    {
        get
        {
            string currentDirectory = Environment.CurrentDirectory;
            if (currentDirectory.IndexOf('~') >= 0)
            {
                currentDirectory = System.IO.Path.GetFullPath(currentDirectory);
            }
            return currentDirectory;
        }
    }

    private static Workspace GetWorkspace(WorkspaceInfo workspaceInfo)
    {
        string tfsName = workspaceInfo.ServerUri.AbsoluteUri;
        var credentials = System.Net.CredentialCache.DefaultCredentials; //new System.Net.NetworkCredential(userName, password, domain);
        var projects = new TfsTeamProjectCollection(TfsTeamProjectCollection.GetFullyQualifiedUriForName(tfsName), credentials, new UICredentialsProvider());

        return workspaceInfo.GetWorkspace(projects);
    }
    #endregion
}

Kompletní zdrojový kód této třídy je také možné stáhnout zde.

 

hodnocení článku

0 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback