[PATCH 1 of 1] transplant: update transplants file when nonexistent and when a new changegroup arrives

Georg Brandl georg at python.org
Sun Aug 29 03:24:51 CDT 2010


# HG changeset patch
# User Georg Brandl <georg at python.org>
# Date 1283070125 -7200
# Node ID 5a13f2475aac0e8ec2453245cd7e86a9231baaba
# Parent  d01e28657429815d4b83098e2ff5dd0d72e961e6
transplant: update transplants file when nonexistent and when a new changegroup arrives

While the transplant extension records each transplanted changeset's source in
the "transplant_source" extra item of the changeset, it also maintains a cache
of this information in .hg/transplant, which is consulted when checking for
already transplanted changesets.  When changesets are not transplanted in the
local clone, the cache file becomes outdated.

This change makes transplant update the cache file when receiving changesets,
so that (as long as the extension is enabled) the cache file contents should
be complete.  Also, the cache file is built once when it does not exist; this
helps for existing repositories and when making local clones, which do not
normally pull and therefore do not call the changegroup hook.

diff -r d01e28657429 -r 5a13f2475aac hgext/transplant.py
--- a/hgext/transplant.py	Sat Aug 28 23:57:39 2010 +0200
+++ b/hgext/transplant.py	Sun Aug 29 10:22:05 2010 +0200
@@ -23,11 +23,21 @@
         self.lnode = lnode
         self.rnode = rnode
 
+def findtransplants(repo, firstrev):
+    foundtransplants = []
+    for revno in range(firstrev, len(repo)):
+        ctx = repo[revno]
+        source = ctx.extra().get('transplant_source')
+        if source:
+            foundtransplants.append((ctx.node(), source))
+    return foundtransplants
+
 class transplants(object):
-    def __init__(self, path=None, transplantfile=None, opener=None):
+    def __init__(self, path=None, transplantfile=None, opener=None, repo=None):
         self.path = path
         self.transplantfile = transplantfile
         self.opener = opener
+        self.repo = repo
 
         if not opener:
             self.opener = util.opener(self.path)
@@ -37,10 +47,19 @@
 
     def read(self):
         abspath = os.path.join(self.path, self.transplantfile)
-        if self.transplantfile and os.path.exists(abspath):
+        if not self.transplantfile:
+            return
+        if os.path.exists(abspath):
             for line in self.opener(self.transplantfile).read().splitlines():
                 lnode, rnode = map(revlog.bin, line.split(':'))
                 self.transplants.append(transplantentry(lnode, rnode))
+        else:
+            # find all transplants
+            for left, right in findtransplants(self.repo, 0):
+                self.set(left, right)
+            # and write the cache file immediately
+            self.dirty = True
+            self.write()
 
     def write(self):
         if self.dirty and self.transplantfile:
@@ -70,7 +89,7 @@
         self.path = repo.join('transplant')
         self.opener = util.opener(self.path)
         self.transplants = transplants(self.path, 'transplants',
-                                       opener=self.opener)
+                                       opener=self.opener, repo=repo)
 
     def applied(self, repo, node, parent):
         '''returns True if a node is already an ancestor of parent
@@ -598,6 +617,25 @@
             source.close()
             os.unlink(bundle)
 
+def updatetransplantfile(ui, repo, hooktype, **kwds):
+    '''installed as a changegroup hook, records pulled transplanted changesets
+    in the repository's transplants file'''
+    firstrev = repo[kwds['node']].rev()
+    newtransplants = findtransplants(repo, firstrev)
+    if not newtransplants:
+        ui.debug(_('no new transplanted changesets found'))
+        return
+    path = repo.join('transplant')
+    tp = transplants(path, 'transplants', opener=util.opener(path), repo=repo)
+    for left, right in newtransplants:
+        tp.set(left, right)
+    tp.write()
+    ui.debug(_('recorded %d transplanted changeset(s)') %
+             len(newtransplants))
+
+def uisetup(ui):
+    ui.setconfig('hooks', 'changegroup.__transplant__', updatetransplantfile)
+
 cmdtable = {
     "transplant":
         (transplant,
diff -r d01e28657429 -r 5a13f2475aac tests/test-transplant.t
--- a/tests/test-transplant.t	Sat Aug 28 23:57:39 2010 +0200
+++ b/tests/test-transplant.t	Sun Aug 29 10:22:05 2010 +0200
@@ -351,3 +351,49 @@
   $ python -c "print repr(file('b', 'rb').read())"
   'a\r\nb\r\n'
   $ cd ..
+
+Test update of transplants file on pull:
+
+first create a nice repository with some changes
+
+  $ hg init repo1
+  $ cd repo1
+  $ echo a > a
+  $ hg ci -Am a
+  adding a
+  $ echo b > b
+  $ hg ci -Am b
+  adding b
+  $ echo c > c
+  $ hg ci -Am c
+  adding c
+  $ cd ..
+
+then make a clone and transplant a change
+
+  $ hg -q clone -r0 repo1 repo2
+  $ cd repo2
+  $ hg transplant -s ../repo1 tip
+  searching for changes
+  applying 177f92b77385
+  177f92b77385 transplanted to 229c260e510e
+  $ cd ..
+
+then pull into another clone and check if the same transplant is skipped
+
+  $ hg init repo3
+  $ cd repo3
+  $ hg -q pull -u ../repo2
+  $ hg transplant -s ../repo1 tip
+  searching for changes
+  skipping already applied revision 2:177f92b77385
+  $ cd ..
+
+same goes for direct cloning (different case becasue it does not pull locally)
+
+  $ hg -q clone repo2 repo4
+  $ cd repo4
+  $ hg transplant -s ../repo1 tip
+  searching for changes
+  skipping already applied revision 2:177f92b77385
+  $ cd ..


More information about the Mercurial-devel mailing list