changeset 2355:29a8096820dc codereview

added basic comparision of two repositories using bundles and hg incomming
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 28 May 2012 23:21:43 +0200
parents d787da66c292
children ea079c9b62b5
files rhodecode/controllers/compare.py rhodecode/lib/diffs.py
diffstat 2 files changed, 79 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/controllers/compare.py	Mon May 28 02:00:14 2012 +0200
+++ b/rhodecode/controllers/compare.py	Mon May 28 23:21:43 2012 +0200
@@ -86,24 +86,30 @@
 
         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 @@
 
     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()
 
--- a/rhodecode/lib/diffs.py	Mon May 28 02:00:14 2012 +0200
+++ b/rhodecode/lib/diffs.py	Mon May 28 23:21:43 2012 +0200
@@ -26,16 +26,23 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 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 @@
         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 @@
     :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))