Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix possible standard output deadlock in external git handler #33

Merged
merged 4 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions src/RepoCleaner/Git/External/GitHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace Develix.RepoCleaner.Git.External;

internal class GitHandler : IGitHandler
{
private const string FormattedGitBranchArguments = "branch --format \"%(HEAD)\t%(refname)\t%(refname:short)\t%(upstream:track)\t%(authordate:unix)\t%(authorname)\" --all";

public IReadOnlyList<Result> DeleteBranches(string repositoryPath, IEnumerable<Branch> branches)
{
var results = new List<Result>();
Expand Down Expand Up @@ -61,26 +63,22 @@ private static Result DeleteBranch(string repositoryPath, Branch branch)
var arguments = $"branch -D {branch.FriendlyName}";
var processStartInfo = GetGitProcessStartInfo(repositoryPath, arguments);

var process = Process.Start(processStartInfo) ?? throw new InvalidOperationException("The git process could not start");
process.WaitForExit();
var (_, errorLines, exitCode) = RunProcess(processStartInfo);

return process.ExitCode == 0
return exitCode == 0
? Result.Ok()
: Result.Fail(process.StandardError.ReadToEnd());
: Result.Fail(string.Join(Environment.NewLine, errorLines));
}

private static Result<IEnumerable<Branch>> GetBranches(string path)
{
var processStartInfo = GetGitProcessStartInfo(
path,
"branch --format \"%(HEAD)\t%(refname)\t%(refname:short)\t%(upstream:track)\t%(authordate:unix)\t%(authorname)\" --all");
var processStartInfo = GetGitProcessStartInfo(path, FormattedGitBranchArguments);

var process = Process.Start(processStartInfo) ?? throw new UnreachableException("The git process could not start");
process.WaitForExit();
var (outputLines, errorLines, exitCode) = RunProcess(processStartInfo);

return process.ExitCode != 0
? Result.Fail<IEnumerable<Branch>>(process.StandardError.ReadToEnd())
: Result.Ok(ParseBranchOutput(process.StandardOutput));
return exitCode == 0
? Result.Ok(ParseBranchOutput(outputLines))
: Result.Fail<IEnumerable<Branch>>(string.Join(Environment.NewLine, errorLines));
}

private static ProcessStartInfo GetGitProcessStartInfo(string path, string arguments)
Expand All @@ -92,13 +90,33 @@ private static ProcessStartInfo GetGitProcessStartInfo(string path, string argum
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
WorkingDirectory = path
WorkingDirectory = path,
};
}

private static IEnumerable<Branch> ParseBranchOutput(StreamReader standardOutput)
private static (List<string> outputLines, List<string> errorLines, int exitCode) RunProcess(ProcessStartInfo processStartInfo)
{
using var process = Process.Start(processStartInfo) ?? throw new UnreachableException("The git process could not start");
List<string> standardOutput = [];
List<string> standardError = [];
process.OutputDataReceived += (s, e) => Add(e.Data, standardOutput);
process.ErrorDataReceived += (s, e) => Add(e.Data, standardError);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();

return (standardOutput, standardError, process.ExitCode);

static void Add(string? data, List<string> outputLines)
{
if (data is not null)
outputLines.Add(data);
}
}

private static IEnumerable<Branch> ParseBranchOutput(IEnumerable<string> outputLines)
{
while (standardOutput.ReadLine() is { } line)
foreach (var line in outputLines)
{
var split = line
.Split("\t")
Expand Down
2 changes: 1 addition & 1 deletion src/RepoCleaner/RepoCleaner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<RootNamespace>Develix.RepoCleaner</RootNamespace>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Version>1.8.0</Version>
<Version>1.8.1</Version>
</PropertyGroup>

<ItemGroup>
Expand Down