/// <summary> /// Update the detailed status from the git status /// </summary> /// <param name="module">Current module</param> /// <param name="gitStatus">git status</param> /// <param name="cancelToken">Cancellation token</param> /// <returns>The task</returns> private async Task UpdateSubmodulesStatusAsync(VsrModule module, [CanBeNull] IReadOnlyList <GitItemStatus> gitStatus, CancellationToken cancelToken) { _previousSubmoduleUpdateTime = DateTime.Now; await TaskScheduler.Default; // TopModule is dirty if there are any changes in any module if (gitStatus != null && gitStatus.Count > 0) { SetTopModuleAsDirty(module.GetTopModule().WorkingDir); } else if (module.GetTopModule() == module) { // status includes top module changes to files and 'dirty' can be cleared // (keep 'dirty' if unknown) _submoduleInfos[module.WorkingDir].Detailed = null; } var changedSubmodules = gitStatus.Where(i => i.IsSubmodule); foreach (var submoduleName in module.GetSubmodulesLocalPaths(false).Where(s => !changedSubmodules.Any(i => i.Name == s))) { SetSubmoduleEmptyDetailedStatus(module, submoduleName); } foreach (var submoduleName in changedSubmodules) { cancelToken.ThrowIfCancellationRequested(); await GetSubmoduleDetailedStatusAsync(module, submoduleName.Name, cancelToken); } }
/// <summary> /// Get the detailed submodule status for 'submoduleName' and below /// </summary> /// <param name="superModule">Module to compare to</param> /// <param name="submoduleName">Name of the submodule</param> /// <param name="cancelToken">Cancelation token</param> /// <returns>the task</returns> private async Task GetSubmoduleDetailedStatusAsync(VsrModule superModule, string submoduleName, CancellationToken cancelToken) { if (superModule == null || string.IsNullOrWhiteSpace(submoduleName)) { return; } var path = superModule.GetSubmoduleFullPath(submoduleName); if (!_submoduleInfos.ContainsKey(path) || _submoduleInfos[path] == null) { return; } var info = _submoduleInfos[path]; cancelToken.ThrowIfCancellationRequested(); var submoduleStatus = await GitCommandHelpers.GetCurrentSubmoduleChangesAsync(superModule, submoduleName, noLocks : true) .ConfigureAwait(false); if (submoduleStatus != null && submoduleStatus.Commit != submoduleStatus.OldCommit) { submoduleStatus.CheckSubmoduleStatus(submoduleStatus.GetSubmodule(superModule)); } info.Detailed = submoduleStatus == null ? null : new DetailedSubmoduleInfo() { Status = submoduleStatus.Status, IsDirty = submoduleStatus.IsDirty, AddedAndRemovedText = submoduleStatus.AddedAndRemovedString() }; if (submoduleStatus != null) { SetTopModuleAsDirty(superModule.GetTopModule().WorkingDir); } // Recursively update submodules var module = new VsrModule(path); await GetSubmoduleDetailedStatusAsync(module, cancelToken); }
private void AddNodesToTree( ref Nodes nodes, List <SubmoduleNode> submoduleNodes, VsrModule threadModule, SubmoduleInfo topProject) { // 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('/'); 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('/'); 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; } } // Add top-module node, and move children of root to it var topModuleNode = new SubmoduleNode(this, topProject, topProject.Bold, "", topProject.Path); topModuleNode.Nodes.AddNodes(rootNode.Nodes); nodes.AddNode(topModuleNode); }
/// <inheritdoc /> public void UpdateSubmodulesStructure(string workingDirectory, string noBranchText, bool updateStatus) { _submoduleInfoResult = null; _gitStatusWhileUpdatingStructure = null; _submoduleInfos.Clear(); ThreadHelper.JoinableTaskFactory.RunAsync(async() => { // Cancel any previous async activities: var cancelToken = _submodulesStatusSequence.Next(); // Do not throttle next status update _previousSubmoduleUpdateTime = DateTime.MinValue; OnStatusUpdating(); await TaskScheduler.Default; // Start gathering new submodule structure asynchronously. var currentModule = new VsrModule(workingDirectory); var result = new SubmoduleInfoResult { Module = currentModule }; // Add all submodules inside the current repository: GetRepositorySubmodulesStructure(result, noBranchText); GetSuperProjectRepositorySubmodulesStructure(currentModule, result, noBranchText); // Structure is updated OnStatusUpdated(result, cancelToken); // Prepare info for status updates (normally triggered by StatusMonitor) foreach (var info in result.OurSubmodules) { _submoduleInfos[info.Path] = info; } foreach (var info in result.SuperSubmodules) { _submoduleInfos[info.Path] = info; } if (!_submoduleInfos.ContainsKey(result.TopProject.Path)) { _submoduleInfos.Add(result.TopProject.Path, result.TopProject); } // Start update status for the submodules if (updateStatus) { if (result.SuperProject != null) { // Update from top module (will stop at current) await GetSubmoduleDetailedStatusAsync(currentModule.GetTopModule(), cancelToken); } if (_gitStatusWhileUpdatingStructure != null) { // Current module must be updated separately (not in _submoduleInfos) await UpdateSubmodulesStatusAsync(currentModule, _gitStatusWhileUpdatingStructure, cancelToken); } OnStatusUpdated(result, cancelToken); } _submoduleInfoResult = result; }).FileAndForget(); }