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_): @@ -379,6 +386,7 @@ class DiffProcessor(object): }) line = lineiter.next() + except StopIteration: pass @@ -445,7 +453,7 @@ class DiffProcessor(object): new_lineno_class='lineno old', old_lineno_class='lineno new', code_class='code', enable_comments=False, diff_lines=None): """ - Return udiff as html table with customized css classes + Return given diff as html table with customized css classes """ def _link_to_if(condition, label, url): """ @@ -542,3 +550,78 @@ class DiffProcessor(object): Returns tuple of added, and removed lines for this instance """ return self.adds, self.removes + + +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 = {} + + +def differ(org_repo, org_ref, other_repo, other_ref, discovery_data=None): + """ + General differ between branches, bookmarks or separate but releated + repositories + + :param org_repo: + :type org_repo: + :param org_ref: + :type org_ref: + :param other_repo: + :type other_repo: + :param other_ref: + :type other_ref: + """ + + bundlerepo = None + ignore_whitespace = False + context = 3 + 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] + + 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') and incoming: + # 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) + + buf.seek(0) + # replace chunked _stream with data that can do tell() and seek() + unbundle._stream = buf + + ui = make_ui('db') + bundlerepo = InMemoryBundleRepo(ui, path=org_repo.root, + bundlestream=unbundle) + + return ''.join(patch.diff(bundlerepo or org_repo, + node1=org_repo[org_ref].node(), + node2=other_repo[other_ref].node(), + opts=opts)) + else: + return ''.join(patch.diff(org_repo, node1=org_ref, node2=other_ref, + opts=opts))