[PATCH] bundlerepo: fix and improve getremotechanges

Peter Arrenbrecht peter.arrenbrecht at gmail.com
Mon May 2 06:55:56 CDT 2011


# HG changeset patch
# User Peter Arrenbrecht <peter.arrenbrecht at gmail.com>
# Date 1304332583 -7200
bundlerepo: fix and improve getremotechanges

Fixes the regression where incoming could show local changes
introduced by rev 72c84f24b420.

diff --git a/hgext/transplant.py b/hgext/transplant.py
--- a/hgext/transplant.py
+++ b/hgext/transplant.py
@@ -494,10 +494,8 @@
     and then resume where you left off by calling :hg:`transplant
     --continue/-c`.
     '''
-    def incwalk(repo, commmon, branches, match=util.always):
-        if not branches:
-            branches = None
-        for node in repo.changelog.findmissing(common, branches):
+    def incwalk(repo, csets, match=util.always):
+        for node in csets:
             if match(node):
                 yield node
 
@@ -547,15 +545,16 @@
         if m or a or r or d:
             raise util.Abort(_('outstanding local changes'))
 
-    bundle = None
-    source = opts.get('source')
-    if source:
-        sourcerepo = ui.expandpath(source)
-        source = hg.repository(ui, sourcerepo)
-        source, common, anyinc, bundle = bundlerepo.getremotechanges(ui, repo,
-                                          source, force=True)
+    sourcerepo = opts.get('source')
+    if sourcerepo:
+        source = hg.repository(ui, ui.expandpath(sourcerepo))
+        branches = map(source.lookup, opts.get('branch', ()))
+        source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, source,
+                                    onlyheads=branches, force=True)
     else:
         source = repo
+        branches = map(source.lookup, opts.get('branch', ()))
+        cleanupfn = None
 
     try:
         if opts.get('continue'):
@@ -569,7 +568,6 @@
             matchfn = lambda x: tf(x) and x not in prune
         else:
             matchfn = tf
-        branches = map(source.lookup, opts.get('branch', ()))
         merges = map(source.lookup, opts.get('merge', ()))
         revmap = {}
         if revs:
@@ -577,8 +575,7 @@
                 revmap[int(r)] = source.lookup(r)
         elif opts.get('all') or not merges:
             if source != repo:
-                alltransplants = incwalk(source, common, branches,
-                                         match=matchfn)
+                alltransplants = incwalk(source, csets, match=matchfn)
             else:
                 alltransplants = transplantwalk(source, p1, branches,
                                                 match=matchfn)
@@ -594,9 +591,8 @@
 
         tp.apply(repo, source, revmap, merges, opts)
     finally:
-        if bundle:
-            source.close()
-            os.unlink(bundle)
+        if cleanupfn:
+            cleanupfn()
 
 def revsettransplanted(repo, subset, x):
     """``transplanted(set)``
diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py
--- a/mercurial/bundlerepo.py
+++ b/mercurial/bundlerepo.py
@@ -288,30 +288,48 @@
         repopath, bundlename = parentpath, path
     return bundlerepository(ui, repopath, bundlename)
 
-def getremotechanges(ui, repo, other, revs=None, bundlename=None,
+def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
                      force=False):
-    tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force)
+    '''obtains a bundle of changes incoming from other
+
+    "onlyheads" restricts the returned changes to those reachable from the
+      specified heads.
+    "bundlename", if given, stores the bundle to this file path permanently;
+      the returned "bundle" will be None.
+    "force" indicates whether to proceed on unrelated repos.
+
+    Returns a tuple (local, csets, cleanupfn):
+
+    "local" is a local repo from which to obtain the actual incoming changesets; it
+      is a bundlerepo for the obtained bundle when the original "other" is remote.
+    "csets" lists the incoming changeset node ids.
+    "cleanupfn" must be called without arguments when you're done processing the
+      changes; it closes both the original "other" and the one returned here.
+    '''
+    tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force)
     common, incoming, rheads = tmp
     if not incoming:
         try:
             os.unlink(bundlename)
         except OSError:
             pass
-        return other, None, None, None
+        return other, [], other.close
 
     bundle = None
+    bundlerepo = None
+    localrepo = other
     if bundlename or not other.local():
         # create a bundle (uncompressed if other repo is not local)
 
-        if revs is None and other.capable('changegroupsubset'):
-            revs = rheads
+        if onlyheads is None and other.capable('changegroupsubset'):
+            onlyheads = rheads
 
         if other.capable('getbundle'):
-            cg = other.getbundle('incoming', common=common, heads=revs)
-        elif revs is None:
+            cg = other.getbundle('incoming', common=common, heads=onlyheads)
+        elif onlyheads is None:
             cg = other.changegroup(incoming, "incoming")
         else:
-            cg = other.changegroupsubset(incoming, revs, 'incoming')
+            cg = other.changegroupsubset(incoming, onlyheads, 'incoming')
         bundletype = other.local() and "HG10BZ" or "HG10UN"
         fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
         # keep written bundle?
@@ -319,6 +337,18 @@
             bundle = None
         if not other.local():
             # use the created uncompressed bundlerepo
-            other = bundlerepository(ui, repo.root, fname)
-    return (other, common, incoming, bundle)
+            localrepo = bundlerepo = bundlerepository(ui, repo.root, fname)
+            # this repo contains local and other now, so filter out local again
+            common = repo.heads()
 
+    csets = localrepo.changelog.findmissing(common, onlyheads)
+
+    def cleanup():
+        if bundlerepo:
+            bundlerepo.close()
+        if bundle:
+            os.unlink(bundle)
+        localrepo.close()
+
+    return (localrepo, csets, cleanup)
+
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -427,14 +427,13 @@
 
     if revs:
         revs = [other.lookup(rev) for rev in revs]
-    other, common, anyinc, bundle = bundlerepo.getremotechanges(ui, repo, other,
-                                     revs, opts["bundle"], opts["force"])
-    if not anyinc:
-        ui.status(_("no changes found\n"))
-        return subreporecurse()
+    other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
+                                revs, opts["bundle"], opts["force"])
+    try:
+        if not chlist:
+            ui.status(_("no changes found\n"))
+            return subreporecurse()
 
-    try:
-        chlist = other.changelog.findmissing(common, revs)
         displayer = cmdutil.show_changeset(ui, other, opts, buffered)
 
         # XXX once graphlog extension makes it into core,
@@ -443,10 +442,7 @@
 
         displayer.close()
     finally:
-        if hasattr(other, 'close'):
-            other.close()
-        if bundle:
-            os.unlink(bundle)
+        cleanupfn()
     subreporecurse()
     return 0 # exit code is zero since we found incoming changes
 
diff --git a/tests/test-http.t b/tests/test-http.t
--- a/tests/test-http.t
+++ b/tests/test-http.t
@@ -70,6 +70,24 @@
   adding bar
   $ cd ..
 
+incoming via HTTP
+
+  $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 4 changes to 4 files
+  updating to branch default
+  4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cd partial
+  $ touch LOCAL
+  $ hg ci -qAm LOCAL
+  $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
+  comparing with http://localhost:$HGPORT1/
+  searching for changes
+  2
+  $ cd ..
+
 pull
 
   $ cd copy-pull


More information about the Mercurial-devel mailing list