Beispiel #1
0
            private async Task <Nodes> LoadNodesAsync(SubmoduleInfoResult info, CancellationToken token)
            {
                await TaskScheduler.Default;

                token.ThrowIfCancellationRequested();

                return(FillSubmoduleTree(info));
            }
Beispiel #2
0
        /// <summary>
        /// Check that the repo is reverted after the test, prepared for next
        /// An explicit Git clean and reset will require additional time
        /// </summary>
        /// <param name="result">The existing structure, reused from the test</param>
        /// <returns>a Task</returns>
        private async Task CheckRevertedStatus(SubmoduleInfoResult result)
        {
            var changedFiles = GetStatusChangedFiles(_repo1Module);

            changedFiles.Should().HaveCount(0);
            await SubmoduleTestHelpers.UpdateSubmoduleStatusAndWaitForResultAsync(_provider, _repo1Module, changedFiles);

            result.AllSubmodules.All(i => i.Detailed is null).Should().BeTrue();
            result.TopProject.Detailed.Should().BeNull();
        }
            private Nodes FillSubmoduleTree(SubmoduleInfoResult result)
            {
                var threadModule = (GitModule)result.Module;

                var submoduleNodes = new List <SubmoduleNode>();

                // We always want to display submodules rooted from the top project.
                CreateSubmoduleNodes(result.AllSubmodules, threadModule, ref submoduleNodes);

                var nodes = new Nodes(this);

                AddNodesToTree(ref nodes, submoduleNodes, threadModule, result.TopProject);
                return(nodes);
            }
            private Nodes FillSubmoduleTree(SubmoduleInfoResult result)
            {
                Validates.NotNull(result.TopProject);

                var threadModule = (GitModule?)result.Module;

                Validates.NotNull(threadModule);

                List <SubmoduleNode> submoduleNodes = new();

                // We always want to display submodules rooted from the top project.
                CreateSubmoduleNodes(result, threadModule, ref submoduleNodes);

                Nodes nodes = new(this);

                AddTopAndNodesToTree(ref nodes, submoduleNodes, threadModule, result);
                return(nodes);
            }
            private void CreateSubmoduleNodes(SubmoduleInfoResult result, GitModule threadModule, ref List <SubmoduleNode> nodes)
            {
                // result.OurSubmodules/AllSubmodules contain a recursive list of submodules, but don't provide info about the super
                // project path. So we deduce these by substring matching paths against an ordered list of all paths.
                var modulePaths = result.AllSubmodules.Select(info => info.Path).ToList();

                // Add current and parent module paths
                var parentModule = threadModule;

                while (parentModule is not null)
                {
                    modulePaths.Add(parentModule.WorkingDir);
                    parentModule = parentModule.SuperprojectModule;
                }

                // Sort descending so we find the nearest outer folder first
                modulePaths = modulePaths.OrderByDescending(path => path).ToList();

                foreach (var submoduleInfo in result.AllSubmodules)
                {
                    string?superPath = GetSubmoduleSuperPath(submoduleInfo.Path);
                    if (!Directory.Exists(superPath))
                    {
                        MessageBoxes.SubmoduleDirectoryDoesNotExist(owner: null, superPath ?? submoduleInfo.Path, submoduleInfo.Text);
                        continue;
                    }

                    string localPath = Path.GetDirectoryName(submoduleInfo.Path.Substring(superPath.Length)).ToPosixPath();

                    var isCurrent = submoduleInfo.Bold;
                    nodes.Add(new SubmoduleNode(this,
                                                submoduleInfo,
                                                isCurrent,
                                                isCurrent ? result.CurrentSubmoduleStatus : null,
                                                localPath,
                                                superPath));
                }

                return;

                string?GetSubmoduleSuperPath(string submodulePath) =>
                modulePaths.Find(path => submodulePath != path && submodulePath.Contains(path));
            }
            private static SubmoduleInfoResult UpdateSubmoduleStatusAndWaitForResult(ISubmoduleStatusProvider provider, GitModule module)
            {
                SubmoduleInfoResult result = null;
                SemaphoreSlim       onUpdateCompleteSignal = new SemaphoreSlim(0, 1);

                provider.UpdateSubmodulesStatus(
                    updateStatus: false,
                    workingDirectory: module.WorkingDir,
                    noBranchText: string.Empty,
                    onUpdateBegin: () => { },
                    onUpdateCompleteAsync: async(SubmoduleInfoResult submoduleInfoResult, CancellationToken token) =>
                {
                    await TaskScheduler.Default;
                    result = submoduleInfoResult;
                    onUpdateCompleteSignal.Release();
                });

                onUpdateCompleteSignal.Wait();
                return(result);
            }
        public static SubmoduleInfoResult UpdateSubmoduleStatusAndWaitForResult(ISubmoduleStatusProvider provider, GitModule module, bool updateStatus = false)
        {
            SubmoduleInfoResult result = null;

            provider.StatusUpdated += Provider_StatusUpdated;

            provider.UpdateSubmodulesStatus(
                updateStatus: updateStatus,
                workingDirectory: module.WorkingDir,
                noBranchText: string.Empty);

            AsyncTestHelper.WaitForPendingOperations();

            provider.StatusUpdated -= Provider_StatusUpdated;

            return(result);

            void Provider_StatusUpdated(object sender, SubmoduleStatusEventArgs e)
            {
                result = e.Info;
            }
        }
            private Nodes FillSubmoduleTree(SubmoduleInfoResult result)
            {
                var threadModule = (GitModule)result.Module;

                var submoduleNodes = new List <SubmoduleNode>();

                // We always want to display submodules rooted from the top project. If the currently open project is the top project,
                // OurSubmodules contains all child submodules recursively; otherwise, if we're currently in a submodule, SuperSubmodules
                // contains all submodule info relative to the top project.
                if (result.SuperSubmodules?.Count > 0)
                {
                    CreateSubmoduleNodes(result.SuperSubmodules, threadModule, ref submoduleNodes);
                }
                else
                {
                    CreateSubmoduleNodes(result.OurSubmodules, threadModule, ref submoduleNodes);
                }

                var nodes = new Nodes(this);

                AddNodesToTree(ref nodes, submoduleNodes, threadModule, result.TopProject);
                return(nodes);
            }
            private void AddTopAndNodesToTree(
                ref Nodes nodes,
                List <SubmoduleNode> submoduleNodes,
                GitModule threadModule,
                SubmoduleInfoResult result)
            {
                // Create tree of SubmoduleFolderNode for each path directory and add input SubmoduleNodes as leaves.

                // Example of (SuperPath + LocalPath).ToPosixPath() for all nodes:
                //
                // C:/code/gitextensions2/Externals/conemu-inside
                // C:/code/gitextensions2/Externals/Git.hub
                // C:/code/gitextensions2/Externals/ICSharpCode.TextEditor
                // C:/code/gitextensions2/Externals/ICSharpCode.TextEditor/gitextensions
                // C:/code/gitextensions2/Externals/ICSharpCode.TextEditor/gitextensions/Externals/conemu-inside
                // C:/code/gitextensions2/Externals/ICSharpCode.TextEditor/gitextensions/Externals/Git.hub
                // C:/code/gitextensions2/Externals/ICSharpCode.TextEditor/gitextensions/Externals/ICSharpCode.TextEditor
                // C:/code/gitextensions2/Externals/ICSharpCode.TextEditor/gitextensions/Externals/NBug
                // C:/code/gitextensions2/Externals/ICSharpCode.TextEditor/gitextensions/GitExtensionsDoc
                // C:/code/gitextensions2/Externals/NBug
                // C:/code/gitextensions2/GitExtensionsDoc
                //
                // What we want to do is first remove the topModule portion, "C:/code/gitextensions2/", and
                // then build our tree by breaking up each path into parts, separated by '/'.
                //
                // Note that when we break up the paths, some parts are just directories, the others are submodule nodes:
                //
                // Externals / ICSharpCode.TextEditor / gitextensions / Externals / Git.hub
                //  folder          submodule             submodule      folder     submodule
                //
                // Input 'nodes' is an array of SubmoduleNodes for all the submodules; now we need to create SubmoduleFolderNodes
                // and insert everything into a tree.

                var topModule = threadModule.GetTopModule();

                // Build a mapping of top-module-relative path to node
                var pathToNodes = new Dictionary <string, Node>();

                // Add existing SubmoduleNodes
                foreach (var node in submoduleNodes)
                {
                    pathToNodes[GetNodeRelativePath(topModule, node)] = node;
                }

                // Create and add missing SubmoduleFolderNodes
                foreach (var node in submoduleNodes)
                {
                    var parts = GetNodeRelativePath(topModule, node).Split(Delimiters.ForwardSlash);
                    for (int i = 0; i < parts.Length - 1; ++i)
                    {
                        var path = string.Join("/", parts.Take(i + 1));
                        if (!pathToNodes.ContainsKey(path))
                        {
                            pathToNodes[path] = new SubmoduleFolderNode(this, parts[i]);
                        }
                    }
                }

                // Now build the tree
                var rootNode    = new DummyNode();
                var nodesInTree = new HashSet <Node>();

                foreach (var node in submoduleNodes)
                {
                    Node parentNode = rootNode;
                    var  parts      = GetNodeRelativePath(topModule, node).Split(Delimiters.ForwardSlash);
                    for (int i = 0; i < parts.Length; ++i)
                    {
                        var path      = string.Join("/", parts.Take(i + 1));
                        var nodeToAdd = pathToNodes[path];

                        // If node is not already in the tree, add it
                        if (!nodesInTree.Contains(nodeToAdd))
                        {
                            parentNode.Nodes.AddNode(nodeToAdd);
                            nodesInTree.Add(nodeToAdd);
                        }

                        parentNode = nodeToAdd;
                    }
                }

                Validates.NotNull(result.TopProject);

                // Add top-module node, and move children of root to it
                var topModuleNode = new SubmoduleNode(
                    this,
                    result.TopProject,
                    result.TopProject.Bold,
                    result.TopProject.Bold ? result.CurrentSubmoduleStatus : null,
                    "",
                    result.TopProject.Path);

                topModuleNode.Nodes.AddNodes(rootNode.Nodes);
                nodes.AddNode(topModuleNode);
            }
Beispiel #10
0
        private void UpdateSubmodulesList()
        {
            _previousUpdateTime = DateTime.Now;

            // Cancel any previous async activities:
            _submodulesStatusCTS.Cancel();
            _submodulesStatusCTS.Dispose();
            _submodulesStatusCTS = new CancellationTokenSource();

            RemoveSubmoduleButtons();
            toolStripButtonLevelUp.DropDownItems.Add(_loading.Text);

            // Start gathering new submodule information asynchronously.  This makes a significant difference in UI
            // responsiveness if there are numerous submodules (e.g. > 100).
            var cancelToken = _submodulesStatusCTS.Token;
            string thisModuleDir = Module.WorkingDir;
            // First task: Gather list of submodules on a background thread.
            var updateTask = Task.Factory.StartNew(() =>
            {
                // Don't access Module directly because it's not thread-safe.  Use a thread-local version:
                GitModule threadModule = new GitModule(thisModuleDir);
                SubmoduleInfoResult result = new SubmoduleInfoResult();

                // Add all submodules inside the current repository:
                foreach (var submodule in threadModule.GetSubmodulesLocalPaths().OrderBy(submoduleName => submoduleName))
                {
                    cancelToken.ThrowIfCancellationRequested();
                    var name = submodule;
                    string path = threadModule.GetSubmoduleFullPath(submodule);
                    if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
                        name = name + " " + GetModuleBranch(path);

                    var smi = new SubmoduleInfo { Text = name, Path = path };
                    result.OurSubmodules.Add(smi);
                    GetSubmoduleStatusAsync(smi, cancelToken);
                }

                if (threadModule.SuperprojectModule != null)
                {
                    GitModule supersuperproject = threadModule.FindTopProjectModule();
                    if (threadModule.SuperprojectModule.WorkingDir != supersuperproject.WorkingDir)
                    {
                        var name = Path.GetFileName(Path.GetDirectoryName(supersuperproject.WorkingDir));
                        string path = supersuperproject.WorkingDir;
                        if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
                            name = name + " " + GetModuleBranch(path);

                        result.TopProject = new SubmoduleInfo { Text = name, Path = supersuperproject.WorkingDir };
                        GetSubmoduleStatusAsync(result.TopProject, cancelToken);
                    }

                    {
                        string name;
                        GitModule parentModule = threadModule.SuperprojectModule;
                        string localpath = "";
                        if (threadModule.SuperprojectModule.WorkingDir != supersuperproject.WorkingDir)
                        {
                            parentModule = supersuperproject;
                            localpath = threadModule.SuperprojectModule.WorkingDir.Substring(supersuperproject.WorkingDir.Length);
                            localpath = PathUtil.GetDirectoryName(localpath.ToPosixPath());
                            name = localpath;
                        }
                        else
                            name = Path.GetFileName(Path.GetDirectoryName(supersuperproject.WorkingDir));
                        string path = threadModule.SuperprojectModule.WorkingDir;
                        if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
                            name = name + " " + GetModuleBranch(path);

                        result.Superproject = new SubmoduleInfo { Text = name, Path = threadModule.SuperprojectModule.WorkingDir };
                        GetSubmoduleStatusAsync(result.Superproject, cancelToken);
                    }

                    var submodules = supersuperproject.GetSubmodulesLocalPaths().OrderBy(submoduleName => submoduleName);
                    if (submodules.Any())
                    {
                        string localpath = threadModule.WorkingDir.Substring(supersuperproject.WorkingDir.Length);
                        localpath = PathUtil.GetDirectoryName(localpath.ToPosixPath());

                        foreach (var submodule in submodules)
                        {
                            cancelToken.ThrowIfCancellationRequested();
                            var name = submodule;
                            string path = supersuperproject.GetSubmoduleFullPath(submodule);
                            if (Settings.DashboardShowCurrentBranch && !GitModule.IsBareRepository(path))
                                name = name + " " + GetModuleBranch(path);
                            bool bold = false;
                            if (submodule == localpath)
                            {
                                result.CurrentSubmoduleName = threadModule.GetCurrentSubmoduleLocalPath();
                                bold = true;
                            }
                            var smi = new SubmoduleInfo { Text = name, Path = path, Bold = bold };
                            result.SuperSubmodules.Add(smi);
                            GetSubmoduleStatusAsync(smi, cancelToken);
                        }
                    }
                }
                return result;
            }, cancelToken);

            // Second task: Populate toolbar menu on UI thread.  Note further tasks are created by
            // CreateSubmoduleMenuItem to update images with submodule status.
            updateTask.ContinueWith((task) =>
            {
                if (task.Result == null)
                    return;

                RemoveSubmoduleButtons();
                var newItems = new List<ToolStripItem>();

                task.Result.OurSubmodules.ForEach(submodule => newItems.Add(CreateSubmoduleMenuItem(submodule)));
                if (task.Result.OurSubmodules.Count == 0)
                    newItems.Add(new ToolStripMenuItem(_noSubmodulesPresent.Text));

                if (task.Result.Superproject != null)
                {
                    newItems.Add(new ToolStripSeparator());
                    if (task.Result.TopProject != null)
                        newItems.Add(CreateSubmoduleMenuItem(task.Result.TopProject, _topProjectModuleFormat.Text));
                    newItems.Add(CreateSubmoduleMenuItem(task.Result.Superproject, _superprojectModuleFormat.Text));
                    task.Result.SuperSubmodules.ForEach(submodule => newItems.Add(CreateSubmoduleMenuItem(submodule)));
                }

                newItems.Add(new ToolStripSeparator());

                var mi = new ToolStripMenuItem(updateAllSubmodulesToolStripMenuItem.Text);
                mi.Click += UpdateAllSubmodulesToolStripMenuItemClick;
                newItems.Add(mi);

                if (task.Result.CurrentSubmoduleName != null)
                {
                    var usmi = new ToolStripMenuItem(_updateCurrentSubmodule.Text);
                    usmi.Tag = task.Result.CurrentSubmoduleName;
                    usmi.Click += UpdateSubmoduleToolStripMenuItemClick;
                    newItems.Add(usmi);
                }

                // Using AddRange is critical: if you used Add to add menu items one at a
                // time, performance would be extremely slow with many submodules (> 100).
                toolStripButtonLevelUp.DropDownItems.AddRange(newItems.ToArray());

                _previousUpdateTime = DateTime.Now;
            },
                cancelToken,
                TaskContinuationOptions.OnlyOnRanToCompletion,
                TaskScheduler.FromCurrentSynchronizationContext());
        }