This class encapsulates the Mercurial client application.
Esempio n. 1
0
        /// <summary>
        /// This spins up a Mercurial client in command server mode for the
        /// repository.
        /// </summary>
        private void StartPersistentMercurialClient()
        {
            var psi = new ProcessStartInfo
            {
                FileName               = ClientExecutable.ClientPath,
                WorkingDirectory       = _RepositoryPath,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                CreateNoWindow         = true,
                WindowStyle            = ProcessWindowStyle.Hidden,
                UseShellExecute        = false,
                ErrorDialog            = false,
                Arguments              = "serve --cmdserver pipe --noninteractive" //--encoding cp1251",
            };

            psi.EnvironmentVariables.Add("LANGUAGE", "EN");
            psi.EnvironmentVariables.Remove("HGENCODING");
            psi.EnvironmentVariables.Add("HGENCODING", ClientExecutable.GetMainEncoding().WebName);

            psi.StandardOutputEncoding = ClientExecutable.GetMainEncoding();
            psi.StandardErrorEncoding  = ClientExecutable.GetMainEncoding();

            _Process = Process.Start(psi);
            DecodeInitialBlock();
        }
Esempio n. 2
0
        /// <summary>
        /// Executes the given <see cref="IMercurialCommand"/> command against
        /// the Mercurial repository.
        /// </summary>
        /// <param name="repositoryPath">
        /// The root path of the repository to execute the command in.
        /// </param>
        /// <param name="command">
        /// The <see cref="IMercurialCommand"/> command to execute.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="repositoryPath"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="command"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="MercurialException">
        /// HG did not complete within the allotted time.
        /// </exception>
        public static void Execute(string repositoryPath, IMercurialCommand command)
        {
            if (StringEx.IsNullOrWhiteSpace(repositoryPath))
            {
                throw new ArgumentNullException("repositoryPath");
            }
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }

            ClientExecutable.LazyInitialize();
            var specialArguments = (IEnumerable <string>) new[]
            {
                "--noninteractive", "--encoding", "utf-8",
            };
            var environmentVariables = new[]
            {
                new KeyValuePair <string, string>("LANGUAGE", "EN"), new KeyValuePair <string, string>("HGENCODING", "utf-8"),
            };

            try
            {
                CommandProcessor.Execute(repositoryPath, ClientExecutable.ClientPath, command, environmentVariables, specialArguments);
            }
            finally
            {
                MercurialVersionBase.Current.WaitForLocksToDissipate(repositoryPath);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Decodes and returns a single string from the <see cref="StreamReader"/>.
        /// </summary>
        /// <param name="reader">
        /// The <see cref="StreamReader"/> to decode the string from.
        /// </param>
        /// <returns>
        /// The decoded string or <c>null</c> if the data could not be decoded.
        /// </returns>
        /// <remarks>
        /// Note that this method will not check that <paramref name="reader"/> is non-null.
        /// </remarks>
        private static string DecodeString(StreamReader reader)
        {
            var length = DecodeInt32(reader);

            if (length < 0)
            {
                return(null);
            }

            // max: 1MB, to avoid memory problems with corrupt data
            if (length > 1 * 1024 * 1024)
            {
                return(null);
            }

            var buffer = new byte[length];

            for (int index = 0; index < length; index++)
            {
                int unencodedByte = reader.Read();
                if (unencodedByte == -1)
                {
                    return(null);
                }
                buffer[index] = (byte)unencodedByte;
            }

            var encoding = ClientExecutable.GetMainEncoding();

            return(encoding.GetString(buffer));
        }
Esempio n. 4
0
        /// <summary>
        /// Gets the sequence of arguments to pass to the command line client. This might write out a temporary file on disk,
        /// so be sure to call <see cref="Cleanup"/> when the command has completed execution.
        /// </summary>
        /// <returns>
        /// A collection of arguments to pass to the command line client.
        /// </returns>
        public string[] GetArguments(bool useInPersistentClient)
        {
            var arguments =
                (from argument in _Collection
                 where !StringEx.IsNullOrWhiteSpace(argument)
                 select argument.Trim()).ToArray();

            if (arguments.Length == 0)
            {
                return(arguments);
            }

            if (ClientExecutable.CurrentVersion < new Version(1, 8) || useInPersistentClient)
            {
                return(arguments);
            }

            _ListFileName = Path.GetTempFileName();

            var listFileEncoding = ClientExecutable.GetMainEncoding();

            File.WriteAllText(_ListFileName, string.Join(Environment.NewLine, arguments), listFileEncoding);

            return(new[] { string.Format(CultureInfo.InvariantCulture, "\"listfile:{0}\"", _ListFileName) });
        }
Esempio n. 5
0
        /// <summary>
        /// Executes the given <see cref="IMercurialCommand"/> command without
        /// a repository.
        /// </summary>
        /// <param name="command">
        /// The <see cref="IMercurialCommand"/> command to execute.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="command"/> is <c>null</c>.</para>
        /// </exception>
        public static void Execute(IMercurialCommand command)
        {
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }

            ClientExecutable.LazyInitialize();
            Execute(Path.GetTempPath(), command);
        }
Esempio n. 6
0
        private int RunCommand(IList <string> command,
                               IDictionary <CommandChannel, Stream> outputs,
                               IDictionary <CommandChannel, Func <uint, byte[]> > inputs)
        {
            if (null == command || 0 == command.Count)
            {
                throw new ArgumentException("Command must not be empty", "command");
            }

            var _codec = ClientExecutable.GetMainEncoding();

            byte[] commandBuffer = _codec.GetBytes("runcommand\n");
            byte[] argumentBuffer;

            argumentBuffer = command.Aggregate(new List <byte>(), (bytes, arg) => {
                bytes.AddRange(_codec.GetBytes(arg));
                bytes.Add(0);
                return(bytes);
            },
                                               bytes => {
                bytes.RemoveAt(bytes.Count - 1);
                return(bytes.ToArray());
            }
                                               ).ToArray();

            byte[] lengthBuffer = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(argumentBuffer.Length));

            lock (_Process)
            {
                _Process.StandardInput.BaseStream.Write(commandBuffer, 0, commandBuffer.Length);
                _Process.StandardInput.BaseStream.Write(lengthBuffer, 0, lengthBuffer.Length);
                _Process.StandardInput.BaseStream.Write(argumentBuffer, 0, argumentBuffer.Length);
                _Process.StandardInput.BaseStream.Flush();

                return(ReadCommandOutputs(command, outputs, inputs));
            }// lock _Process
        }
Esempio n. 7
0
        /// <summary>
        /// Executes the given <see cref="IMercurialCommand"/> command against
        /// the Mercurial repository.
        /// </summary>
        /// <param name="command">
        /// The <see cref="IMercurialCommand"/> command to execute.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="command"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="MercurialException">
        /// HG did not complete within the allotted time.
        /// </exception>
        public void Execute(IMercurialCommand command)
        {
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }

            if (_Process == null)
            {
                StartPersistentMercurialClient();
            }

            command.Validate();
            command.Before();

            IEnumerable <string> arguments = new[]
            {
                command.Command,
                "--noninteractive",
            };

            arguments = arguments.Concat(command.Arguments.Where(a => !StringEx.IsNullOrWhiteSpace(a)));
            arguments = arguments.Concat(command.AdditionalArguments.Where(a => !StringEx.IsNullOrWhiteSpace(a)));

            var commandParts = arguments.ToArray();

            string commandEncoded = string.Join("\0", commandParts.Select(p => p.Trim('"')).ToArray());
            int    length         = commandEncoded.Length;
            var    commandBuffer  = new StringBuilder();

            commandBuffer.Append("runcommand\n");
            commandBuffer.Append((char)((length >> 24) & 0xff));
            commandBuffer.Append((char)((length >> 16) & 0xff));
            commandBuffer.Append((char)((length >> 8) & 0xff));
            commandBuffer.Append((char)(length & 0xff));
            commandBuffer.Append(commandEncoded);

            string commandArguments = null;

            if (command.Observer != null)
            {
                commandArguments = string.Join(" ", commandParts.Skip(1).ToArray());
                command.Observer.Executing(command.Command, commandArguments);
            }

            MemoryStream output  = new MemoryStream();
            MemoryStream error   = new MemoryStream();
            var          outputs = new Dictionary <CommandChannel, Stream>()
            {
                { CommandChannel.Output, output },
                { CommandChannel.Error, error },
            };

            var _codec = ClientExecutable.GetMainEncoding();

            int resultCode = RunCommand(commandParts, outputs, null);
            var result     = new CommandResult(_codec.GetString(output.GetBuffer(), 0, (int)output.Length),
                                               _codec.GetString(error.GetBuffer(), 0, (int)error.Length),
                                               resultCode);

            if (resultCode == 0 || !string.IsNullOrEmpty(result.Output))
            {
                if (command.Observer != null)
                {
                    command.Observer.Output(result.Output);
                    command.Observer.ErrorOutput(result.Error);
                    command.Observer.Executed(command.Command, commandArguments, resultCode, result.Output, result.Error);
                }
                command.After(resultCode, result.Output, result.Error);
                return;
            }

            StopPersistentMercurialClient();
            throw new MercurialExecutionException(
                      string.IsNullOrEmpty(result.Error) ?
                      "Unable to decode output from executing command, spinning down persistent client"
                : result.Error);
        }
Esempio n. 8
0
        /// <summary>
        /// Executes the given executable to process the given command.
        /// </summary>
        /// <param name="workingDirectory">
        /// The working directory while executing the command.
        /// </param>
        /// <param name="executable">
        /// The full path to and name of the executable to execute.
        /// </param>
        /// <param name="command">
        /// The options to the executable.
        /// </param>
        /// <param name="environmentVariables">
        /// An array of <see cref="KeyValuePair{TKey,TValue}"/> objects, containing environment variable
        /// overrides to use while executing the executable.
        /// </param>
        /// <param name="specialArguments">
        /// Any special arguments to pass to the executable, not defined by the <paramref name="command"/>
        /// object, typically common arguments that should always be passed to the executable.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <para><paramref name="workingDirectory"/> is <c>null</c> or empty.</para>
        /// <para>- or -</para>
        /// <para><paramref name="executable"/> is <c>null</c> or empty.</para>
        /// <para>- or -</para>
        /// <para><paramref name="command"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="environmentVariables"/> is <c>null</c>.</para>
        /// <para>- or -</para>
        /// <para><paramref name="specialArguments"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="MercurialException">
        /// <para>The executable did not finish in the allotted time.</para>
        /// </exception>
        public static void Execute(
            string workingDirectory, string executable, ICommand command, KeyValuePair <string, string>[] environmentVariables,
            IEnumerable <string> specialArguments)
        {
            if (StringEx.IsNullOrWhiteSpace(workingDirectory))
            {
                throw new ArgumentNullException("workingDirectory");
            }
            if (StringEx.IsNullOrWhiteSpace(executable))
            {
                throw new ArgumentNullException("executable");
            }
            if (command == null)
            {
                throw new ArgumentNullException("command");
            }
            if (environmentVariables == null)
            {
                throw new ArgumentNullException("environmentVariables");
            }
            if (specialArguments == null)
            {
                throw new ArgumentNullException("specialArguments");
            }

            command.Validate();
            command.Before();

            IEnumerable <string> arguments = specialArguments;

            arguments = arguments.Concat(command.Arguments.Where(a => !StringEx.IsNullOrWhiteSpace(a)));
            arguments = arguments.Concat(command.AdditionalArguments.Where(a => !StringEx.IsNullOrWhiteSpace(a)));

            string argumentsString = string.Join(" ", arguments.ToArray());

            var psi = new ProcessStartInfo
            {
                FileName               = executable,
                WorkingDirectory       = workingDirectory,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                CreateNoWindow         = true,
                WindowStyle            = ProcessWindowStyle.Hidden,
                UseShellExecute        = false,
                ErrorDialog            = false,
                Arguments              = command.Command + " " + argumentsString,
            };

            foreach (var kvp in environmentVariables)
            {
                psi.EnvironmentVariables[kvp.Key] = kvp.Value;
            }
            ClientExecutable.LazyInitialize();
            psi.StandardErrorEncoding  = ClientExecutable.GetMainEncoding();
            psi.StandardOutputEncoding = ClientExecutable.GetMainEncoding();

            if (command.Observer != null)
            {
                command.Observer.Executing(command.Command, argumentsString);
            }

            Process process = Process.Start(psi);

            try
            {
                Func <StreamReader, Action <string>, string> reader;
                if (command.Observer != null)
                {
                    reader = delegate(StreamReader streamReader, Action <string> logToObserver)
                    {
                        var    output = new StringBuilder();
                        string line;
                        while ((line = streamReader.ReadLine()) != null)
                        {
                            logToObserver(line);
                            if (output.Length > 0)
                            {
                                output.Append(Environment.NewLine);
                            }
                            output.Append(line);
                        }
                        return(output.ToString());
                    };
                }
                else
                {
                    reader = (StreamReader streamReader, Action <string> logToObserver) => streamReader.ReadToEnd();
                }

                IAsyncResult outputReader = reader.BeginInvoke(process.StandardOutput, line => command.Observer.Output(line), null, null);
                IAsyncResult errorReader  = reader.BeginInvoke(process.StandardError, line => command.Observer.ErrorOutput(line), null, null);

                int timeout = Timeout.Infinite;
                if (command.Timeout > 0)
                {
                    timeout = 1000 * command.Timeout;
                }

                if (!process.WaitForExit(timeout))
                {
                    if (command.Observer != null)
                    {
                        command.Observer.Executed(psi.FileName, psi.Arguments, 0, string.Empty, string.Empty);
                    }
                    throw new MercurialException("The executable did not complete within the allotted time");
                }

                string standardOutput = reader.EndInvoke(outputReader);
                string errorOutput    = reader.EndInvoke(errorReader);

                if (command.Observer != null)
                {
                    command.Observer.Executed(command.Command, argumentsString, process.ExitCode, standardOutput, errorOutput);
                }

                command.After(process.ExitCode, standardOutput, errorOutput);
            }
            finally
            {
                process.Dispose();
            }
        }
Esempio n. 9
0
 /// <summary>
 /// Override this method to implement code that will execute before command
 /// line execution.
 /// </summary>
 protected override void Prepare()
 {
     _MessageFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Replace("-", string.Empty).ToLowerInvariant() + ".txt");
     File.WriteAllText(_MessageFilePath, Message, ClientExecutable.GetMainEncoding());
 }