public static string GetPropertyValue(FilePath svnExecutableFilePath, AbsolutePath path, string propertyName, ILogger logger)
        {
            logger.LogDebug($"Getting value of SVN property {propertyName} for {path}...");

            var arguments = $@"propget {propertyName} ""{path}"" --xml";

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            // Parse output.
            var svnOutput = outputCollector.GetOutputText();

            var document = XDocument.Parse(svnOutput);

            var properties = document.Element("properties");

            if (!properties.Elements().Any())
            {
                throw new Exception($"SVN automation failure.\nReceived:\n{svnOutput}");
            }

            var expectedPath = PathUtilities.EnsureNonWindowsDirectorySeparator(path.Value); // Path value is converted to a non-Windows path.

            var property = properties.Elements("target").Where(x => x.Attribute("path").Value == expectedPath).Single().Element("property");

            if (property.Attribute("name").Value != propertyName)
            {
                throw new Exception($"SVN automation failure.\nReceived:\n{svnOutput}");
            }

            var output = property.Value;

            logger.LogInformation($"Got value of SVN property {propertyName} for {path}.");

            return(output);
        }
        public static int Update(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            var nonDirectoryIndicatedPath = PathUtilities.EnsurePathIsNotDirectoryIndicated(path.Value).AsAbsolutePath();

            logger.LogDebug($"SVN updating {path}...");                 // Use the specified path.

            var arguments = $@"update ""{nonDirectoryIndicatedPath}"""; // Use the non-directory indicated path.

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            var lines = new List <string>();

            using (var reader = outputCollector.GetOutputReader())
            {
                while (!reader.ReadLineIsEnd(out var line))
                {
                    lines.Add(line);
                }
            }

            var lastLine        = lines.Last();
            var trimmedLastLine = lastLine.TrimEnd('.');
            var tokens          = trimmedLastLine.Split(' ');
            var revisionNumber  = tokens[tokens.Length - 1];

            var revision = Int32.Parse(revisionNumber);

            logger.LogInformation($"SVN updated {path}.\nRevision: {revision}");

            return(revision);
        }
        public static string[] ListProperties(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            logger.LogDebug($"Listing SVN properties of {path}...");

            var arguments = $@"proplist ""{path}"" --xml";

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            var svnOutput = outputCollector.GetOutputText();
            var document  = XDocument.Parse(svnOutput);

            var properties = document.Element("properties");

            if (!properties.Elements().Any())
            {
                return(Array.Empty <string>());
            }

            var expectedPath = path.Value; // This method does not convert paths to non-Windows.

            var target = properties.Elements("target").Where(x => x.Attribute("path").Value == expectedPath).Single();

            var output = target.Elements("property").Attributes("name").Select(x => x.Value).ToArray();

            logger.LogInformation($"Listed SVN properties of {path}.");

            return(output);
        }
        public static void Delete(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger, bool force = false)
        {
            logger.LogDebug($"SVN deleting path {path}...");

            var arguments = $@"delete ""{path}""";

            if (force)
            {
                arguments = arguments.Append(" --force");
            }

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            bool success        = false;
            var  expectedOutput = $"D         {path}";

            using (var reader = outputCollector.GetOutputReader())
            {
                while (!reader.ReadLineIsEnd(out var line))
                {
                    if (expectedOutput == line)
                    {
                        success = true;
                        break;
                    }
                }
            }

            if (!success)
            {
                throw new Exception($"SVN automation failure.\nReceived:\n{outputCollector.GetOutputText()}");
            }

            logger.LogInformation($"SVN deleted path {path}.");
        }
        public static IArgumentsBuilder GetStatusVerbose(AbsolutePath path)
        {
            var argumentsBuilder = SvnCommandServicesProvider.GetStatus(path)
                                   .AddVerbose();

            return(argumentsBuilder);
        }
        public static IArgumentsBuilder GetStatusVerboseForInstanceOnly(AbsolutePath path)
        {
            var argumentsBuilder = SvnCommandServicesProvider.GetStatusVerbose(path)
                                   .ForInstanceOnly();

            return(argumentsBuilder);
        }
        public static IArgumentsBuilder GetStatusVerboseDepthInfinity(AbsolutePath path)
        {
            var argumentsBuilder = SvnCommandServicesProvider.GetStatusVerbose(path)
                                   .SetDepth("infinity");

            return(argumentsBuilder);
        }
        /// <summary>
        /// Sets command flags to get an infinite-depth SVN status command that includes ignored items (does not respect svn:ignore and global ignore properties).
        /// </summary>
        public static IArgumentsBuilder GetStatusVerboseDepthInfinityNoIgnore(AbsolutePath path)
        {
            var argumentsBuilder = SvnCommandServicesProvider.GetStatusVerboseDepthInfinity(path)
                                   .AddFlagFull("no-ignore");

            return(argumentsBuilder);
        }
        public static string RunGetText(FilePath svnExecutableFilePath, IArgumentsBuilder argumentsBuilder, bool throwIfAnyError = true)
        {
            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, argumentsBuilder, throwIfAnyError);

            var text = outputCollector.GetOutputText();

            return(text);
        }
        public static ProcessOutputCollector Run(FilePath svnExecutableFilePath, IArgumentsBuilder argumentsBuilder, bool throwIfAnyError = true)
        {
            var arguments = argumentsBuilder.Build();

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments, throwIfAnyError);

            return(outputCollector);
        }
        public static string AddPath(string svnArguments, string path)
        {
            var token = $@"""{path}""";

            var output = SvnCommandServicesProvider.AddToken(svnArguments, token);

            return(output);
        }
        /// <summary>
        /// The default SVN status method.
        /// </summary>
        public static SvnStringPathStatus[] GetStatuses(FilePath svnExecutableFilePath, IArgumentsBuilder argumentsBuilder)
        {
            var xmlStatusType = SvnCommandServicesProvider.GetXmlStatusType(svnExecutableFilePath, argumentsBuilder);

            var statuses = SvnCommandServicesProvider.GetStatuses(xmlStatusType);

            return(statuses);
        }
        public static string AddStatus(string svnArguments, string path)
        {
            var token = "status";

            var output = SvnCommandServicesProvider.AddToken(svnArguments, token);

            output = SvnCommandServicesProvider.AddPath(svnArguments, path);
            return(output);
        }
        public static string RunGetXmlText(FilePath svnExecutableFilePath, IArgumentsBuilder argumentsBuilder, bool throwIfAnyError = true)
        {
            argumentsBuilder
            .AddXml();
            ;

            var textXml = SvnCommandServicesProvider.RunGetText(svnExecutableFilePath, argumentsBuilder, throwIfAnyError);

            return(textXml);
        }
        public static TXmlType RunGetXmlType <TXmlType>(FilePath svnExecutableFilePath, IArgumentsBuilder argumentsBuilder, bool throwIfAnyError = true)
        {
            var xmlText = SvnCommandServicesProvider.RunGetXmlText(svnExecutableFilePath, argumentsBuilder, throwIfAnyError);

            using (var stream = StreamHelper.FromString(xmlText))
            {
                var xmlType = XmlStreamSerializer.Deserialize <TXmlType>(stream, SvnXml.DefaultNamespace);
                return(xmlType);
            }
        }
        public static SvnStringPathStatus[] Statuses(this SvnCommand svnCommand, IArgumentsBuilder argumentsBuilder)
        {
            svnCommand.Logger.LogDebug($"Getting all SVN status results...");

            var statuses = SvnCommandServicesProvider.GetStatuses(svnCommand.SvnExecutableFilePath, argumentsBuilder);

            svnCommand.Logger.LogInformation($"Got all SVN status results ({statuses.Count()} results).");

            return(statuses);
        }
        public static StatusType GetStatusXmlType(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            //var statusXmlText = SvnCommandServicesProvider.GetStatusXmlText(svnExecutableFilePath, path, logger);
            var statusXmlText = SvnCommandServicesProvider.GetStatusXmlText2(svnExecutableFilePath, path, logger);

            using (var stream = StreamHelper.FromString(statusXmlText))
            {
                var statusXmlType = XmlStreamSerializer.Deserialize <StatusType>(stream, SvnXml.DefaultNamespace);
                return(statusXmlType);
            }
        }
        /// <summary>
        /// Get the SVN status results for a file or directory.
        /// If the specified path is a file, a single result will be returned.
        /// If the specified path is a directory, possibly many results will be returned.
        /// If the path does not exist, zero results will be returned.
        /// </summary>
        public static SvnStringPathStatus[] Statuses(this SvnCommand svnCommand, AbsolutePath path)
        {
            svnCommand.Logger.LogDebug($"Getting all SVN status results for path {path}...");

            var arguments = SvnCommandServicesProvider.GetStatusVerbose(path);

            var statuses = SvnCommandServicesProvider.GetStatuses(svnCommand.SvnExecutableFilePath, arguments);

            svnCommand.Logger.LogInformation($"Got all SVN status results for path {path} ({statuses.Count()} results).");

            return(statuses);
        }
        /// <summary>
        /// Gets SVN status values for all directory contents recursively, disregarding any SVN ignore properties and the global SVN ignore values.
        /// </summary>
        public static SvnStringPathStatus[] StatusesInfinityNoIgnore(this SvnCommand svnCommand, DirectoryPath directoryPath)
        {
            svnCommand.Logger.LogDebug($"Getting all SVN status results for directory path {directoryPath}...");

            var arguments = SvnCommandServicesProvider.GetStatusVerboseDepthInfinityNoIgnore(directoryPath);

            var statuses = SvnCommandServicesProvider.GetStatuses(svnCommand.SvnExecutableFilePath, arguments);

            svnCommand.Logger.LogInformation($"Got all SVN status results for directroy path {directoryPath} ({statuses.Count()} results).");

            return(statuses);
        }
        public static bool HasProperty(FilePath svnExecutableFilePath, AbsolutePath path, string propertyName, ILogger logger)
        {
            logger.LogDebug($"Testing existence of SVN property {propertyName} for {path}...");

            var properties = SvnCommandServicesProvider.ListProperties(svnExecutableFilePath, path, logger);

            var hasProperty = properties.Contains(propertyName);

            logger.LogInformation($"Tested existence of SVN property {propertyName} for {path}.");

            return(hasProperty);
        }
        private static SvnStringPathStatus StatusRobust_Internal(this SvnCommand svnCommand, AbsolutePath absolutePath)
        {
            var arguments = SvnCommandServicesProvider.GetStatusVerboseForInstanceOnly(absolutePath)
                            .AddXml(); // Get XML.

            var outputCollector = SvnCommandServicesProvider.Run(svnCommand.SvnExecutableFilePath, arguments, false);

            if (outputCollector.AnyError)
            {
                var errorText = outputCollector.GetErrorText().Trim();

                var notWorkingCopyText = $"svn: warning: W155007: '{absolutePath}' is not a working copy";
                if (errorText == notWorkingCopyText)
                {
                    var output = new SvnStringPathStatus {
                        Path = absolutePath.Value, ItemStatus = ItemStatus.NotWorkingCopy
                    };
                    return(output);
                }

                var notFoundText = $"svn: warning: W155010: The node '{absolutePath}' was not found.";
                if (errorText == notFoundText)
                {
                    var output = new SvnStringPathStatus {
                        Path = absolutePath.Value, ItemStatus = ItemStatus.NotFound
                    };
                    return(output);
                }

                throw new Exception($"Unknown SVN error:\n{errorText}");
            }

            var xmlText = outputCollector.GetOutputText();

            using (var stream = StreamHelper.FromString(xmlText))
            {
                var xmlStatusType = XmlStreamSerializer.Deserialize <StatusType>(stream, SvnXml.DefaultNamespace);

                var statuses = SvnCommandServicesProvider.GetStatuses(xmlStatusType);

                var status = statuses.Count() < 1
                    ? new SvnStringPathStatus {
                    Path = absolutePath.Value, ItemStatus = ItemStatus.None
                }
                    : statuses.Single() // Should be only 1.
                ;

                return(status);
            }
        }
        public static string GetStatusXmlText(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            logger.LogDebug($"Getting SVN status XML text for path {path}...");

            var arguments = $@"status ""{path}"" -v --xml";

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            logger.LogInformation($"Got SVN status XML text of path {path}.");

            var output = outputCollector.GetOutputText();

            return(output);
        }
        public static SvnStringPathStatus[] GetStatus(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            var statusXmlType = SvnCommandServicesProvider.GetStatusXmlType(svnExecutableFilePath, path, logger);

            var output = statusXmlType.target
                         .Where(x => x.entry != null)
                         .SelectMany(x => x.entry)
                         .Select(x => new SvnStringPathStatus {
                Path = x.path, ItemStatus = x.wcstatus.item.ToItemStatus()
            })
                         .ToArray();

            return(output);
        }
        public static CheckoutResult Checkout(this SvnCommand svnCommand, string repositoryUrl, string localDirectoryPath)
        {
            svnCommand.Logger.LogDebug($"SVN checkout of '{repositoryUrl}' to '{localDirectoryPath}'...");

            // Need to ensure the local directory path is NOT directory indicated (does NOT end with a directory separator).
            var correctedLocalDirectoryPath = PathUtilities.EnsureFilePathNotDirectoryIndicated(localDirectoryPath);

            var arguments = SvnCommandServicesProvider.GetCheckoutArguments(repositoryUrl, correctedLocalDirectoryPath);

            var commandOutput = SvnCommandServicesProvider.Run(svnCommand.SvnExecutableFilePath, arguments);

            var lines = commandOutput.GetOutputLines().ToList();

            var lastLine            = lines.Last();
            var lastLineTokens      = lastLine.Split(' ');
            var revisionNumberToken = lastLineTokens.Last().TrimEnd('.');
            var revisionNumber      = Int32.Parse(revisionNumberToken);

            var entryUpdateLines = lines.ExceptLast();
            var statuses         = new List <EntryUpdateStatus>();

            foreach (var line in entryUpdateLines)
            {
                var lineTokens = line.Split(new[] { " " }, 2, StringSplitOptions.RemoveEmptyEntries);

                var statusToken  = lineTokens[0];
                var relativePath = lineTokens[1];

                var updateStatus = statusToken.ToUpdateStatus();

                var status = new EntryUpdateStatus
                {
                    UpdateStatus = updateStatus,
                    RelativePath = relativePath,
                };
                statuses.Add(status);
            }

            var result = new CheckoutResult
            {
                RevisionNumber = revisionNumber,
                Statuses       = statuses.ToArray(),
            };

            svnCommand.Logger.LogInformation($"SVN checkout of '{repositoryUrl}' to '{localDirectoryPath}' complete.");

            return(result);
        }
        public static string GetStatusXmlText2(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            logger.LogDebug($"Getting SVN status XML text for path {path}...");

            var arguments = SvnCommandServicesProvider.GetStatusVerboseForInstanceOnly(path)
                            .AddFlagFull("xml")
                            .Build();

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            logger.LogInformation($"Got SVN status XML text of path {path}.");

            var output = outputCollector.GetOutputText();

            return(output);
        }
        public static Version GetVersion(FilePath svnExecutableFilePath, ILogger logger)
        {
            logger.LogDebug("Getting svn version...");

            var arguments = "--version --quiet";

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            var line = outputCollector.GetOutputText();

            var versionString = line;

            var version = Version.Parse(versionString);

            logger.LogInformation("Got svn version.");

            return(version);
        }
        public static void Add(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            logger.LogDebug($"Adding changes for {path}...");

            var arguments = $@"add ""{path}""";

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            var svnOutput = outputCollector.GetOutputText();

            var expectedOutput = $"A         {path}\r\n";

            if (svnOutput != expectedOutput)
            {
                throw new Exception($"SVN automation failure.\nReceived:\n{svnOutput}");
            }

            logger.LogInformation($"Added changes for {path}.");
        }
        /// <summary>s
        /// Get only the SVN status of a specific directory, and not its children.
        /// If the directory path does not exist, returns a result with status <see cref="ItemStatus.None"/>.
        /// </summary>
        public static SvnStringPathStatus Status(this SvnCommand svnCommand, DirectoryPath directoryPath)
        {
            svnCommand.Logger.LogDebug($"Getting SVN status of directory path {directoryPath}...");

            var arguments = SvnCommandServicesProvider.GetStatusVerboseForInstanceOnly(directoryPath);

            var statuses = SvnCommandServicesProvider.GetStatuses(svnCommand.SvnExecutableFilePath, arguments);

            var status = statuses.Count() < 1
                ? new SvnStringPathStatus {
                Path = directoryPath.Value, ItemStatus = ItemStatus.None
            }
                : statuses.Single() // Should be only 1.
            ;

            svnCommand.Logger.LogDebug($"Got SVN status of directory path {directoryPath}.");

            return(status);
        }
        public static void SetPropertyValue(FilePath svnExecutableFilePath, AbsolutePath path, string propertyName, string ignoreValue, ILogger logger)
        {
            logger.LogDebug($"Setting value of SVN property {propertyName} for {path}...");

            var arguments = $@"propset {propertyName} {ignoreValue} ""{path}""";

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            // Test for success.
            var line = outputCollector.GetOutputTextTrimmed();

            var expectedLine = $"property '{propertyName}' set on '{path}'";

            if (expectedLine != line)
            {
                throw new Exception($"SVN automation failure.\nExpected output:\n{expectedLine}\nReceived:\n{line}");
            }

            logger.LogInformation($"Set value of SVN property {propertyName} for {path}.");
        }
        public static void Revert(FilePath svnExecutableFilePath, AbsolutePath path, ILogger logger)
        {
            logger.LogDebug($"SVN reverting {path}...");

            var arguments = $@"revert ""{path}""";

            var outputCollector = SvnCommandServicesProvider.Run(svnExecutableFilePath, arguments);

            // Check for success.
            var line = outputCollector.GetOutputTextTrimmed();

            var expectedLine = $"Reverted '{path}'";

            if (expectedLine != line)
            {
                throw new Exception($"SVN automation failure.\nReceived:\n{outputCollector.GetOutputText()}");
            }

            logger.LogInformation($"SVN reverted {path}.");
        }