diff --git a/rhodecode/controllers/compare.py b/rhodecode/controllers/compare.py --- a/rhodecode/controllers/compare.py +++ b/rhodecode/controllers/compare.py @@ -86,24 +86,30 @@ class CompareController(BaseRepoControll raise HTTPNotFound - def _get_changesets(self, org_repo, org_ref, other_repo, other_ref): + def _get_discovery(self,org_repo, org_ref, other_repo, other_ref): + from mercurial import discovery + other = org_repo._repo + repo = other_repo._repo + tmp = discovery.findcommonincoming( + repo=repo, # other_repo we check for incoming + remote=other, # org_repo source for incoming + heads=[other[org_ref[1]].node()], + force=False + ) + return tmp + + def _get_changesets(self, org_repo, org_ref, other_repo, other_ref, tmp): changesets = [] #case two independent repos if org_repo != other_repo: - from mercurial import discovery - other = org_repo._repo - repo = other_repo._repo - onlyheads = None - tmp = discovery.findcommonincoming(repo=repo, - remote=other, - heads=onlyheads, force=False) common, incoming, rheads = tmp + if not incoming: revs = [] else: - revs = other.changelog.findmissing(common, rheads) + revs = org_repo._repo.changelog.findmissing(common, rheads) - for cs in map(binascii.hexlify, revs): + for cs in reversed(map(binascii.hexlify, revs)): changesets.append(org_repo.get_changeset(cs)) else: revs = ['ancestors(%s) and not ancestors(%s)' % (org_ref[1], @@ -117,21 +123,26 @@ class CompareController(BaseRepoControll def index(self, ref): org_repo, org_ref, other_repo, other_ref = self._handle_ref(ref) - c.swap_url = h.url('compare_home', repo_name=c.repo_name, + c.swap_url = h.url('compare_home', repo_name=other_repo, ref='%s...%s' % (':'.join(other_ref), - ':'.join(org_ref))) + ':'.join(org_ref)), + repo=org_repo) c.org_repo = org_repo = Repository.get_by_repo_name(org_repo) c.other_repo = other_repo = Repository.get_by_repo_name(other_repo) - + tmp = self._get_discovery(org_repo.scm_instance, + org_ref, + other_repo.scm_instance, + other_ref) c.cs_ranges = self._get_changesets(org_repo.scm_instance, org_ref, other_repo.scm_instance, - other_ref) + other_ref, + tmp) c.org_ref = org_ref[1] c.other_ref = other_ref[1] # diff needs to have swapped org with other to generate proper diff - _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref) + _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref, tmp) diff_processor = diffs.DiffProcessor(_diff, format='gitdiff') _parsed = diff_processor.prepare() diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py --- a/rhodecode/lib/diffs.py +++ b/rhodecode/lib/diffs.py @@ -26,16 +26,23 @@ # along with this program. If not, see . import re +import io import difflib import markupsafe + from itertools import tee, imap +from mercurial import patch +from mercurial.mdiff import diffopts +from mercurial.bundlerepo import bundlerepository +from mercurial import localrepo + from pylons.i18n.translation import _ from rhodecode.lib.vcs.exceptions import VCSError from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode from rhodecode.lib.helpers import escape -from rhodecode.lib.utils import EmptyChangeset +from rhodecode.lib.utils import EmptyChangeset, make_ui def wrap_to_table(str_): @@ -534,7 +541,7 @@ class DiffProcessor(object): return self.adds, self.removes -def differ(org_repo, org_ref, other_repo, other_ref): +def differ(org_repo, org_ref, other_repo, other_ref, discovery_data=None): """ General differ between branches, bookmarks or separate but releated repositories @@ -548,18 +555,55 @@ def differ(org_repo, org_ref, other_repo :param other_ref: :type other_ref: """ + ignore_whitespace = False context = 3 - from mercurial import patch - from mercurial.mdiff import diffopts - org_repo = org_repo.scm_instance._repo other_repo = other_repo.scm_instance._repo - + opts = diffopts(git=True, ignorews=ignore_whitespace, context=context) org_ref = org_ref[1] other_ref = other_ref[1] - opts = diffopts(git=True, ignorews=ignore_whitespace, context=context) + if org_repo != other_repo: + + common, incoming, rheads = discovery_data + # create a bundle (uncompressed if other repo is not local) + if other_repo.capable('getbundle'): + # disable repo hooks here since it's just bundle ! + # patch and reset hooks section of UI config to not run any + # hooks on fetching archives with subrepos + for k, _ in other_repo.ui.configitems('hooks'): + other_repo.ui.setconfig('hooks', k, None) + + unbundle = other_repo.getbundle('incoming', common=common, + heads=rheads) + + buf = io.BytesIO() + while True: + chunk = unbundle._stream.read(1024*4) + if not chunk: + break + buf.write(chunk) - return ''.join(patch.diff(org_repo, node1=org_ref, node2=other_ref, - opts=opts)) + buf.seek(0) + unbundle._stream = buf + + class InMemoryBundleRepo(bundlerepository): + def __init__(self, ui, path, bundlestream): + self._tempparent = None + localrepo.localrepository.__init__(self, ui, path) + self.ui.setconfig('phases', 'publish', False) + + self.bundle = bundlestream + + # dict with the mapping 'filename' -> position in the bundle + self.bundlefilespos = {} + + ui = make_ui('db') + bundlerepo = InMemoryBundleRepo(ui, path=other_repo.root, + bundlestream=unbundle) + return ''.join(patch.diff(bundlerepo, node1=org_ref, node2=other_ref, + opts=opts)) + else: + return ''.join(patch.diff(org_repo, node1=org_ref, node2=other_ref, + opts=opts))