using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; namespace SourceGit.Commands { /// /// 用于取消命令执行的上下文对象 /// public class Context { public bool IsCancelRequested { get; set; } = false; } /// /// 命令接口 /// public class Command { private static readonly Regex PROGRESS_REG = new Regex(@"\d+%"); /// /// 读取全部输出时的结果 /// public class ReadToEndResult { public bool IsSuccess { get; set; } public string Output { get; set; } public string Error { get; set; } } /// /// 上下文 /// public Context Ctx { get; set; } = null; /// /// 运行路径 /// public string Cwd { get; set; } = ""; /// /// 参数 /// public string Args { get; set; } = ""; /// /// 是否忽略错误 /// public bool DontRaiseError { get; set; } = false; /// /// 使用标准错误输出 /// public bool TraitErrorAsOutput { get; set; } = false; /// /// 运行 /// public bool Exec() { var start = new ProcessStartInfo(); start.FileName = Models.Preference.Instance.Git.Path; start.Arguments = "--no-pager -c core.quotepath=off " + Args; start.UseShellExecute = false; start.CreateNoWindow = true; start.RedirectStandardOutput = true; start.RedirectStandardError = true; start.StandardOutputEncoding = Encoding.UTF8; start.StandardErrorEncoding = Encoding.UTF8; if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd; var errs = new List(); var proc = new Process() { StartInfo = start }; var isCancelled = false; proc.OutputDataReceived += (o, e) => { if (Ctx != null && Ctx.IsCancelRequested) { isCancelled = true; proc.CancelErrorRead(); proc.CancelOutputRead(); if (!proc.HasExited) proc.Kill(); return; } if (e.Data == null) return; OnReadline(e.Data); }; proc.ErrorDataReceived += (o, e) => { if (Ctx != null && Ctx.IsCancelRequested) { isCancelled = true; proc.CancelErrorRead(); proc.CancelOutputRead(); if (!proc.HasExited) proc.Kill(); return; } if (string.IsNullOrEmpty(e.Data)) return; if (TraitErrorAsOutput) OnReadline(e.Data); // 错误信息中忽略进度相关的输出 if (e.Data.StartsWith("remote: Enumerating objects:", StringComparison.Ordinal)) return; if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal)) return; if (e.Data.StartsWith("remote: Compressing objects:", StringComparison.Ordinal)) return; if (e.Data.StartsWith("Filtering content:", StringComparison.Ordinal)) return; if (PROGRESS_REG.IsMatch(e.Data)) return; errs.Add(e.Data); }; try { proc.Start(); } catch (Exception e) { if (!DontRaiseError) OnException(e.Message); return false; } proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.WaitForExit(); int exitCode = proc.ExitCode; proc.Close(); if (!isCancelled && exitCode != 0 && errs.Count > 0) { if (!DontRaiseError) OnException(string.Join("\n", errs)); return false; } else { return true; } } /// /// 直接读取全部标准输出 /// public ReadToEndResult ReadToEnd() { var start = new ProcessStartInfo(); start.FileName = Models.Preference.Instance.Git.Path; start.Arguments = "--no-pager -c core.quotepath=off " + Args; start.UseShellExecute = false; start.CreateNoWindow = true; start.RedirectStandardOutput = true; start.RedirectStandardError = true; start.StandardOutputEncoding = Encoding.UTF8; start.StandardErrorEncoding = Encoding.UTF8; if (!string.IsNullOrEmpty(Cwd)) start.WorkingDirectory = Cwd; var proc = new Process() { StartInfo = start }; try { proc.Start(); } catch (Exception e) { return new ReadToEndResult() { Output = string.Empty, Error = e.Message, IsSuccess = false, }; } var rs = new ReadToEndResult(); rs.Output = proc.StandardOutput.ReadToEnd(); rs.Error = proc.StandardError.ReadToEnd(); proc.WaitForExit(); rs.IsSuccess = proc.ExitCode == 0; proc.Close(); return rs; } /// /// 调用Exec时的读取函数 /// /// public virtual void OnReadline(string line) { } /// /// 默认异常处理函数 /// /// public virtual void OnException(string message) { Models.Exception.Raise(message); } } }