示例#1
0
        private static Patch CreatePatchFromString([ItemNotNull, NotNull] string[] lines, [NotNull] Lazy <Encoding> filesContentEncoding, ref int lineIndex)
        {
            if (lineIndex >= lines.Length)
            {
                return(null);
            }

            string header = lines[lineIndex];

            var headerMatch = _patchHeaderRegex.Match(header);

            if (!headerMatch.Success)
            {
                return(null);
            }

            header = VsrModule.ReEncodeFileNameFromLossless(header);

            var state = PatchProcessorState.InHeader;

            string fileNameA, fileNameB;

            var isCombinedDiff = headerMatch.Groups["type"].Value != "git";

            if (!isCombinedDiff)
            {
                // diff --git a/GitCommands/CommitInformationTest.cs b/GitCommands/CommitInformationTest.cs
                // diff --git b/Benchmarks/App.config a/Benchmarks/App.config
                Match match = Regex.Match(header, "^diff --git [\\\"]?[abiwco12]/(.*)[\\\"]? [\\\"]?[abiwco12]/(.*)[\\\"]?$");

                if (!match.Success)
                {
                    throw new FormatException("Invalid patch header: " + header);
                }

                fileNameA = match.Groups[1].Value.Trim();
                fileNameB = match.Groups[2].Value.Trim();
            }
            else
            {
                Match match = Regex.Match(header, "^diff --(cc|combined) [\\\"]?(?<filenamea>.*)[\\\"]?$");

                if (!match.Success)
                {
                    throw new FormatException("Invalid patch header: " + header);
                }

                fileNameA = match.Groups["filenamea"].Value.Trim();
                fileNameB = null;
            }

            string index      = null;
            var    changeType = PatchChangeType.ChangeFile;
            var    fileType   = PatchFileType.Text;
            var    patchText  = new StringBuilder();

            patchText.Append(header);
            if (lineIndex < lines.Length - 1)
            {
                patchText.Append("\n");
            }

            var done = false;
            var i    = lineIndex + 1;

            for (; i < lines.Length; i++)
            {
                var line = lines[i];

                if (IsStartOfANewPatch(line))
                {
                    lineIndex = i - 1;
                    done      = true;
                    break;
                }

                if (line.StartsWith("@@"))
                {
                    // Starting a new chunk
                    state = PatchProcessorState.InBody;
                    break;
                }

                // header lines are encoded in VsrModule.SystemEncoding
                line = VsrModule.ReEncodeStringFromLossless(line, VsrModule.SystemEncoding);

                if (line.StartsWith("index "))
                {
                    // Index line
                    index = line;
                    patchText.Append(line);
                    if (i < lines.Length - 1)
                    {
                        patchText.Append("\n");
                    }

                    continue;
                }

                if (line.StartsWith("new file mode "))
                {
                    changeType = PatchChangeType.NewFile;
                }
                else if (line.StartsWith("deleted file mode "))
                {
                    changeType = PatchChangeType.DeleteFile;
                }
                else if (line.StartsWith("old mode "))
                {
                    changeType = PatchChangeType.ChangeFileMode;
                }
                else if (line.StartsWith("Binary files a/") && line.EndsWith(" and /dev/null differ"))
                {
                    // Unlisted binary file deletion
                    if (changeType != PatchChangeType.DeleteFile)
                    {
                        throw new FormatException("Change not parsed correctly: " + line);
                    }

                    fileType = PatchFileType.Binary;
                    state    = PatchProcessorState.OutsidePatch;
                    break;
                }
                else if (line.StartsWith("Binary files /dev/null and b/") && line.EndsWith(" differ"))
                {
                    // Unlisted binary file addition
                    if (changeType != PatchChangeType.NewFile)
                    {
                        throw new FormatException("Change not parsed correctly: " + line);
                    }

                    fileType = PatchFileType.Binary;
                    state    = PatchProcessorState.OutsidePatch;
                    break;
                }
                else if (line.StartsWith("GIT binary patch"))
                {
                    fileType = PatchFileType.Binary;
                    state    = PatchProcessorState.OutsidePatch;
                    break;
                }

                if (line.StartsWith("--- /dev/null"))
                {
                    // there is no old file, so this should be a new file
                    if (changeType != PatchChangeType.NewFile)
                    {
                        throw new FormatException("Change not parsed correctly: " + line);
                    }
                }
                else if (line.StartsWith("--- "))
                {
                    // old file name
                    line = VsrModule.UnescapeOctalCodePoints(line);
                    Match regexMatch = Regex.Match(line, "[-]{3} [\\\"]?[abiwco12]/(.*)[\\\"]?");

                    if (regexMatch.Success)
                    {
                        fileNameA = regexMatch.Groups[1].Value.Trim();
                    }
                    else
                    {
                        throw new FormatException("Old filename not parsed correctly: " + line);
                    }
                }
                else if (line.StartsWith("+++ /dev/null"))
                {
                    // there is no new file, so this should be a deleted file
                    if (changeType != PatchChangeType.DeleteFile)
                    {
                        throw new FormatException("Change not parsed correctly: " + line);
                    }
                }
                else if (line.StartsWith("+++ "))
                {
                    // new file name
                    line = VsrModule.UnescapeOctalCodePoints(line);
                    Match regexMatch = Regex.Match(line, "[+]{3} [\\\"]?[abiwco12]/(.*)[\\\"]?");

                    if (regexMatch.Success)
                    {
                        fileNameB = regexMatch.Groups[1].Value.Trim();
                    }
                    else
                    {
                        throw new FormatException("New filename not parsed correctly: " + line);
                    }
                }

                patchText.Append(line);

                if (i < lines.Length - 1)
                {
                    patchText.Append("\n");
                }
            }

            // process patch body
            for (; !done && i < lines.Length; i++)
            {
                var line = lines[i];

                if (IsStartOfANewPatch(line))
                {
                    lineIndex = i - 1;
                    break;
                }

                if (state == PatchProcessorState.InBody && line.StartsWithAny(new[] { " ", "-", "+", "@" }))
                {
                    // diff content
                    line = VsrModule.ReEncodeStringFromLossless(line, filesContentEncoding.Value);
                }
                else
                {
                    // warnings, messages ...
                    line = VsrModule.ReEncodeStringFromLossless(line, VsrModule.SystemEncoding);
                }

                if (i < lines.Length - 1)
                {
                    line += "\n";
                }

                patchText.Append(line);
            }

            lineIndex = i - 1;

            return(new Patch(header, index, fileType, fileNameA, fileNameB, isCombinedDiff, changeType, patchText.ToString()));
        }
            public static LostObject TryParse(VsrModule module, string raw)
            {
                if (string.IsNullOrEmpty(raw))
                {
                    throw new ArgumentException("Raw source must be non-empty string", raw);
                }

                var patternMatch = RawDataRegex.Match(raw);

                // show failed assertion for unsupported cases (for developers)
                // if you get this message,
                //     you can implement this format parsing
                //     or post an issue to https://github.com/gitextensions/gitextensions/issues
                Debug.Assert(patternMatch.Success, "Lost object's extracted diagnostics format not implemented", raw);

                // skip unsupported raw data format (for end users)
                if (!patternMatch.Success)
                {
                    return(null);
                }

                var matchedGroups = patternMatch.Groups;
                var rawType       = matchedGroups[1].Value;
                var objectType    = GetObjectType(matchedGroups[3]);
                var objectId      = ObjectId.Parse(raw, matchedGroups[4]);
                var result        = new LostObject(objectType, rawType, objectId);

                if (objectType == LostObjectType.Commit)
                {
                    var commitLog       = GetLostCommitLog();
                    var logPatternMatch = LogRegex.Match(commitLog);
                    if (logPatternMatch.Success)
                    {
                        result.Author = module.ReEncodeStringFromLossless(logPatternMatch.Groups[1].Value);
                        string encodingName = logPatternMatch.Groups[2].Value;
                        result.Subject = module.ReEncodeCommitMessage(logPatternMatch.Groups[3].Value, encodingName);
                        result.Date    = DateTimeUtils.ParseUnixTime(logPatternMatch.Groups[4].Value);
                        if (logPatternMatch.Groups.Count >= 5)
                        {
                            var parentId = logPatternMatch.Groups[5].Value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
                            if (parentId != null)
                            {
                                result.Parent = ObjectId.Parse(parentId);
                            }
                        }
                    }
                }
                else if (objectType == LostObjectType.Tag)
                {
                    var tagData         = GetLostTagData();
                    var tagPatternMatch = TagRegex.Match(tagData);
                    if (tagPatternMatch.Success)
                    {
                        result.Parent  = ObjectId.Parse(tagData, tagPatternMatch.Groups[1]);
                        result.Author  = module.ReEncodeStringFromLossless(tagPatternMatch.Groups[3].Value);
                        result.TagName = tagPatternMatch.Groups[2].Value;
                        result.Subject = result.TagName + ":" + tagPatternMatch.Groups[5].Value;
                        result.Date    = DateTimeUtils.ParseUnixTime(tagPatternMatch.Groups[4].Value);
                    }
                }
                else if (objectType == LostObjectType.Blob)
                {
                    var hash     = objectId.ToString();
                    var blobPath = Path.Combine(module.WorkingDirGitDir, "objects", hash.Substring(0, 2), hash.Substring(2, ObjectId.GuidCharCount - 2));
                    result.Date = new FileInfo(blobPath).CreationTime;
                }

                return(result);

                string GetLostCommitLog() => VerifyHashAndRunCommand(LogCommandArgumentsFormat);
                string GetLostTagData() => VerifyHashAndRunCommand(TagCommandArgumentsFormat);

                string VerifyHashAndRunCommand(ArgumentString commandFormat)
                {
                    return(module.GitExecutable.GetOutput(string.Format(commandFormat, objectId), outputEncoding: VsrModule.LosslessEncoding));
                }

                LostObjectType GetObjectType(Group matchedGroup)
                {
                    if (!matchedGroup.Success)
                    {
                        return(LostObjectType.Other);
                    }

                    switch (matchedGroup.Value)
                    {
                    case "commit": return(LostObjectType.Commit);

                    case "blob": return(LostObjectType.Blob);

                    case "tree": return(LostObjectType.Tree);

                    case "tag": return(LostObjectType.Tag);

                    default: return(LostObjectType.Other);
                    }
                }
            }