Skip to content

Commit

Permalink
Merge pull request #6372 from jbialobr/jb/fix#6357
Browse files Browse the repository at this point in the history
Serialize submodules info loading
  • Loading branch information
jbialobr committed Apr 2, 2019
2 parents aa74f4d + b2fdd78 commit 703f607
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 147 deletions.
24 changes: 22 additions & 2 deletions GitCommands/Git/ExecutableExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
Expand Down Expand Up @@ -183,8 +184,6 @@ string ComposeOutput()
Encoding outputEncoding = null,
bool stripAnsiEscapeCodes = true)
{
// TODO make this method async, maybe via IAsyncEnumerable<...>?

if (outputEncoding == null)
{
outputEncoding = _defaultOutputEncoding.Value;
Expand Down Expand Up @@ -226,6 +225,27 @@ string ComposeOutput()
}
}

/// <summary>
/// Launches a process for the executable and returns output lines as they become available.
/// </summary>
/// <param name="executable">The executable from which to launch a process.</param>
/// <param name="arguments">The arguments to pass to the executable</param>
/// <param name="writeInput">A callback that writes bytes to the process's standard input stream, or <c>null</c> if no input is required.</param>
/// <param name="outputEncoding">The text encoding to use when decoding bytes read from the process's standard output and standard error streams, or <c>null</c> if the default encoding is to be used.</param>
/// <param name="stripAnsiEscapeCodes">A flag indicating whether ANSI escape codes should be removed from output strings.</param>
/// <returns>An enumerable sequence of lines that yields lines as they become available. Lines from standard output are returned first, followed by lines from standard error.</returns>
[MustUseReturnValue("If output lines are not required, use " + nameof(RunCommand) + " instead")]
public static async Task<IEnumerable<string>> GetOutputLinesAsync(
this IExecutable executable,
ArgumentString arguments = default,
Action<StreamWriter> writeInput = null,
Encoding outputEncoding = null,
bool stripAnsiEscapeCodes = true)
{
var result = await executable.ExecuteAsync(arguments, writeInput, outputEncoding, stripAnsiEscapeCodes);
return result.StandardOutput.SplitLines().Concat(result.StandardError.SplitLines());
}

/// <summary>
/// Launches a process for the executable and returns an object detailing exit code, standard output and standard error values.
/// </summary>
Expand Down
10 changes: 6 additions & 4 deletions GitCommands/Git/GitCommandHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using GitCommands.Git;
using GitCommands.Git.Extensions;
using GitCommands.Patches;
using GitCommands.Utils;
using GitUIPluginInterfaces;
using JetBrains.Annotations;
using Microsoft.VisualStudio.Threading;

namespace GitCommands
{
Expand Down Expand Up @@ -494,17 +496,17 @@ public static ArgumentString GetAllChangedFilesCmd(bool excludeIgnoredFiles, Unt
}

[CanBeNull]
public static GitSubmoduleStatus GetCurrentSubmoduleChanges(GitModule module, string fileName, string oldFileName, bool staged, bool noLocks = false)
public static async Task<GitSubmoduleStatus> GetCurrentSubmoduleChangesAsync(GitModule module, string fileName, string oldFileName, bool staged, bool noLocks = false)
{
Patch patch = module.GetCurrentChanges(fileName, oldFileName, staged, "", noLocks: noLocks);
Patch patch = await module.GetCurrentChangesAsync(fileName, oldFileName, staged, "", noLocks: noLocks).ConfigureAwait(false);
string text = patch != null ? patch.Text : "";
return ParseSubmoduleStatus(text, module, fileName);
}

[CanBeNull]
public static GitSubmoduleStatus GetCurrentSubmoduleChanges(GitModule module, string submodule, bool noLocks = false)
public static Task<GitSubmoduleStatus> GetCurrentSubmoduleChangesAsync(GitModule module, string submodule, bool noLocks = false)
{
return GetCurrentSubmoduleChanges(module, submodule, submodule, false, noLocks: noLocks);
return GetCurrentSubmoduleChangesAsync(module, submodule, submodule, false, noLocks: noLocks);
}

[CanBeNull]
Expand Down
44 changes: 22 additions & 22 deletions GitCommands/Git/GitModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -696,19 +696,19 @@ string FindAvailableFileName(string basePath)
}
}

public ConflictData GetConflict(string filename)
public async Task<ConflictData> GetConflictAsync(string filename)
{
return GetConflicts(filename).SingleOrDefault();
return (await GetConflictsAsync(filename)).SingleOrDefault();
}

public List<ConflictData> GetConflicts(string filename = "")
public async Task<List<ConflictData>> GetConflictsAsync(string filename = "")
{
filename = filename.ToPosixPath();

var list = new List<ConflictData>();

var unmerged = _gitExecutable
.GetOutput("ls-files -z --unmerged " + filename.QuoteNE())
var unmerged = (await _gitExecutable
.GetOutputAsync("ls-files -z --unmerged " + filename.QuoteNE()).ConfigureAwait(false))
.Split(new[] { '\0', '\n' }, StringSplitOptions.RemoveEmptyEntries);

var item = new ConflictedFileData[3];
Expand Down Expand Up @@ -762,7 +762,7 @@ public IReadOnlyList<string> GetSortedRefs()
return tree.Split();
}

public Dictionary<IGitRef, IGitItem> GetSubmoduleItemsForEachRef(string filename, Func<IGitRef, bool> showRemoteRef, bool noLocks = false)
public async Task<Dictionary<IGitRef, IGitItem>> GetSubmoduleItemsForEachRefAsync(string filename, Func<IGitRef, bool> showRemoteRef, bool noLocks = false)
{
string command = GetSortedRefsCommand(noLocks: noLocks);

Expand All @@ -773,7 +773,7 @@ public IReadOnlyList<string> GetSortedRefs()

filename = filename.ToPosixPath();

var refList = _gitExecutable.GetOutput(command);
var refList = await _gitExecutable.GetOutputAsync(command).ConfigureAwait(false);

var refs = ParseRefs(refList);

Expand Down Expand Up @@ -1190,7 +1190,7 @@ public bool TryResolvePartialCommitId(string objectIdPrefix, out ObjectId object
return false;
}

public (char code, ObjectId currentCommitId) GetSuperprojectCurrentCheckout()
public async Task<(char code, ObjectId currentCommitId)> GetSuperprojectCurrentCheckoutAsync()
{
if (SuperprojectModule == null)
{
Expand All @@ -1203,7 +1203,8 @@ public bool TryResolvePartialCommitId(string objectIdPrefix, out ObjectId object
"--cached",
SubmodulePath.Quote()
};
var lines = SuperprojectModule.GitExecutable.GetOutput(args).Split('\n');
var output = await SuperprojectModule.GitExecutable.GetOutputAsync(args).ConfigureAwait(false);
var lines = output.Split('\n');

if (lines.Length == 0)
{
Expand Down Expand Up @@ -2159,9 +2160,9 @@ public IReadOnlyList<string> GetRemoteNames()

private static readonly Regex _remoteVerboseLineRegex = new Regex(@"^(?<name>[^ ]+)\t(?<url>.+?) \((?<direction>fetch|push)\)$", RegexOptions.Compiled);

public IReadOnlyList<Remote> GetRemotes()
public async Task<IReadOnlyList<Remote>> GetRemotesAsync()
{
return ParseRemotes(_gitExecutable.GetOutputLines("remote -v"));
return ParseRemotes(await _gitExecutable.GetOutputLinesAsync("remote -v"));

IReadOnlyList<Remote> ParseRemotes(IEnumerable<string> lines)
{
Expand Down Expand Up @@ -2500,9 +2501,8 @@ private void GetCurrentSubmoduleStatus(IReadOnlyList<GitItemStatus> status)
var localItem = item;
localItem.SetSubmoduleStatus(ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
{
await TaskScheduler.Default.SwitchTo(alwaysYield: true);
var submoduleStatus = GitCommandHelpers.GetCurrentSubmoduleChanges(this, localItem.Name, localItem.OldName, localItem.Staged == StagedStatus.Index);
var submoduleStatus = await GitCommandHelpers.GetCurrentSubmoduleChangesAsync(this, localItem.Name, localItem.OldName, localItem.Staged == StagedStatus.Index)
.ConfigureAwait(false);
if (submoduleStatus != null && submoduleStatus.Commit != submoduleStatus.OldCommit)
{
var submodule = submoduleStatus.GetSubmodule(this);
Expand Down Expand Up @@ -2617,41 +2617,41 @@ public bool IsDirtyDir()
}

[CanBeNull]
public Patch GetCurrentChanges(string fileName, [CanBeNull] string oldFileName, bool staged, string extraDiffArguments, Encoding encoding = null, bool noLocks = false)
public async Task<Patch> GetCurrentChangesAsync(string fileName, [CanBeNull] string oldFileName, bool staged, string extraDiffArguments, Encoding encoding = null, bool noLocks = false)
{
var output = _gitExecutable.GetOutput(GetCurrentChangesCmd(fileName, oldFileName, staged, extraDiffArguments, noLocks),
outputEncoding: LosslessEncoding);
var output = await _gitExecutable.GetOutputAsync(GetCurrentChangesCmd(fileName, oldFileName, staged, extraDiffArguments, noLocks),
outputEncoding: LosslessEncoding).ConfigureAwait(false);

IReadOnlyList<Patch> patches = PatchProcessor.CreatePatchesFromString(output, new Lazy<Encoding>(() => encoding ?? FilesEncoding)).ToList();

return GetPatch(patches, fileName, oldFileName);
}

[CanBeNull]
private string GetFileContents(string path)
private async Task<string> GetFileContentsAsync(string path)
{
var args = new GitArgumentBuilder("show") { $"HEAD:{path.ToPosixPath().Quote()}" };
var result = _gitExecutable.Execute(args);
var result = await _gitExecutable.ExecuteAsync(args).ConfigureAwaitRunInline();

return result.ExitCode == 0
? result.StandardOutput
: null;
}

[CanBeNull]
public string GetFileContents(GitItemStatus file)
public async Task<string> GetFileContentsAsync(GitItemStatus file)
{
var contents = new StringBuilder();

string currentContents = GetFileContents(file.Name);
string currentContents = await GetFileContentsAsync(file.Name).ConfigureAwaitRunInline();
if (currentContents != null)
{
contents.Append(currentContents);
}

if (file.OldName != null)
{
string oldContents = GetFileContents(file.OldName);
string oldContents = await GetFileContentsAsync(file.OldName).ConfigureAwaitRunInline();
if (oldContents != null)
{
contents.Append(oldContents);
Expand Down
31 changes: 14 additions & 17 deletions GitCommands/Submodules/SubmoduleStatusProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ private void OnStatusUpdated(SubmoduleInfoResult info, CancellationToken token)

private async Task GetRepositorySubmodulesStatusAsync(bool updateStatus, SubmoduleInfoResult result, IGitModule module, CancellationToken cancelToken, string noBranchText)
{
List<Task> tasks = new List<Task>();
await TaskScheduler.Default;

foreach (var submodule in module.GetSubmodulesLocalPaths().OrderBy(submoduleName => submoduleName))
{
cancelToken.ThrowIfCancellationRequested();
Expand All @@ -145,20 +146,17 @@ private async Task GetRepositorySubmodulesStatusAsync(bool updateStatus, Submodu

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

await Task.WhenAll(tasks);
}

private async Task GetSubmoduleStatusAsync(bool updateStatus, SubmoduleInfo info, CancellationToken cancelToken)
private void GetSubmoduleStatus(bool updateStatus, SubmoduleInfo info, CancellationToken cancelToken)
{
if (!updateStatus)
{
return;
}

await TaskScheduler.Default;
cancelToken.ThrowIfCancellationRequested();

var submodule = new GitModule(info.Path);
Expand All @@ -172,10 +170,9 @@ private async Task GetSubmoduleStatusAsync(bool updateStatus, SubmoduleInfo info

info.Detailed = new AsyncLazy<DetailedSubmoduleInfo>(async () =>
{
await TaskScheduler.Default;
cancelToken.ThrowIfCancellationRequested();
var submoduleStatus = GitCommandHelpers.GetCurrentSubmoduleChanges(supermodule, submoduleName, noLocks: true);
var submoduleStatus = await GitCommandHelpers.GetCurrentSubmoduleChangesAsync(supermodule, submoduleName, noLocks: true).ConfigureAwait(false);
if (submoduleStatus != null && submoduleStatus.Commit != submoduleStatus.OldCommit)
{
submoduleStatus.CheckSubmoduleStatus(submoduleStatus.GetSubmodule(supermodule));
Expand Down Expand Up @@ -213,7 +210,7 @@ private async Task GetSuperProjectRepositorySubmodulesStatusAsync(bool updateSta
bool isParentTopProject = module.SuperprojectModule.WorkingDir == topProject.WorkingDir;

// Set result.SuperProject
await SetSuperProjectSubmoduleInfoAsync(updateStatus, result, module, cancelToken, noBranchText, topProject, isParentTopProject);
SetSuperProjectSubmoduleInfo(updateStatus, result, module, cancelToken, noBranchText, topProject, isParentTopProject);

// Set result.TopProject
await SetTopProjectSubmoduleInfoAsync(updateStatus, result, module, cancelToken, noBranchText, topProject, isParentTopProject);
Expand All @@ -222,7 +219,7 @@ private async Task GetSuperProjectRepositorySubmodulesStatusAsync(bool updateSta
await SetSubmoduleDataAsync(updateStatus, result, module, cancelToken, noBranchText, topProject);
}

private async Task SetSuperProjectSubmoduleInfoAsync(bool updateStatus, SubmoduleInfoResult result, GitModule module, CancellationToken cancelToken, string noBranchText, IGitModule topProject, bool isParentTopProject)
private void SetSuperProjectSubmoduleInfo(bool updateStatus, SubmoduleInfoResult result, GitModule module, CancellationToken cancelToken, string noBranchText, IGitModule topProject, bool isParentTopProject)
{
string name;
if (isParentTopProject)
Expand All @@ -239,11 +236,13 @@ private async Task SetSuperProjectSubmoduleInfoAsync(bool updateStatus, Submodul
string path = module.SuperprojectModule.WorkingDir;
name += GetBranchNameSuffix(path, noBranchText);
result.SuperProject = new SubmoduleInfo { Text = name, Path = module.SuperprojectModule.WorkingDir };
await GetSubmoduleStatusAsync(updateStatus, result.SuperProject, cancelToken);
GetSubmoduleStatus(updateStatus, result.SuperProject, cancelToken);
}

private async Task SetTopProjectSubmoduleInfoAsync(bool updateStatus, SubmoduleInfoResult result, GitModule module, CancellationToken cancelToken, string noBranchText, IGitModule topProject, bool isParentTopProject)
{
await TaskScheduler.Default;

if (isParentTopProject)
{
result.TopProject = result.SuperProject;
Expand All @@ -253,20 +252,20 @@ private async Task SetTopProjectSubmoduleInfoAsync(bool updateStatus, SubmoduleI
string path = topProject.WorkingDir;
string name = Path.GetFileName(Path.GetDirectoryName(topProject.WorkingDir)) + GetBranchNameSuffix(path, noBranchText);
result.TopProject = new SubmoduleInfo { Text = name, Path = topProject.WorkingDir };
await GetSubmoduleStatusAsync(updateStatus, result.TopProject, cancelToken);
GetSubmoduleStatus(updateStatus, result.TopProject, cancelToken);
}
}

private async Task SetSubmoduleDataAsync(bool updateStatus, SubmoduleInfoResult result, GitModule module, CancellationToken cancelToken, string noBranchText, IGitModule topProject)
{
await TaskScheduler.Default;

var submodules = topProject.GetSubmodulesLocalPaths().OrderBy(submoduleName => submoduleName).ToArray();
if (submodules.Any())
{
string localPath = module.WorkingDir.Substring(topProject.WorkingDir.Length);
localPath = PathUtil.GetDirectoryName(localPath.ToPosixPath());

List<Task> subTasks = new List<Task>();

foreach (var submodule in submodules)
{
cancelToken.ThrowIfCancellationRequested();
Expand All @@ -282,10 +281,8 @@ private async Task SetSubmoduleDataAsync(bool updateStatus, SubmoduleInfoResult

var smi = new SubmoduleInfo { Text = name, Path = path, Bold = bold };
result.SuperSubmodules.Add(smi);
subTasks.Add(GetSubmoduleStatusAsync(updateStatus, smi, cancelToken));
GetSubmoduleStatus(updateStatus, smi, cancelToken);
}

await Task.WhenAll(subTasks);
}
}

Expand Down
Loading

0 comments on commit 703f607

Please sign in to comment.