/// <summary>
        /// Parse git-status --porcelain=1 and git-diff --name-status
        /// Outputs are similar, except that git-status has status for both worktree and index
        /// </summary>
        /// <param name="getAllChangedFilesCommandOutput">An output of <see cref="GitCommandHelpers.GetAllChangedFilesCmd"/> command.</param>
        /// <param name="fromDiff">Parse git-diff</param>
        /// <param name="staged">The staged status <see cref="GitItemStatus"/>, only relevant for git-diff (parsed for git-status)</param>
        /// <returns>list with the git items</returns>
        internal List <GitItemStatus> GetAllChangedFilesFromString_v1(string getAllChangedFilesCommandOutput, bool fromDiff, StagedStatus staged)
        {
            var diffFiles = new List <GitItemStatus>();

            if (string.IsNullOrEmpty(getAllChangedFilesCommandOutput))
            {
                return(diffFiles);
            }

            string trimmedStatus = RemoveWarnings(getAllChangedFilesCommandOutput);

            // Doesn't work with removed submodules
            var submodules = GetModule().GetSubmodulesLocalPaths();

            // Split all files on '\0' (WE NEED ALL COMMANDS TO BE RUN WITH -z! THIS IS ALSO IMPORTANT FOR ENCODING ISSUES!)
            var files = trimmedStatus.Split(new[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);

            for (int n = 0; n < files.Length; n++)
            {
                if (string.IsNullOrEmpty(files[n]))
                {
                    continue;
                }

                int splitIndex;
                if (fromDiff)
                {
                    splitIndex = -1;
                }
                else
                {
                    // Note that this fails for files with spaces (git-status --porcelain=1 is deprecated)
                    var splitChars = new[] { '\t', ' ' };
                    splitIndex = files[n].IndexOfAny(splitChars, 1);
                }

                string status;
                string fileName;

                if (splitIndex < 0)
                {
                    if (n >= files.Length - 1)
                    {
                        // Illegal, ignore
                        continue;
                    }

                    status   = files[n];
                    fileName = files[n + 1];
                    n++;
                }
                else
                {
                    status   = files[n].Substring(0, splitIndex);
                    fileName = files[n].Substring(splitIndex);
                }

                char x = status[0];
                char y = status.Length > 1 ? status[1] : ' ';

                if (fromDiff && staged == StagedStatus.WorkTree && x == 'U')
                {
                    // git-diff has two lines to inform that a file is modified and has a merge conflict
                    continue;
                }

                if (x != '?' && x != '!' && x != ' ')
                {
                    GitItemStatus gitItemStatusX;
                    var           stagedX = fromDiff ? staged : StagedStatus.Index;
                    if (x == 'R' || x == 'C')
                    {
                        // Find renamed files...
                        string nextFile = n + 1 < files.Length ? files[n + 1] : "";
                        gitItemStatusX = GitItemStatusFromCopyRename(stagedX, fromDiff, nextFile, fileName, x, status);
                        n++;
                    }
                    else
                    {
                        gitItemStatusX = GitItemStatusConverter.FromStatusCharacter(stagedX, fileName, x);
                    }

                    if (submodules.Contains(gitItemStatusX.Name))
                    {
                        gitItemStatusX.IsSubmodule = true;
                    }

                    diffFiles.Add(gitItemStatusX);
                }

                if (fromDiff || y == ' ')
                {
                    continue;
                }

                GitItemStatus gitItemStatusY;
                var           stagedY = StagedStatus.WorkTree;
                if (y == 'R' || y == 'C')
                {
                    // Find renamed files...
                    string nextFile = n + 1 < files.Length ? files[n + 1] : "";
                    gitItemStatusY = GitItemStatusFromCopyRename(stagedY, false, nextFile, fileName, y, status);
                    n++;
                }
                else
                {
                    gitItemStatusY = GitItemStatusConverter.FromStatusCharacter(stagedY, fileName, y);
                }

                if (submodules.Contains(gitItemStatusY.Name))
                {
                    gitItemStatusY.IsSubmodule = true;
                }

                diffFiles.Add(gitItemStatusY);
            }

            return(diffFiles);
        }
        /// <summary>
        /// Parse the output from git-status --porcelain=2
        /// </summary>
        /// <param name="getAllChangedFilesCommandOutput">output from the git command</param>
        /// <returns>list with the parsed GitItemStatus</returns>
        private static IReadOnlyList <GitItemStatus> GetAllChangedFilesFromString_v2(string getAllChangedFilesCommandOutput)
        {
            var diffFiles = new List <GitItemStatus>();

            if (string.IsNullOrEmpty(getAllChangedFilesCommandOutput))
            {
                return(diffFiles);
            }

            string trimmedStatus = RemoveWarnings(getAllChangedFilesCommandOutput);

            // Split all files on '\0'
            var files = trimmedStatus.Split(new[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);

            for (int n = 0; n < files.Length; n++)
            {
                string line      = files[n];
                char   entryType = line[0];

                if (entryType == '?' || entryType == '!')
                {
                    Debug.Assert(line.Length > 2 && line[1] == ' ', "Cannot parse for untracked:" + line);
                    string fileName = line.Substring(2);
                    UpdateItemStatus(entryType, false, "N...", fileName, null, null);
                }
                else if (entryType == '1' || entryType == '2' || entryType == 'u')
                {
                    // Parse from git-status documentation, assuming SHA-1 is used
                    // Ignore octal and treeGuid
                    // 1 XY subm <mH> <mI> <mW> <hH> <hI> <path>
                    // renamed:
                    // 2 XY subm <mH> <mI> <mW> <hH> <hI> <X><score> <path><sep><origPath>
                    // worktree (merge conflicts)
                    // u XY subm <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>

                    char   x = line[2];
                    char   y = line[3];
                    string fileName;
                    string oldFileName   = null;
                    string renamePercent = null;
                    string subm          = line.Substring(5, 4);

                    if (entryType == '1')
                    {
                        Debug.Assert(line.Length > 113 && line[1] == ' ', "Cannot parse line:" + line);
                        fileName = line.Substring(113);
                    }
                    else if (entryType == '2')
                    {
                        Debug.Assert(line.Length > 2 && n + 1 < files.Length, "Cannot parse renamed:" + line);

                        // Find renamed files...
                        string[] renames = line.Substring(114).Split(new[] { ' ' }, 2);
                        renamePercent = renames[0];
                        fileName      = renames[1];
                        oldFileName   = files[++n];
                    }
                    else if (entryType == 'u')
                    {
                        Debug.Assert(line.Length > 161, "Cannot parse unmerged:" + line);
                        fileName = line.Substring(161);
                    }
                    else
                    {
                        // illegal
                        continue;
                    }

                    UpdateItemStatus(x, true, subm, fileName, oldFileName, renamePercent);
                    UpdateItemStatus(y, false, subm, fileName, oldFileName, renamePercent);
                }
            }

            return(diffFiles);

            void UpdateItemStatus(char x, bool isIndex, string subm, string fileName, string oldFileName, string renamePercent)
            {
                if (x == '.')
                {
                    return;
                }

                var           staged        = isIndex ? StagedStatus.Index : StagedStatus.WorkTree;
                GitItemStatus gitItemStatus = GitItemStatusConverter.FromStatusCharacter(staged, fileName, x);

                if (oldFileName != null)
                {
                    gitItemStatus.OldName = oldFileName;
                }

                if (renamePercent != null)
                {
                    gitItemStatus.RenameCopyPercentage = renamePercent;
                }

                if (subm[0] == 'S')
                {
                    gitItemStatus.IsSubmodule = true;

                    if (!isIndex)
                    {
                        // Slight modification on how the following flags are used
                        // Changed commit
                        gitItemStatus.IsChanged = subm[1] == 'C';
                        gitItemStatus.IsDirty   = subm[2] == 'M' || subm[3] == 'U';
                    }
                }

                diffFiles.Add(gitItemStatus);
            }
        }