从 C# 程序可靠地运行 Mercurial 的“hg status”

问题描述 投票:0回答:1

我已经编写了一个 C# 程序来充当 Mercurial 的预提交挂钩,但我能让它可靠工作的唯一方法有点复杂,所以我一定错过了一些东西。我在 Windows 10 Pro 64 位上运行 TortoiseHg 6.4.5,该程序面向 .NET 6.0。

目标是获取已更改文件的列表并在提交之前对其进行处理。最明显的事情一开始似乎有效:

IEnumerable<string> GetHgStatus()
{
    // never terminates when a large number of files have been changed, and if I
    // kill the hung hg.exe process there's nothing in the stdout/stderr streams.

    // run exe: hg status -n -m -a
    var psi = new ProcessStartInfo
    {
        // check status:
        // -n = don't output leading status indicator, eg. M | A | R
        // -m = show only modified files
        // -a = show only files that have been added
        FileName = config.HgPath,
        Arguments = $"status -n -m -a",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        CreateNoWindow = true,
        WorkingDirectory = config.RepoPath,
    };
    var exe = Process.Start(psi);
    try
    {
        exe.WaitForExit(); // never returns
        while (!exe.StandardOutput.EndOfStream)
            yield return exe.StandardOutput.ReadLine();
    }
    finally
    {
        exe.Dispose();
    }
}

但是,如果更改的文件列表很长,则等待永远不会完成。我在任务列表中看到一个 hg.exe 实例,分配了大约 57-60MB 的空间,但什么也不做。如果我杀死它,则不会将任何结果输出到 stdout 或 stderr。

从命令行运行此命令时我从未遇到过问题,因此我尝试在 cmd shell 中运行它:

IEnumerable<string> GetHgStatus()
{
    // never terminates when a large number of files have been changed, and if I
    // kill the hung hg.exe process there's nothing in the stdout/stderr streams.
    // run exe: hg status -n -m -a
    var psi = new ProcessStartInfo
    {
        // check status:
        // -n = don't output leading status indicator, eg. M | A | R
        // -m = show only modified files
        // -a = show only files that have been added
        FileName = "cmd.exe",
        Arguments = $"/c \"{config.HgPath}\" status -n -m -a",
        UseShellExecute = false,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        CreateNoWindow = true,
        WorkingDirectory = config.RepoPath,
    };
    var exe = Process.Start(psi);
    try
    {
        exe.WaitForExit(); // never returns
        while (!exe.StandardOutput.EndOfStream)
            yield return exe.StandardOutput.ReadLine();
    }
    finally
    {
        exe.Dispose();
    }
}

这与第一个版本具有完全相同的行为。如果我将 CreateNoWindow 选项切换为 false,那么我可以看到 hg 创建一个命令 shell 窗口,然后它就坐在那里。

我可以让它可靠地工作的唯一方法是放弃重定向 stdout/stderr,并将输出重定向到我然后读入的临时文件:

IEnumerable<string> GetHgStatus()
{
    // have to loop catching file access exceptions until the file is accessible

    // run exe: hg status -n -m -a
    var output = Path.GetTempFileName();
    var psi = new ProcessStartInfo
    {
        // check status:
        // -n = don't output leading status indicator, eg. M | A | R
        // -m = show only modified files
        // -a = show only files that have been added
        FileName = "cmd.exe",
        Arguments = $"/c \"{config.HgPath}\" status -n -m -a > {output}",
        UseShellExecute = false,
        CreateNoWindow = true,
        WorkingDirectory = config.RepoPath,
    };
    var exe = Process.Start(psi);
    try
    {
        exe.WaitForExit();
        // have to call Close() then poll until file is accessible
        exe.Close();
        do
        {
            try
            {
                return File.ReadAllLines(output);
            }
            catch (IOException e) when (e.Message.StartsWith("The process cannot access the file"))
            {
                continue;
            }
        } while (true);
    }
    finally
    {
        exe.Dispose();
        File.Delete(output);
    }
}

那么两个问题:

  1. 我错过了什么,我无法直接运行 hg.exe 并重定向其标准输出,而我必须通过 shell 间接执行此操作,然后将其输出重定向到文件?
  2. 是否有更好的方法来等待或知道进程的打开文件句柄已关闭,以便我可以避免此旋转循环?
c# process mercurial system.diagnostics mercurial-hook
1个回答
0
投票

hg.exe 是否在等待某些内容消耗其输出缓冲区,然后再写入更多内容?也许需要在进程对象上设置一些附加属性,以告诉它始终在 sdout 可用时立即读取它。

这里有一些例子...也许您需要在直播中

ReadToEnd
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.standardoutput?view=net-7.0#remarks

© www.soinside.com 2019 - 2024. All rights reserved.