public HgBundle GetBundle(HgRevset common, HgRevset heads)
        {
            var hgRevsetManager = new HgRevsetManager();
            var hgRevset = hgRevsetManager.GetRevset(this, common, heads);

            var hgBundleBuilder = new HgBundleBuilder(new HgFileSystem(), Encoder);
            var hgBundle = hgBundleBuilder.BuildBundle(this, hgRevset);

            return hgBundle;
        }
        public HgDivergenceInfo GetDivergence(HgNodeID baseNodeID, HgNodeID headNodeID)
        {
            var hgRevsetManager = new HgRevsetManager();
            var @base = (baseNodeID == HgNodeID.Null ? null : Changelog[baseNodeID]);
            var head = (headNodeID == HgNodeID.Null ? null : Changelog[headNodeID]);

            var baseAncestors = @base == null ?
                new HgRevset() : 
                hgRevsetManager.GetAncestors(this, new HgRevset(@base.Metadata));
            var headAncestors = head == null ?
                new HgRevset() :
                hgRevsetManager.GetAncestors(this, new HgRevset(head.Metadata));

            var ahead = headAncestors - baseAncestors;
            var behind = baseAncestors - headAncestors;

            //
            // If ahead has only one commit and that one is closing branch without affecting any files, ignore it altogether
            if(ahead.Count == 1)
            {
                var aheadChangeset = Changelog[ahead.Single().NodeID];
                if(aheadChangeset.Branch.Closed && aheadChangeset.Files.Count == 0)
                    ahead = new HgRevset();
            } // if

            return new HgDivergenceInfo(ahead, head, behind, @base);
        }
        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 IList<HgChangeset> GetFileHistory(HgPath path, HgNodeID? startFilelogNodeID = null)
        {
            var filelog = GetFilelog(path);
            if(filelog == null) return null;

            var changesetNodeIDs = GetFileHistory(filelog, startFilelogNodeID ?? filelog.Revlog.Entries.Last().NodeID);
            
            var revsetManager = new HgRevsetManager();
            var revset = revsetManager.GetRevset(this, changesetNodeIDs);
            
            return GetChangesets(revset).OrderByDescending(c => c.Metadata.Revision).ToList();
        }