public void PatchEntryBasics() { // Init test repo var path = SandboxStandardTestRepoGitDir(); string file = "numbers.txt"; // The repo using (var repo = new Repository(path)) { Tree rootCommitTree = repo.Lookup <Commit>("f8d44d7").Tree; Tree commitTreeWithUpdatedFile = repo.Lookup <Commit>("ec9e401").Tree; // Create path by diffing using (var patch = repo.Diff.Compare <Patch>(rootCommitTree, commitTreeWithUpdatedFile)) { PatchEntryChanges entryChanges = patch[file]; Assert.Equal(2, entryChanges.LinesAdded); Assert.Equal(1, entryChanges.LinesDeleted); Assert.Equal(187, entryChanges.Patch.Length); // Smoke test Assert.Equal(Mode.NonExecutableFile, entryChanges.Mode); Assert.Equal(new ObjectId("4625a3628cb78970c57e23a2fe2574514ba403c7"), entryChanges.Oid); Assert.Equal(ChangeKind.Modified, entryChanges.Status); Assert.Equal(file, entryChanges.OldPath); Assert.Equal(Mode.NonExecutableFile, entryChanges.OldMode); Assert.Equal(new ObjectId("7909961ae96accd75b6813d32e0fc1d6d52ec941"), entryChanges.OldOid); } } }
public GitChange(PatchEntryChanges patchEntryChanges) { Summary = new GitChangeSummary(patchEntryChanges.Status, patchEntryChanges.LinesAdded, patchEntryChanges.LinesDeleted); OldBlob = new GitChangeBlob(patchEntryChanges.OldOid, patchEntryChanges.OldPath, patchEntryChanges.OldMode); NewBlob = new GitChangeBlob(patchEntryChanges.Oid, patchEntryChanges.Path, patchEntryChanges.Mode); }
public void CanCompareTwoVersionsOfAFileWithATrailingNewlineDeletion(int contextLines, int expectedPatchLength) { using (var repo = new Repository(StandardTestRepoPath)) { Tree rootCommitTree = repo.Lookup <Commit>("f8d44d7").Tree; Tree commitTreeWithUpdatedFile = repo.Lookup <Commit>("ec9e401").Tree; var changes = repo.Diff.Compare <TreeChanges>(rootCommitTree, commitTreeWithUpdatedFile); Assert.Equal(1, changes.Count()); Assert.Equal(1, changes.Modified.Count()); var patch = repo.Diff.Compare <Patch>(rootCommitTree, commitTreeWithUpdatedFile, compareOptions: new CompareOptions { ContextLines = contextLines }); Assert.Equal(expectedPatchLength, patch.Content.Length); PatchEntryChanges entryChanges = patch["numbers.txt"]; Assert.Equal(2, entryChanges.LinesAdded); Assert.Equal(1, entryChanges.LinesDeleted); Assert.Equal(expectedPatchLength, entryChanges.Patch.Length); Assert.Equal("numbers.txt", entryChanges.Path); } }
public PatchEntry(int offset, int length, Patch patch, PatchEntryChanges changes, IReadOnlyList <PatchHunk> hunks) { Offset = offset; Length = length; Patch = patch; Changes = changes; Hunks = hunks; }
public void Prepend(PatchEntryChanges changes) { List <LineRange> patch = new List <LineRange>(); //foreach (var l in changes) //{ // //} }
private string GetFileRowString(PatchEntryChanges pe) { if (pe.Status == ChangeKind.Renamed || pe.Status == ChangeKind.Copied) { return($"{pe.OldPath} ⇒ {pe.Path} ({pe.Status})"); } else { return($"{pe.Path} ({pe.Status})"); } }
void ComputeMerge_Deleted(Commit mergeBase, PatchEntryChanges change, Patch headChanges) { var folder = change.Path.Replace($"/{FileSystemStorage.DataFile}", string.Empty); if (headChanges.Any(c => c.Path.Equals(folder, StringComparison.OrdinalIgnoreCase) && (c.Status == ChangeKind.Added || c.Status == ChangeKind.Modified))) { throw new NotImplementedException("Node deletion while children have been added or modified in head is not supported."); } var mergeBaseObject = GetContent(mergeBase, change.Path, "branch tip"); DeletedObjects.Add(new MetadataTreeMergeObjectDelete(change.Path, mergeBaseObject)); }
void ComputeMerge_Added(Commit branchTip, PatchEntryChanges change, Patch headChanges) { var parentDataPath = change.Path.GetDataParentDataPath(); if (headChanges.Any(c => c.Path.Equals(parentDataPath, StringComparison.OrdinalIgnoreCase) && c.Status == ChangeKind.Deleted)) { throw new NotImplementedException("Node addition while parent has been deleted in head is not supported."); } var branchObject = GetContent(branchTip, change.Path, "branch tip"); AddedObjects.Add(new MetadataTreeMergeObjectAdd(change.Path, branchObject)); }
private void btnRunDebug_Click(object sender, EventArgs e) { var repo = new LibGit2Sharp.Repository(@"c:\client_server\bmm580\"); foreach (Commit commit in repo.Commits) { foreach (var parent in commit.Parents) { textBoxDebug.AppendText(commit.Sha + " " + commit.MessageShort + Environment.NewLine); textBoxDebug.AppendText(commit.Committer.ToString() + Environment.NewLine); //textBoxDebug.AppendText(commit.Author.ToString() + Environment.NewLine); foreach (TreeEntryChanges change in repo.Diff.Compare <TreeChanges>(repo.Head.Tip.Tree, DiffTargets.Index | DiffTargets.WorkingDirectory)) // foreach (TreeEntryChanges change in repo.Diff.Compare<TreeChanges>(parent.Tree,commit.Tree)) { textBoxDebug.AppendText(change.Status + " " + change.Path + Environment.NewLine); List <Commit> CommitList = new List <Commit>(); foreach (LogEntry entry in repo.Commits.QueryBy(change.Path).ToList()) { CommitList.Add(entry.Commit); } CommitList.Add(null); // Added to show correct initial add int ChangeDesired = 0; // Change difference desired var repoDifferences = repo.Diff.Compare <Patch>((Equals(CommitList[ChangeDesired + 1], null)) ? null : CommitList[ChangeDesired + 1].Tree, (Equals(CommitList[ChangeDesired], null)) ? null : CommitList[ChangeDesired].Tree); PatchEntryChanges file = null; try { file = repoDifferences.First( ); } catch { } // If the file has been renamed in the past- this search will fail if (!Equals(file, null)) { String result = file.Patch; textBoxDebug.AppendText(result + " " + Environment.NewLine); } } } } }
/// <summary> /// Use LibGit2Sharp to perform a Git diff procedure on the given file, comparing the current working directory /// against the file version at the given commit index. Returns the total number of lines changed. /// </summary> public int DiffFile(CustomFile file, int commitIndex) { //Ensure any previous changes detected by Git are removed before considering other changes if (file != null) { file.ClearPreviousChanges(); } // Return if the given file was null or if user selected "NO SELECTION" index from DependencyWindow drop-down if (file == null || commitIndex == -1) { return(0); } // Select the Git tree and file path for the given CustomFile LibGit2Sharp.Tree chosenTree = commitList[commitIndex].Tree; List <string> chosenFilePath = new List <string>() { file.relPath }; // Perform the Git diff and create a string for the resulting patch Patch patch = repo.Diff.Compare <Patch>(chosenTree, DiffTargets.WorkingDirectory, chosenFilePath, pathOptions, compareOptions); PatchEntryChanges changes = patch[file.relPath]; string patchText = changes.Patch; // If no changes b/w commits, then "", or if notIncludeModified, null changes // Update the CustomFile with this patchText for future direct access file.diffPatchText = patchText; // Parse the patch to localize additions and deletions to the appropriate CustomTypes and CustomMethods int totalFileChanges = ParseDiffPatch(patchText, file); //Debugging output for checking where changes were found in file //List<CustomType> typesInFile = file.types; //foreach (CustomType t in typesInFile) { // Debug.LogError("Total changes in " + t + ": " + t + ", inMethods: " + t.totalChangesInMethods + ", outMethods: " + t.totalChangesOutsideMethods); // foreach (CustomMethod m in t.methods) { // Debug.LogWarning(" Total changes in " + m + ": " + (m.additions + m.deletions) + "(Adds: " + m.additions + ", Dels: " + m.deletions + ")"); // } //} return(totalFileChanges); }
private void AddModifiedProperties(PatchEntryChanges branchChange, IModelObject mergeBaseObject, IModelObject newObject, IModelObject headObject, PatchEntryChanges headChange) { if (headChange?.Status == ChangeKind.Deleted) { throw new NotImplementedException($"Conflict as a modified node {branchChange.Path} in merge branch source has been deleted in head."); } var changes = ComputeModifiedProperties(branchChange, mergeBaseObject, newObject, headObject); foreach (var modifiedProperty in changes) { if (typeof(IObjectRepositoryIndex).IsAssignableFrom(modifiedProperty.Property.Property.ReflectedType)) { // Indexes will be recomputed anyways from the changes when committed, // so there is no need to track them in the modified chunks continue; } ModifiedProperties.Add(modifiedProperty); } }
private void ComputeMerge_Deleted(Commit mergeBase, PatchEntryChanges change, Patch headChanges) { // Only data file changes have to be taken into account // Changes made to the blobs will product a 'modified' change as well if (Path.GetFileName(change.Path) != FileSystemStorage.DataFile) { return; } var folder = change.Path.Replace($"/{FileSystemStorage.DataFile}", string.Empty); if (headChanges.Any(c => c.Path.Equals(folder, StringComparison.OrdinalIgnoreCase) && (c.Status == ChangeKind.Added || c.Status == ChangeKind.Modified))) { throw new NotImplementedException("Node deletion while children have been added or modified in head is not supported."); } var mergeBaseObject = GetContent(mergeBase, change.Path, "branch tip"); DeletedObjects.Add(new ObjectRepositoryDelete(change.Path, mergeBaseObject.Id)); }
private void ComputeMerge_Added(Commit branchTip, PatchEntryChanges change, Patch headChanges) { // Only data file changes have to be taken into account // Changes made to the blobs will product a 'modified' change as well if (Path.GetFileName(change.Path) != FileSystemStorage.DataFile) { return; } var parentDataPath = change.Path.GetDataParentDataPath(); if (headChanges.Any(c => c.Path.Equals(parentDataPath, StringComparison.OrdinalIgnoreCase) && c.Status == ChangeKind.Deleted)) { throw new NotImplementedException("Node addition while parent has been deleted in head is not supported."); } var branchObject = GetContent(branchTip, change.Path, "branch tip"); var parentId = change.Path.GetDataParentId(Repository); AddedObjects.Add(new ObjectRepositoryAdd(change.Path, branchObject, parentId)); }
public static IEnumerable <Method> GetInvolvedMethodInFile(this Commit commit, PatchEntryChanges changes, Func <Hunk, IEnumerable <Line> > linesStrategy) { var methods = new List <Method>(); var fileContent = commit.GetFileContent(changes.Path); if (fileContent == null) { return(methods); } foreach (var chunk in changes.Hunks) { methods.AddRange(linesStrategy(chunk) .Select(line => fileContent.GetMethodFromLineAndFile(changes.Path, line))); } return(methods.Distinct()); }
void AddModifiedChunks(PatchEntryChanges branchChange, JObject mergeBaseObject, JObject newObject, JObject headObject, PatchEntryChanges headChange) { if (headChange?.Status == ChangeKind.Deleted) { throw new NotImplementedException($"Conflict as a modified node {branchChange.Path} in merge branch source has been deleted in head."); } var type = Type.GetType(mergeBaseObject.Value <string>("$type")); var properties = _modelDataProvider.Get(type).ModifiableProperties; var changes = from kvp in (IEnumerable <KeyValuePair <string, JToken> >) newObject let p = properties.FirstOrDefault(pr => pr.Name.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase)) where p != null let mergeBaseValue = mergeBaseObject[kvp.Key] where mergeBaseValue == null || !JToken.DeepEquals(kvp.Value, mergeBaseValue) let headValue = TryGetToken(headObject, kvp) select new MetadataTreeMergeChunkChange(branchChange.Path, mergeBaseObject, newObject, headObject, p, mergeBaseValue, kvp.Value, headValue); foreach (var modifiedProperty in changes) { ModifiedChunks.Add(modifiedProperty); } }
public void CanCompareTwoVersionsOfAFileWithADiffOfTwoHunks(int contextLines, int interhunkLines) { var compareOptions = new CompareOptions { ContextLines = contextLines, InterhunkLines = interhunkLines, Similarity = SimilarityOptions.None, }; var path = SandboxStandardTestRepoGitDir(); using (var repo = new Repository(path)) { Tree rootCommitTree = repo.Lookup <Commit>("f8d44d7").Tree; Tree mergedCommitTree = repo.Lookup <Commit>("7252fe2").Tree; var changes = repo.Diff.Compare <TreeChanges>(rootCommitTree, mergedCommitTree, compareOptions: compareOptions); Assert.Equal(3, changes.Count()); Assert.Equal(1, changes.Modified.Count()); Assert.Equal(1, changes.Deleted.Count()); Assert.Equal(1, changes.Added.Count()); Assert.Equal(Mode.Nonexistent, changes["my-name-does-not-feel-right.txt"].Mode); var patch = repo.Diff.Compare <Patch>(rootCommitTree, mergedCommitTree, compareOptions: compareOptions); PatchEntryChanges entryChanges = patch["numbers.txt"]; Assert.Equal(3, entryChanges.LinesAdded); Assert.Equal(1, entryChanges.LinesDeleted); Assert.Equal(Expected("f8d44d7...7252fe2/numbers.txt-{0}-{1}.diff", contextLines, interhunkLines), entryChanges.Patch); Assert.Equal(Expected("f8d44d7...7252fe2/full-{0}-{1}.diff", contextLines, interhunkLines), patch); Assert.Equal("numbers.txt", entryChanges.Path); } }
private Change BuildChange(PatchEntryChanges change) { var fileChange = new Change(); fileChange.Name = change.Path; switch (change.Status) { case ChangeKind.Added: fileChange.ChangeType = ChangeType.Added; break; case ChangeKind.Copied: fileChange.ChangeType = ChangeType.Copied; break; case ChangeKind.Deleted: fileChange.ChangeType = ChangeType.Deleted; break; case ChangeKind.Modified: fileChange.ChangeType = ChangeType.Modified; break; case ChangeKind.Renamed: fileChange.ChangeType = ChangeType.Renamed; break; case ChangeKind.TypeChanged: fileChange.ChangeType = ChangeType.TypeChanged; break; case ChangeKind.Unmodified: fileChange.ChangeType = ChangeType.Unmerged; break; } return(fileChange); }
/// <summary> /// Analyzes a patch entry change /// </summary> /// <param name="change">The change.</param> /// <returns>A file changes descriptor.</returns> private IFileChanges AnalyzeChange(PatchEntryChanges change) { var fileChange = _fileChangesFactory.Create(); fileChange.RelativePath = change.Path; fileChange.AbsolutePath = Path.GetFullPath(Path.Join(_folderPath, change.Path)); foreach (Match blockHeader in _gitDiffBlockSeparator.Matches(change.Patch)) { var methodNameGroup = blockHeader.Groups["method"]; var newRowGroup = blockHeader.Groups["newRow"]; var blockChange = _blockChangesFactory.Create(); if (methodNameGroup.Success && newRowGroup.Success) { blockChange.Line = int.Parse(newRowGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture); blockChange.StartText = methodNameGroup.Value; } fileChange.Blocks.Add(blockChange); } return(fileChange); }
private string[] GetLines(Commit commit) { PatchEntryChanges changes = null; if (commit != null) { var patch = gitManager.Repository.Diff.Compare <Patch>(commit.Tree, DiffTargets.WorkingDirectory | DiffTargets.Index, new [] { localPath }, explicitPathsOptions, compareOptions); changes = patch[localPath]; } else if (gitManager.Repository.Head != null && gitManager.Repository.Head.Tip != null) { var patch = gitManager.Repository.Diff.Compare <Patch>(gitManager.Repository.Head.Tip.Tree, DiffTargets.WorkingDirectory | DiffTargets.Index, new[] { localPath }, explicitPathsOptions, compareOptions); changes = patch[localPath]; } if (changes != null) { isBinary = changes.IsBinaryComparison; return(changes.Patch.Split('\n')); } return(new string[0]); }
private static bool ShouldSkip(PatchEntryChanges change) { // Игнорируем бинарники и возможно некоторые типы файлов return(change.IsBinaryComparison || System.IO.Path.GetExtension(change.Path).ToLower().Contains(FILTERED_EXT)); }
internal static ObjectRepositoryDelete ComputeChanges_Deleted(IObjectRepositorySerializer serializer, IRepository repository, PatchEntryChanges change, Func <string, string> relativeFileDataResolver, Func <string, IList <TreeEntryChanges> > deletionConflictProvider = null) { // Only data file changes have to be taken into account // Changes made to the blobs will product a 'modified' change as well if (System.IO.Path.GetFileName(change.Path) != FileSystemStorage.DataFile) { return(null); } var conflicts = deletionConflictProvider?.Invoke(change.Path); if (conflicts?.Any() ?? false) { throw new NotImplementedException("Node deletion while children have been added or modified in head is not supported."); } var mergeBaseObject = serializer.Deserialize(repository.Lookup <Blob>(change.OldOid).GetContentStream(), relativeFileDataResolver); return(new ObjectRepositoryDelete(change.Path, mergeBaseObject.Id)); }
/// <summary> /// ユニファイド形式のテキストを解析する /// </summary> void AnalysisUnifiedText(PatchEntryChanges diff, List <ChangedLine> changedLine) { int startLine = 0; int numberOfLines = 0; int lineCount = 0; // 内容の異なる行の開始行 int startDifferentLine = 0; // 内容の異なる行数 int differentLineCount = 0; // ユニファイド形式の差分情報を一行ずつ読み込んで処理する using (var rs = new StringReader(diff.Patch)) { // 読み込む文字列がなくなったら -1 が返る while (rs.Peek() >= 0) { // 一行読み込む string strline = rs.ReadLine(); // 先頭が @@ で始まっている場合は変更行に対する情報 if (strline.StartsWith("@@") == true) { startLine = 0; numberOfLines = 0; lineCount = 0; startDifferentLine = 0; differentLineCount = 0; // 正規表現で変更範囲を抽出する // ※以下の記事がわかりやすかった // http://www.atmarkit.co.jp/fdotnet/dotnettips/579regexmatch/regexmatch.html Match match = Regex.Match(strline, @".*@@.+,.+ \+(?<StartLine>.*?),(?<NumberOfLines>.*?) @@"); if (match.Success == true) { startLine = int.Parse(match.Groups["StartLine"].Value); numberOfLines = int.Parse(match.Groups["NumberOfLines"].Value); logger.Trace($"<UnifiedText>ファイル名:{diff.Path} 開始行:{startLine} 行数:{numberOfLines}"); } } else { // 差分情報を取得できている場合にのみ処理 if (startLine > 0) { // 先頭が スペース で始まっている場合は変更なしの内容 // 先頭が + で始まっている場合は変更後のファイルの内容 // この2つが変更後のファイルに含まれる行なので、この2つだけを行数としてカウントする if (strline.StartsWith(" ") == true) { lineCount++; } if (strline.StartsWith("+") == true) { if (startDifferentLine == 0) { startDifferentLine = startLine + lineCount; } lineCount++; differentLineCount++; } else { // 先頭が + でない場合は差分情報を登録する if (startDifferentLine > 0) { AddChangeLine(changedLine, diff.Path, startDifferentLine, differentLineCount); startDifferentLine = 0; differentLineCount = 0; } } } } } // 未登録の差分情報があるかもしれないので、ここで再度登録 if (startDifferentLine > 0) { AddChangeLine(changedLine, diff.Path, startDifferentLine, differentLineCount); } } }
public ModifiedInfo(PatchEntryChanges changes) { Patch = changes.Patch; Binary = changes.IsBinaryComparison; Hunks = Diff.ParsePatch(Patch); }
private bool FileShouldBeIgnored(PatchEntryChanges file) { return(_context.IgnorePatterns.Any(pattern => file.Path.Contains(pattern))); }
void ComputeMerge_Modified(Commit mergeBase, Commit branchTip, Commit headTip, Patch headChanges, PatchEntryChanges change) { var mergeBaseObject = GetContent(mergeBase, change.Path, "merge base"); var branchObject = GetContent(branchTip, change.Path, "branch tip"); var headObject = GetContent(headTip, change.Path, "head tip"); AddModifiedChunks(change, mergeBaseObject, branchObject, headObject, headChanges[change.Path]); }
internal static ObjectRepositoryAdd ComputeChanges_Added(IObjectRepository objectRepository, IObjectRepositorySerializer serializer, IRepository repository, PatchEntryChanges change, Func <string, string> relativeFileDataResolver) { // Only data file changes have to be taken into account // Changes made to the blobs will product a 'modified' change as well if (System.IO.Path.GetFileName(change.Path) != FileSystemStorage.DataFile) { return(null); } if (objectRepository.TryGetFromGitPath(change.Path) != null) { throw new NotImplementedException("Node already present in current state."); } var parentDataPath = change.Path.GetDataParentDataPath(); if (objectRepository.TryGetFromGitPath(parentDataPath) == null) { throw new NotImplementedException("Node addition while parent has been deleted in head is not supported."); } var @new = serializer.Deserialize(repository.Lookup <Blob>(change.Oid).GetContentStream(), relativeFileDataResolver); var parentId = change.Path.GetDataParentId(objectRepository); return(new ObjectRepositoryAdd(change.Path, @new, parentId)); }
internal static IEnumerable <ObjectRepositoryPropertyChange> ComputeModifiedProperties(PatchEntryChanges changes, IModelObject ancestor, IModelObject theirs, IModelObject ours) { return(from property in ours.DataAccessor.ModifiableProperties let ancestorChunk = GetChunk(ancestor, property) let theirChunk = GetChunk(theirs, property) where !ancestorChunk.HasSameValue(theirChunk) let ourChunk = GetChunk(ours, property) select new ObjectRepositoryPropertyChange(changes.Path, property, ancestorChunk, theirChunk, ourChunk)); ObjectRepositoryPropertyValue GetChunk(IModelObject @object, ModifiablePropertyInfo property) { return(new ObjectRepositoryPropertyValue(@object, property, property.Accessor(@object))); } }
private void ComputeMerge_Modified(Commit mergeBase, Commit branchTip, Commit headTip, Patch headChanges, PatchEntryChanges change) { // Get data file path, in the case where a blob has changed var path = change.Path.GetSiblingFile(FileSystemStorage.DataFile); var mergeBaseObject = GetContent(mergeBase, path, "merge base"); var branchObject = GetContent(branchTip, path, "branch tip"); var headObject = GetContent(headTip, path, "head tip"); AddModifiedProperties(change, mergeBaseObject, branchObject, headObject, headChanges[change.Path]); }
public PatchDiff(List <DiffAnalyzer.Hunk> hunks, PatchEntryChanges patchEntryChanges, CancellableChanges cancellableChanges) => (Hunks, PatchEntryChanges, CancellableChanges) = (hunks, patchEntryChanges, cancellableChanges);
internal static IEnumerable <ObjectRepositoryPropertyChange> ComputeChanges_Modified(IObjectRepository objectRepository, IObjectRepositorySerializer serializer, PatchEntryChanges change, Func <string, Blob> relativeFileDataResolverStart, Func <string, Blob> relativeFileDataResolverEnd) { // Get data file path, in the case where a blob has changed var dataPath = change.Path.GetSiblingFile(FileSystemStorage.DataFile); var currentObject = objectRepository.TryGetFromGitPath(dataPath) ?? throw new NotImplementedException($"Conflict as a modified node {change.Path} has been deleted in current rebase state."); var changeStart = serializer.Deserialize( relativeFileDataResolverStart(FileSystemStorage.DataFile)?.GetContentStream() ?? throw new GitObjectDbException("Change start content could not be found."), relativePath => relativeFileDataResolverStart(relativePath)?.GetContentText() ?? string.Empty); var changeEnd = serializer.Deserialize( relativeFileDataResolverEnd(FileSystemStorage.DataFile)?.GetContentStream() ?? throw new GitObjectDbException("Change end content could not be found."), relativePath => relativeFileDataResolverEnd(relativePath)?.GetContentText() ?? string.Empty); var changes = ObjectRepositoryMerge.ComputeModifiedProperties(change, changeStart, changeEnd, currentObject); // Indexes will be recomputed anyways from the changes when committed, // so there is no need to track them in the modified chunks var changesWithoutIndexes = changes.Where( modifiedProperty => !typeof(IObjectRepositoryIndex).IsAssignableFrom(modifiedProperty.Property.Property.ReflectedType)); return(changesWithoutIndexes); }