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 IEnumerable<HgChangesetDetails> GetChangesetsDetails(HgRevset hgRevset) { // // We need to preload all changesets and manifests var changesets = GetChangesets( hgRevset. Select(e => Changelog.Revlog[e.Revision]). SelectMany(e => new[] { e.Revision, e.FirstParentRevision, e.SecondParentRevision }). Where(e => e != uint.MaxValue). Distinct()). ToDictionary(c => c.Metadata.NodeID); var manifests = GetManifestEntries( new HgRevset( changesets.Values. Select(c => c.ManifestNodeID). Select(e => Manifest.Revlog.GetEntry(e)))). ToDictionary(m => m.Metadata.NodeID); foreach(var entry in hgRevset.OldestToNewest) { var c = changesets[entry.NodeID]; // // This is somehow possible for imported repos if(c.ManifestNodeID == HgNodeID.Null) continue; var m = manifests[c.ManifestNodeID]; IList<HgChangesetFileDetails> files; // // For a very first changeset we treat all files as Added if(c.Metadata.FirstParentRevisionNodeID == HgNodeID.Null) { files = m.Files. Select(mfe => new HgChangesetFileDetails(mfe.Path, HgChangesetFileOperation.Added, null)). ToList(); } // if else { Func<HgManifestFileEntry, string> fileName = (mfe) => mfe.Path.FullPath.TrimStart('/'); var cf = new HashSet<string>(c.Files.Select(f => f.TrimStart('/')).ToList()); var f_ = new HashSet<string>(m.Files.Select(fileName)); var p1 = changesets[c.Metadata.FirstParentRevisionNodeID]; var mp1 = manifests[p1.ManifestNodeID]; var fp1 = new HashSet<string>(mp1.Files.Select(fileName)); // // This is possible for imported repos. See above var parentManifestFiles = mp1 == null ? new List<string>() : mp1.Files.Select(fileName).ToList(); 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).ToList(); var removed = removedFiles.Select(Removed); var added = addedFiles.Select(Added); var modified = modifiedFiles.Select(Modified); // // 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); yield return changesetDetails; } // foreach }