public HgChangesetDetails GetChangesetDetails(HgNodeID hgNodeID, int context = 3) { var c = Changelog[hgNodeID]; if(c == null) return null; var m = Manifest[c.ManifestNodeID]; if(m == null) return new HgChangesetDetails(c, new List<HgChangesetFileDetails>()); IList<HgChangesetFileDetails> files; var diffGenerator = new HgDiffGenerator(); // // For a very first changeset we treat all files as Added if(c.Metadata.FirstParentRevisionNodeID == HgNodeID.Null) { files = m.Files. Select(f => new { f = f.Path, file = GetFile(m[f.Path.FullPath]), }). Select(f => new HgChangesetFileDetails(f.f, HgChangesetFileOperation.Added, f.file.IsBinary ? null : GetHgRollupFileDiffInfo(f.f, f.file.IsBinary, diffGenerator.UnifiedDiff("", Encoder.DecodeAsUtf8(f.file.Data), context, false, true, true)))). ToList(); } // if else { Func<HgManifestFileEntry, string> fileName = mfe => mfe.Path.FullPath.TrimStart('/'); HgManifestEntry mp1 = null; ISet<string> cf = null, f_ = null, fp1 = null; cf = new HashSet<string>(c.Files.Select(f => f.TrimStart('/')).ToList()); f_ = new HashSet<string>(m.Files.Select(fileName)); var p1 = Changelog[c.Metadata.FirstParentRevisionNodeID]; mp1 = Manifest[p1.ManifestNodeID]; fp1 = new HashSet<string>(mp1.Files.Select(fileName)); var addedFiles = f_.Except(fp1).Intersect(cf).ToList(); var removedFiles = fp1.Except(f_).Intersect(cf).ToList(); var modifiedFiles = f_.Intersect(fp1).Intersect(cf).Where(f => m[f].FilelogNodeID != mp1[f].FilelogNodeID || !GetFile(m[f]).Data.SequenceEqual(GetFile(mp1[f]).Data)).ToList(); Func<HgFile, HgFile, HgUnifiedDiff> diff = (f /* from */, t /* to */) => { if(f != null && f.IsBinary || t != null && t.IsBinary) return null; var a = f == null ? "" : Encoder.DecodeAsUtf8(f.Data); var b = t == null ? "" : Encoder.DecodeAsUtf8(t.Data); return diffGenerator.UnifiedDiff(a, b, context, false, true, true); }; var removed = removedFiles. Select(f => new { f, file = GetFile(mp1[f]), }). Where(f => f.file != null). Select(f => Removed(f.f, GetHgRollupFileDiffInfo( new HgPath(f.f), f.file.IsBinary, diff(f.file, null)))). Where(f => f.Diff != null && (f.Diff.Additions > 0 || f.Diff.Removals > 0)); var added = addedFiles. Select(f => new { f, file = GetFile(m[f]), }). Where(f => f.file != null). Select(f => Added(f.f, GetHgRollupFileDiffInfo( new HgPath(f.f), f.file.IsBinary, diff(null, f.file)))). Where(f => f.Diff != null && (f.Diff.Additions > 0 || f.Diff.Removals > 0)); var modified = modifiedFiles. Select(f => new { f, file = GetFile(m[f]), }). Where(f => f.file != null). Select(f => Modified(f.f, GetHgRollupFileDiffInfo( new HgPath(f.f), f.file.IsBinary, diff(GetFile(mp1[f.f]), f.file)))). Where(f => f.Diff == null || f.Diff.Additions > 0 || f.Diff.Removals > 0); // // Prepare enough room to avoid reallocations later on files = new List<HgChangesetFileDetails>(f_.Count * 2); files.AddRange(added); files.AddRange(modified); files.AddRange(removed); } // else var changesetDetails = new HgChangesetDetails(c, files); return changesetDetails; }
public HgCompareInfo PerformComparison(HgNodeID baseNodeID, HgNodeID headNodeID, bool includeFileDiffs = false) { var hgRevsetManager = new HgRevsetManager(); var @base = Changelog[baseNodeID]; var head = Changelog[headNodeID]; var baseAncestors = hgRevsetManager.GetAncestors(this, new HgRevset(@base.Metadata)); var headAncestors = hgRevsetManager.GetAncestors(this, new HgRevset(head.Metadata)); var changesets = GetChangesets(headAncestors - baseAncestors); if(changesets.Count == 0) return new HgCompareInfo(@base, head, new List<HgChangeset>(), new List<HgRollupFileDiffInfo>()); var files = changesets.SelectMany(c => c.Files).Distinct().OrderBy(f => f).ToList(); var startManifest = Manifest[@base.ManifestNodeID]; var endManifest = Manifest[head.ManifestNodeID]; // // Only pick files that were indeed changed between two bounding changesets var diffs = new List<HgRollupFileDiffInfo>(); if(includeFileDiffs) { var changedFiles = files.Where(f => startManifest[f] == null && endManifest[f] != null || startManifest[f] != null && endManifest[f] == null || (startManifest[f] != null && endManifest[f] != null && startManifest[f].FilelogNodeID != endManifest[f].FilelogNodeID)).ToList(); var diffGenerator = new HgDiffGenerator(); diffs = changedFiles. Select(f => new { file = f, start = startManifest[f], end = endManifest[f] }). Select(f => new { f.file, start = f.start == null ? null : GetFile(f.start), end = f.end == null ? null : GetFile(f.end) }). Select(f => new { f.file, isBinary = f.start != null && f.start.IsBinary || f.end != null && f.end.IsBinary, f.start, f.end }). Select(f => new { f.file, f.isBinary, start = f.start == null || f.isBinary ? "" : Encoder.DecodeAsUtf8(f.start.Data), end = f.end == null || f.isBinary ? "" : Encoder.DecodeAsUtf8(f.end.Data) }). Select(f => new { f.file, f.isBinary, diff = f.isBinary ? null : diffGenerator.UnifiedDiff(f.start, f.end, 3) }). // // Do not include files that were not in fact changed between revision Where(f => f.diff == null || f.diff.Hunks.Sum(h => h.Lines.Count(l => l.Added || l.Removed )) > 0). Select(f => GetHgRollupFileDiffInfo(new HgPath(f.file), f.isBinary, f.diff)). ToList(); } return new HgCompareInfo(@base, head, changesets, diffs); }
public HgAnnotation GetAnnotation(HgPath path, HgNodeID? startFilelogNodeID = null) { var fileHistory = repository.GetFileHistory(path, startFilelogNodeID).OrderBy(c => c.Metadata.Revision).ToList(); var prev = ""; var diffGenerator = new HgDiffGenerator(); var annotationLines = new LinkedList<HgAnnotationLine>(); var manifestEntries = repository. GetManifestEntries(new HgRevset(fileHistory.Select(f => repository.Manifest.Revlog.GetEntry(f.ManifestNodeID)))). ToDictionary(me => me.Metadata.NodeID, me => me); foreach(var changeset in fileHistory) { var manifest = manifestEntries[changeset.ManifestNodeID]; var current = Encoding.UTF8.GetString( repository.GetFile(manifest.GetFile(path)).Data); var diff = diffGenerator.Diff(prev, current); LinkedListNode<HgAnnotationLine> annotationLine = null; // before first for(var i = 0; i < diff.Lines.Count; ++i) { if(diff.Lines[i].Unchanged) { annotationLine = annotationLine == null ? annotationLines.First : annotationLine.Next; continue; } // if if(diff.Lines[i].Removed) { if(annotationLine == null) { annotationLines.RemoveFirst(); } // if else { if(annotationLine.Next != null) annotationLines.Remove(annotationLine.Next); } // else } else { if(annotationLine == null) { annotationLines.AddFirst(new HgAnnotationLine(changeset, diff.Lines[i].Content)); annotationLine = annotationLines.First; } else { annotationLines.AddAfter(annotationLine, new HgAnnotationLine(changeset, diff.Lines[i].Content)); annotationLine = annotationLine.Next; } } } // for prev = current; } // foreach /* var lines = prev.Split('\n'); Debug.Assert( lines.Length == annotationLines.Count, string.Format("Annotation line count mismatch: {0} in annotation vs {1} in file", annotationLines.Count, lines.Length));*/ return new HgAnnotation(path, annotationLines); }