Vývoj moderních aplikací na platformě Microsoft .NET

Je vyžadována podpora jazyka JavaScript

Některé stránky na tomto webu vyžadují podporu jazyka JavaScript. Váš webový prohlížeč jazyk JavaScript nepodporuje nebo jazyk JavaScript není povolen.

Chcete-li zjistit, zda webový prohlížeč podporuje jazyk JavaScript nebo jazyk JavaScript chcete povolit, přečtěte si nápovědu k vašemu webovému prohlížeči.


SourceControlHelper.cs

Download file

Toto je zdrojový kód souboru SourceControlHelper.cs

Source Control helper class working on TFS workspace.

using System;
using System.IO;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Reflection;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.VersionControl.Common;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using Microsoft.TeamFoundation;

namespace IMP.TFSSCExplorerExtension.TFS
{
    //VersionControlServer.SupportedFeatures is an indicator of what server version you are talking to
    //                                          42684268421 - Microsoft.TeamFoundation.VersionControl.Common.SupportedFeatures Flags
    // 7     Team Foundation Server 2008 RTM  -         111 - GetLatestOnCheckout, OneLevelMapping, Destroy
    // 31    Team Foundation Server 2008 SP1  -       11111 - +CreateBranch, GetChangesForChangeset
    // 895   Team Foundation Server 2010 RTM  -  1101111111 - +ProxySuite, LocalVersions, BatchedCheckins, WorkspacePermissions
    // 1919  Team Foundation Server 2010 SP1  - 11101111111 - +CheckinDates

    //Microsoft.TeamFoundation.VersionControl.Common.SupportedFeatures Flags
    // None = 0,
    // GetLatestOnCheckout = 1,
    // OneLevelMapping = 2,
    // Destroy = 4,
    // CreateBranch = 8,
    // GetChangesForChangeset = 16,
    // ProxySuite = 32,
    // LocalVersions = 64,
    // BatchedCheckins = 256,
    // WorkspacePermissions = 512,
    // CheckinDates = 1024,

    internal static class SourceControlHelper
    {
        #region member varible and default property initialization
        private static VersionControlServer s_HasRepositoryExtensionsVCS;
        private static bool s_HasRepositoryExtensions;
        #endregion

        #region action methods
        #region Source control actions
        public static void Merge(Workspace workspace, IEnumerable<Tuple<string, string>> itemsToMerge)
        {
            foreach (var itemToMerge in itemsToMerge)
            {
                TrackMessage(string.Format("Merging {0} --> {1}", itemToMerge.Item1, itemToMerge.Item2));
                try
                {
                    var status = workspace.Merge(itemToMerge.Item1, itemToMerge.Item2, null, null, LockLevel.None, RecursionType.None, MergeOptions.None);
                    if (status.NumConflicts != 0)
                    {
                        TrackWarning("Merge operation encountered a conflict.");
                    }
                    else if (status.NoActionNeeded)
                    {
                        TrackMessage(string.Format("File {0} has no changes to merge.", itemToMerge.Item1));
                    }
                }
                catch (Microsoft.TeamFoundation.VersionControl.Client.NoMergeRelationshipException)
                {
                    //The item ... is not a branch of ...
                    TrackMessage(string.Format("A merge relationship does not exists, a baseless merge will be performed.", itemToMerge.Item1));

                    var status = workspace.Merge(itemToMerge.Item1, itemToMerge.Item2, null, null, LockLevel.None, RecursionType.None, MergeOptionsEx.Baseless);
                    if (status.NumConflicts != 0)
                    {
                        bool resolved = false;

                        var conflicts = workspace.QueryConflicts(new string[] { itemToMerge.Item2 }, false);
                        if (conflicts.Length == 1)
                        {
                            var conflict = conflicts[0];

                            //Take source version
                            if (!conflict.IsResolved && !conflict.NameChanged && !conflict.RequiresExplicitAcceptMerge && conflict.YourChangeType == ChangeType.None)
                            {
                                conflict.Resolution = Resolution.AcceptTheirs;
                                workspace.ResolveConflict(conflict);

                                if (conflict.IsResolved)
                                {
                                    resolved = true;

                                    if (UndoUnchangedItem(workspace, itemToMerge.Item2))
                                    {
                                        TrackMessage(string.Format("File {0} has no changes to merge.", itemToMerge.Item1));
                                    }
                                }
                            }
                        }

                        if (!resolved)
                        {
                            TrackWarning("Merge operation encountered a conflict.");
                        }
                    }
                    else if (status.NoActionNeeded)
                    {
                        TrackMessage(string.Format("File {0} has no changes to merge.", itemToMerge.Item1));
                    }
                    else
                    {
                        if (UndoUnchangedItem(workspace, itemToMerge.Item2))
                        {
                            TrackMessage(string.Format("File {0} has no changes to merge.", itemToMerge.Item1));
                        }
                    }
                }
                catch (Microsoft.TeamFoundation.VersionControl.Client.MappingException ex)
                {
                    TrackWarning(ex.Message);
                }
            }
        }

        public static void BranchToFolder(Workspace workspace, IEnumerable<string> items, string folder)
        {
            BranchToFolder(workspace, items, folder, VersionSpec.Latest);
        }

        public static void BranchToFolder(Workspace workspace, IEnumerable<string> items, string folder, VersionSpec versionSpec)
        {
            foreach (var item in items)
            {
                string fileName = VersionControlPath.GetFileName(item);
                string folderPath = VersionControlPath.Combine(folder, fileName);

                workspace.PendBranch(item, folderPath, versionSpec);
            }
        }

        public static void MoveToFolder(Workspace workspace, IEnumerable<string> items, string folder)
        {
            try
            {
                foreach (var item in items)
                {
                    string fileName = VersionControlPath.GetFileName(item);
                    string folderPath = VersionControlPath.Combine(folder, fileName);

                    workspace.PendRename(item, folderPath);
                }
            }
            catch (System.ComponentModel.Win32Exception ex)
            {
                //Access is denied
                //This file is currently not available for use on this computer
                TrackWarning(ex.Message);
            }
        }

        public static void CopyToFolder(Workspace workspace, IEnumerable<VersionControlExplorerItem> items, string folder)
        {
            string folderLocalPath;
            try
            {
                folderLocalPath = workspace.GetLocalItemForServerItem(folder);
            }
            catch (Microsoft.TeamFoundation.VersionControl.Client.MappingException ex)
            {
                TrackWarning(ex.Message);
                return;
            }

            if (!System.IO.Directory.Exists(folderLocalPath))
            {
                TrackMessage(string.Format("Folder {0} not found", folderLocalPath));
                return;
            }

            foreach (var item in items)
            {
                string localPath = null;
                try
                {
                    localPath = item.LocalPath;
                    if (string.IsNullOrEmpty(localPath) && item.TargetServerPath != null)
                    {
                        localPath = workspace.GetLocalItemForServerItem(item.TargetServerPath);
                    }
                }
                catch (Microsoft.TeamFoundation.VersionControl.Client.MappingException ex)
                {
                    TrackWarning(ex.Message);
                    continue;
                }

                if (string.IsNullOrEmpty(localPath))
                {
                    TrackMessage(string.Format("Local item path cannot be loaded", localPath));
                    continue;
                }

                if (item.IsFolder)
                {
                    if (!System.IO.Directory.Exists(localPath))
                    {
                        TrackMessage(string.Format("Folder {0} not found", localPath));
                        continue;
                    }

                    string targetFolder = System.IO.Path.Combine(folderLocalPath, System.IO.Path.GetFileName(localPath));
                    CopyFiles(localPath, targetFolder, true, true);

                    workspace.PendAdd(targetFolder, true);
                    continue;
                }

                if (!System.IO.File.Exists(localPath))
                {
                    TrackMessage(string.Format("File {0} not found", localPath));
                    continue;
                }

                string targetFile = System.IO.Path.Combine(folderLocalPath, System.IO.Path.GetFileName(localPath));
                try
                {
                    System.IO.File.Copy(localPath, targetFile, true);
                }
                catch (System.UnauthorizedAccessException ex)
                {
                    //Access to the path ... is denied - Ignore
                    TrackWarning(ex.Message);
                    continue;
                }

                //Ensure that the file is writeable
                var fileAttributes = System.IO.File.GetAttributes(targetFile);
                System.IO.File.SetAttributes(targetFile, fileAttributes & ~System.IO.FileAttributes.ReadOnly);

                workspace.PendAdd(targetFile);
            }
        }

        public static void Destroy(VersionControlServer vcs, IEnumerable<string> items, bool keepHistory)
        {
            try
            {
                foreach (var item in items)
                {
                    var flags = DestroyFlags.Silent | DestroyFlags.StartCleanup;
                    if (keepHistory)
                    {
                        flags |= DestroyFlags.KeepHistory;
                    }

                    vcs.Destroy(new ItemSpec(item, RecursionType.Full), VersionSpec.Latest, null, flags);
                }
            }
            catch (Exception ex)
            {
                TrackWarning(ex.Message);
            }
        }

        /// <summary>
        /// Undo changes to unchanged files in the workspace
        /// </summary>
        /// <param name="workspace">Workspace</param>
        public static void UndoUnchanged(Workspace workspace)
        {
            //Get pending changes
            PendingChange[] pendingChanges = workspace.GetPendingChanges();
            if (pendingChanges.Length == 0)
            {
                return;
            }

            var spec = new ChangesetVersionSpec(workspace.VersionControlServer.GetLatestChangesetId());

            var sourceArray = ItemSpec.FromPendingChanges(pendingChanges);
            var pendingChangesItems = new ItemSet[0];
            if (sourceArray.Length > 0)
            {
                pendingChangesItems = workspace.VersionControlServer.GetItems(sourceArray, spec, DeletedState.Any, ItemType.Any);
            }

            //Build redundant change list
            var itemsToUndo = new List<ItemSpec>();
            var itemsToGet = new List<string>();
            for (int j = 0; j < pendingChanges.Length; j++)
            {
                var pendingChange = pendingChanges[j];
                if (pendingChange.IsAdd || pendingChange.IsDelete || pendingChange.IsMerge || (pendingChange.ChangeType & ~ChangeType.Lock) == ChangeType.Edit)
                {
                    Item item = null;
                    foreach (Item currentItem in pendingChangesItems[j].Items)
                    {
                        if (pendingChange.ItemType == currentItem.ItemType && (pendingChange.IsDelete || currentItem.DeletionId == 0))
                        {
                            if (pendingChange.IsDelete && currentItem.DeletionId == 0)
                            {
                                item = null;
                                break;
                            }
                            if (!pendingChange.IsDelete || item == null || currentItem.DeletionId >= item.DeletionId)
                            {
                                item = currentItem;
                            }
                        }
                    }

                    if (item != null)
                    {
                        bool additemToUndo = false;
                        bool addItemToGet = false;
                        if (pendingChange.ItemType == ItemType.File)
                        {
                            if (pendingChange.IsDelete)
                            {
                                additemToUndo = true;
                                addItemToGet = true;
                                TrackMessage(string.Format("{0}: {1}", pendingChange.ChangeTypeName, pendingChange.LocalItem));
                            }
                            else if (IsMatchesContent(pendingChange.LocalItem, item.HashValue))
                            {
                                additemToUndo = true;
                                if (pendingChange.IsAdd || pendingChange.IsDelete)
                                {
                                    addItemToGet = true;
                                }
                                TrackMessage(string.Format("{0} (contents match): {1}", pendingChange.ChangeTypeName, pendingChange.LocalItem));
                            }
                        }
                        else if (pendingChange.IsAdd || pendingChange.IsDelete)
                        {
                            additemToUndo = true;
                            addItemToGet = true;
                            TrackMessage(string.Format("{0}: {1}", pendingChange.ChangeTypeName, pendingChange.LocalItem));
                        }

                        if (additemToUndo)
                        {
                            itemsToUndo.Add(new ItemSpec(pendingChange));
                        }
                        if (addItemToGet)
                        {
                            itemsToGet.Add(pendingChange.LocalItem);
                        }
                    }
                }
            }

            if (itemsToUndo.Count == 0)
            {
                //No redundant pending changes
                TrackMessage("There are no redundant pending changes.");
                return;
            }

            //Undo redundant pending changes
            TrackMessage("Undoing redundant changes...");
            workspace.Undo(itemsToUndo.ToArray());

            if (itemsToGet.Count > 0)
            {
                //Force get undone adds
                TrackMessage("Forcing a get for undone adds and deletes...");
                workspace.Get(itemsToGet.ToArray(), spec, RecursionType.None, GetOptions.GetAll | GetOptions.Overwrite);
            }
        }
        #endregion

        #region Branch functions
        public static ExtendedItem GetExtendedItem(VersionControlServer vcs, string path)
        {
            var itemSpecs = new ItemSpec[] { new ItemSpec(path, RecursionType.None) };

            var getItemsOptions = GetBranchObjectSupported(vcs) ? GetItemsOptions.IncludeBranchInfo : GetItemsOptions.None;
            getItemsOptions |= GetItemsOptions.IncludeSourceRenames;

            try
            {
                var extendedItems = vcs.GetExtendedItems(itemSpecs, DeletedState.Any, ItemType.Any, getItemsOptions, null)[0];  //[podle itemSpecs.Length prvků]
                if (extendedItems.Length > 0)
                {
                    return extendedItems[0];    //[podle RecursionType]
                }
            }
            catch (System.Xml.XmlException)
            {
                //Unexpected end of file.
                //The data at the root level is invalid. Line 1, position 1.
            }
            catch (System.IndexOutOfRangeException)
            {
                //Index was outside the bounds of the array.
            }
            catch (System.NotSupportedException)
            {
                //Stream does not support reading.
            }
            catch (System.IO.IOException)
            {
                //Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
            }

            return null;
        }

        public static HashSet<string> GetAllBranchesList(VersionControlServer vcs, string path)
        {
            var list = new HashSet<string>();
            FillAllBranchesList(vcs, new ItemIdentifier(path), list);

            return list;
        }

        public static BranchHistoryTreeItem GetBranchHistory(VersionControlServer vcs, string path)
        {
            try
            {
                return GetBranchHistoryRequestedItem(GetBranchHistoryTree(vcs, path));
            }
            catch (System.NotSupportedException)
            {
                //Stream does not support reading.
            }
            catch (System.IO.InvalidDataException)
            {
                //Unknown block type. Stream might be corrupted.
            }
            catch (System.IO.IOException)
            {
                //Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
            }

            return null;
        }
        #endregion

        #region Others functions
        public static bool IsFolderCloaked(Workspace workspace, string serverItem)
        {
            if (workspace != null)
            {
                WorkingFolder folder = workspace.TryGetWorkingFolderForServerItem(serverItem);
                if (folder == null)
                {
                    return false;
                }
                if (folder.IsCloaked)
                {
                    return true;
                }
            }

            return false;
        }

        public static void Cloak(Workspace workspace, string serverItem)
        {
            workspace.Cloak(serverItem);
            workspace.Get(new string[] { serverItem }, VersionSpec.Latest, RecursionType.Full, GetOptions.None);
        }

        public static void UnCloak(Workspace workspace, string serverItem)
        {
            string localItem = null;
            WorkingFolder mapping = null;
            foreach (WorkingFolder workingFolder in workspace.Folders)
            {
                if (workingFolder.Type == WorkingFolderType.Cloak)
                {
                    if (serverItem != null && (VersionControlPath.Equals(workingFolder.ServerItem, serverItem) || VersionControlPath.Equals(workingFolder.DisplayServerItem, serverItem)))
                    {
                        localItem = workingFolder.LocalItem;
                        mapping = workingFolder;
                        break;
                    }
                    if (localItem != null && Microsoft.TeamFoundation.Common.FileSpec.Equals(workingFolder.LocalItem, localItem))
                    {
                        serverItem = workingFolder.ServerItem;
                        mapping = workingFolder;
                        break;
                    }
                }
            }

            if (mapping != null)
            {
                workspace.DeleteMapping(mapping);
            }
        }

        public static bool HasRepositoryExtensions(VersionControlServer vcs)
        {
            if (vcs == null)
            {
                s_HasRepositoryExtensionsVCS = null;
                s_HasRepositoryExtensions = false;
                return false;
            }

            if (vcs != s_HasRepositoryExtensionsVCS)
            {
                try
                {
#if VS2010
                    //vcs.Repository.Extensions != null;
                    var versionControlServerType = vcs.GetType();

                    var repositoryProperty = versionControlServerType.GetProperty("Repository", BindingFlags.NonPublic | BindingFlags.Instance);
                    object repository = repositoryProperty.GetValue(vcs, new object[0]);

                    var extensionsProperty = repository.GetType().GetProperty("Extensions", BindingFlags.NonPublic | BindingFlags.Instance);
                    object extensions = extensionsProperty.GetValue(repository, new object[0]);

                    s_HasRepositoryExtensionsVCS = vcs;
                    s_HasRepositoryExtensions = extensions != null;
#else
                    s_HasRepositoryExtensionsVCS = vcs;
                    s_HasRepositoryExtensions = vcs.WebServiceLevel >= WebServiceLevel.Tfs2010;
#endif
                }
                catch (Exception)
                {
                    s_HasRepositoryExtensionsVCS = vcs;
                    s_HasRepositoryExtensions = false;
                }
            }

            return s_HasRepositoryExtensions;
        }

        public static bool PendingChangeItemsDirtyCheck(Workspace workspace)
        {
            try
            {
                //Get pending changes
                PendingChange[] pendingChanges = workspace.GetPendingChanges();
                if (pendingChanges.Length == 0)
                {
                    return false;
                }

                for (int j = 0; j < pendingChanges.Length; j++)
                {
                    var pendingChange = pendingChanges[j];

                    if (pendingChange.IsEdit && !string.IsNullOrEmpty(pendingChange.LocalItem) && IsItemDirty(pendingChange.LocalItem))
                    {
                        return true;
                    }
                }
            }
            catch (System.IO.IOException)
            {
                //Unable to read data from the transport connection: The connection was closed.
            }

            return false;
        }
        #endregion
        #endregion

        #region private member functions
        private static string GetParentPath(string serverItem)
        {
            int index = serverItem.LastIndexOf('/');
            if (index != -1)
            {
                if (index == 1)
                {
                    return "$/";
                }

                return serverItem.Substring(0, index);
            }

            return null;
        }

        /// <summary>
        /// Undo changes in item
        /// </summary>
        /// <param name="workspace">Workspace</param>
        private static bool UndoUnchangedItem(Workspace workspace, string sourceItem)
        {
            var spec = new ChangesetVersionSpec(workspace.VersionControlServer.GetLatestChangesetId());
            var itemSet = workspace.VersionControlServer.GetItems(new ItemSpec[] { new ItemSpec(sourceItem, RecursionType.None) }, spec, DeletedState.Any, ItemType.Any)[0];
            if (itemSet.Items.Length == 0)
            {
                return false;
            }

            Item item = itemSet.Items[0];
            string localItem = workspace.GetLocalItemForServerItem(item.ServerItem);

            if (item.ItemType == ItemType.File && IsMatchesContent(localItem, item.HashValue))
            {
                //Undo item
                workspace.Undo(item.ServerItem);
                return true;
            }

            return false;
        }

        private static void CopyFiles(string sourcePath, string destPath, bool overwrite, bool noReadOnly)
        {
            var dir = new System.IO.DirectoryInfo(sourcePath);
            if (!System.IO.Directory.Exists(destPath))
            {
                System.IO.Directory.CreateDirectory(destPath);
            }

            foreach (var file in dir.GetFiles())
            {
                string targetFile = System.IO.Path.Combine(destPath, file.Name);

                file.CopyTo(targetFile, overwrite);

                if (noReadOnly)
                {
                    //Ensure that the file is writeable
                    var fileAttributes = System.IO.File.GetAttributes(targetFile);
                    System.IO.File.SetAttributes(targetFile, fileAttributes & ~System.IO.FileAttributes.ReadOnly);
                }
            }

            foreach (var subDir in dir.GetDirectories())
            {
                string targetDirPath = System.IO.Path.Combine(destPath, subDir.Name);
                CopyFiles(System.IO.Path.Combine(sourcePath, subDir.Name), targetDirPath, overwrite, noReadOnly);
            }
        }

        private static bool IsMatchesContent(string path, byte[] hash)
        {
            try
            {
                if (!string.IsNullOrEmpty(path) && hash.Length > 0 && File.Exists(path))
                {
                    byte[] buffer = ComputeMD5Hash(path);

                    return BitConverter.ToString(hash).Equals(BitConverter.ToString(buffer));
                }
            }
            catch (IOException)
            {
            }
            return false;
        }

        private static byte[] ComputeMD5Hash(string fileName)
        {
            using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                try
                {
                    using (var md = new MD5CryptoServiceProvider())
                    {
                        return md.ComputeHash(stream);
                    }
                }
                catch (InvalidOperationException)
                {
                    return new byte[0];
                }
            }
        }

        private static void TrackMessage(string message)
        {
            TFSSCExplorerExtensionPackage.ReportFailure(String.Format("TFSSCExplorerExtension Message: {0}", message));
        }

        private static void TrackWarning(string message)
        {
            TFSSCExplorerExtensionPackage.ReportFailure(String.Format("TFSSCExplorerExtension Warning: {0}", message));
        }

        private static bool IsItemDirty(string item)
        {
            Assembly visualStudioVersionControlsAssembly = typeof(VersionControlExt).Assembly;

            //Microsoft.VisualStudio.TeamFoundation.VersionControl.ClientHelperVS.IsItemDirty(item);
            var clientHelperVSType = visualStudioVersionControlsAssembly.GetType("Microsoft.VisualStudio.TeamFoundation.VersionControl.ClientHelperVS");
            return (bool)clientHelperVSType.InvokeMember("IsItemDirty", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Static, null, null, new object[] { item });
        }

        private static void FillAllBranchesList(VersionControlServer vcs, ItemIdentifier item, HashSet<string> list)
        {
            var branches = vcs.QueryBranchObjects(item, RecursionType.Full);

            foreach (var branche in branches)
            {
                if (branche.Properties.ParentBranch != null && !list.Contains(branche.Properties.ParentBranch.Item))
                {
                    FillAllBranchesList(vcs, branche.Properties.ParentBranch, list);
                }

                list.Add(branche.Properties.RootItem.Item);

                foreach (var child in branche.ChildBranchesNoClone)
                {
                    list.Add(child.Item);
                }
            }
        }

        private static BranchHistoryTreeItem[] GetBranchHistoryTree(VersionControlServer vcs, string path)
        {
            var item = GetExtendedItem(vcs, path);
            if (item == null)
            {
                return null;
            }

            var version = new ChangesetVersionSpec(item.VersionLatest);

            BranchHistoryTreeItem[][] branchHistory = vcs.GetBranchHistory(new ItemSpec[] { new ItemSpec(path, RecursionType.None) }, version);

            if (((branchHistory.Length > 0) && (branchHistory[0].GetLength(0) > 0)) && (branchHistory[0][0].Children.Count > 0))
            {
                return branchHistory[0];
            }

            return null;
        }

        private static BranchHistoryTreeItem GetBranchHistoryRequestedItem(System.Collections.IEnumerable branches)
        {
            if (branches == null)
            {
                return null;
            }

            foreach (BranchHistoryTreeItem item in branches)
            {
                if (item.Relative.IsRequestedItem)
                {
                    return item;
                }

                var requestedItem = GetBranchHistoryRequestedItem(item.Children);
                if (requestedItem != null)
                {
                    return requestedItem;
                }
            }

            return null;
        }

        private static bool GetBranchObjectSupported(VersionControlServer vcs)
        {
            if (vcs == null)
            {
                return false;
            }
            return (vcs.WebServiceLevel >= WebServiceLevel.Tfs2010);
        }
        #endregion
    }
}