convert: implement startrev for hg source
authorPatrick Mezard <pmezard@gmail.com>
Wed, 13 Aug 2008 23:31:10 +0200
changeset 6885 6e253aa04ff7
parent 6884 11229144aa01
child 6886 41aaaa23745f
convert: implement startrev for hg source
hgext/convert/__init__.py
hgext/convert/hg.py
tests/test-convert-hg-startrev
tests/test-convert-hg-startrev.out
tests/test-convert.out
--- a/hgext/convert/__init__.py	Tue Aug 12 17:47:08 2008 +0200
+++ b/hgext/convert/__init__.py	Wed Aug 13 23:31:10 2008 +0200
@@ -85,6 +85,8 @@
 
     --config convert.hg.saverev=True          (boolean)
         allow target to preserve source revision ID
+    --config convert.hg.startrev=0            (hg revision identifier)
+        convert start revision and its descendants
 
     CVS Source
     ----------
--- a/hgext/convert/hg.py	Tue Aug 12 17:47:08 2008 +0200
+++ b/hgext/convert/hg.py	Wed Aug 13 23:31:10 2008 +0200
@@ -206,6 +206,21 @@
         self.lastctx = None
         self._changescache = None
         self.convertfp = None
+        # Restrict converted revisions to startrev descendants
+        startnode = ui.config('convert', 'hg.startrev')
+        if startnode is not None:
+            try:
+                startnode = self.repo.lookup(startnode)
+            except repo.RepoError:
+                raise util.Abort(_('%s is not a valid start revision') 
+                                 % startnode)
+            startrev = self.repo.changelog.rev(startnode)
+            children = {startnode: 1}
+            for rev in self.repo.changelog.descendants(startrev):
+                children[self.repo.changelog.node(rev)] = 1
+            self.keep = children.__contains__
+        else:
+            self.keep = util.always
 
     def changectx(self, rev):
         if self.lastrev != rev:
@@ -213,11 +228,16 @@
             self.lastrev = rev
         return self.lastctx
 
+    def parents(self, ctx):
+        return [p.node() for p in ctx.parents() 
+                if p and self.keep(p.node())]
+
     def getheads(self):
         if self.rev:
-            return [hex(self.repo[self.rev].node())]
+            heads = [self.repo[self.rev].node()]
         else:
-            return [hex(node) for node in self.repo.heads()]
+            heads = self.repo.heads()
+        return [hex(h) for h in heads if self.keep(h)]
 
     def getfile(self, name, rev):
         try:
@@ -230,10 +250,14 @@
 
     def getchanges(self, rev):
         ctx = self.changectx(rev)
+        parents = self.parents(ctx)
+        if not parents:
+            files = util.sort(ctx.manifest().keys())
+            return [(f, rev) for f in files], {}
         if self._changescache and self._changescache[0] == rev:
             m, a, r = self._changescache[1]
         else:
-            m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3]
+            m, a, r = self.repo.status(parents[0], ctx.node())[:3]
         changes = [(name, rev) for name in m + a + r]
         return util.sort(changes), self.getcopies(ctx, m + a)
 
@@ -241,14 +265,16 @@
         copies = {}
         for name in files:
             try:
-                copies[name] = ctx.filectx(name).renamed()[0]
+                copynode = ctx.filectx(name).renamed()[0]
+                if self.keep(copynode):
+                    copies[name] = copynode
             except TypeError:
                 pass
         return copies
 
     def getcommit(self, rev):
         ctx = self.changectx(rev)
-        parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid]
+        parents = [hex(p) for p in self.parents(ctx)]
         if self.saverev:
             crev = rev
         else:
@@ -259,12 +285,18 @@
 
     def gettags(self):
         tags = [t for t in self.repo.tagslist() if t[0] != 'tip']
-        return dict([(name, hex(node)) for name, node in tags])
+        return dict([(name, hex(node)) for name, node in tags
+                     if self.keep(node)])
 
     def getchangedfiles(self, rev, i):
         ctx = self.changectx(rev)
-        i = i or 0
-        changes = self.repo.status(ctx.parents()[i].node(), ctx.node())[:3]
+        parents = self.parents(ctx)
+        if not parents and i is None:
+            i = 0
+            changes = [], ctx.manifest().keys(), []
+        else:
+            i = i or 0
+            changes = self.repo.status(parents[i], ctx.node())[:3]
 
         if i == 0:
             self._changescache = (rev, changes)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-hg-startrev	Wed Aug 13 23:31:10 2008 +0200
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+echo '[extensions]' >> $HGRCPATH
+echo 'hgext.graphlog =' >> $HGRCPATH
+echo 'hgext.convert =' >> $HGRCPATH
+
+glog()
+{
+    hg -R "$1" glog --template '#rev# "#desc#" files: #files#\n'
+}
+
+hg init source
+cd source
+
+echo a > a
+echo b > b
+hg ci -d '0 0' -qAm '0: add a b'
+echo c > c
+hg ci -d '1 0' -qAm '1: add c'
+hg copy a e
+echo b >> b
+hg ci -d '2 0' -qAm '2: copy e from a, change b'
+hg up -C 0
+echo a >> a
+hg ci -d '3 0' -qAm '3: change a'
+hg merge
+hg copy b d
+hg ci -d '4 0' -qAm '4: merge 2 and 3, copy d from b'
+echo a >> a
+hg ci -d '5 0' -qAm '5: change a'
+cd ..
+
+echo % convert from null revision
+hg convert --config convert.hg.startrev=null source empty
+glog empty
+
+echo % convert from zero revision
+hg convert --config convert.hg.startrev=0 source full
+glog full
+
+echo % convert from merge parent
+hg convert --config convert.hg.startrev=1 source conv1
+glog conv1
+cd conv1
+echo % check copy preservation
+hg log --follow --copies e
+echo % check copy removal on missing parent
+hg log --follow --copies d
+hg cat -r tip a b
+hg -q verify
+cd ..
+
+echo % convert from merge
+hg convert --config convert.hg.startrev=4 source conv4
+glog conv4
+cd conv4
+hg up -C
+hg cat -r tip a b
+hg -q verify
+cd ..
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-convert-hg-startrev.out	Wed Aug 13 23:31:10 2008 +0200
@@ -0,0 +1,88 @@
+1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+merging a and e to e
+2 files updated, 1 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% convert from null revision
+initializing destination empty repository
+scanning source...
+sorting...
+converting...
+% convert from zero revision
+initializing destination full repository
+scanning source...
+sorting...
+converting...
+5 0: add a b
+4 1: add c
+3 2: copy e from a, change b
+2 3: change a
+1 4: merge 2 and 3, copy d from b
+0 5: change a
+o  5 "5: change a" files: a
+|
+o    4 "4: merge 2 and 3, copy d from b" files: d e
+|\
+| o  3 "3: change a" files: a
+| |
+o |  2 "2: copy e from a, change b" files: b e
+| |
+o |  1 "1: add c" files: c
+|/
+o  0 "0: add a b" files: a b
+
+% convert from merge parent
+initializing destination conv1 repository
+scanning source...
+sorting...
+converting...
+3 1: add c
+2 2: copy e from a, change b
+1 4: merge 2 and 3, copy d from b
+0 5: change a
+o  3 "5: change a" files: a
+|
+o  2 "4: merge 2 and 3, copy d from b" files: a d e
+|
+o  1 "2: copy e from a, change b" files: b e
+|
+o  0 "1: add c" files: a b c
+
+% check copy preservation
+changeset:   2:cb71f8e79b45
+user:        test
+date:        Thu Jan 01 00:00:04 1970 +0000
+summary:     4: merge 2 and 3, copy d from b
+
+changeset:   1:3334790240a8
+user:        test
+date:        Thu Jan 01 00:00:02 1970 +0000
+summary:     2: copy e from a, change b
+
+% check copy removal on missing parent
+changeset:   2:cb71f8e79b45
+user:        test
+date:        Thu Jan 01 00:00:04 1970 +0000
+summary:     4: merge 2 and 3, copy d from b
+
+a
+a
+a
+b
+b
+% convert from merge
+initializing destination conv4 repository
+scanning source...
+sorting...
+converting...
+1 4: merge 2 and 3, copy d from b
+0 5: change a
+o  1 "5: change a" files: a
+|
+o  0 "4: merge 2 and 3, copy d from b" files: a b c d e
+
+5 files updated, 0 files merged, 0 files removed, 0 files unresolved
+a
+a
+a
+b
+b
--- a/tests/test-convert.out	Tue Aug 12 17:47:08 2008 +0200
+++ b/tests/test-convert.out	Wed Aug 13 23:31:10 2008 +0200
@@ -73,6 +73,8 @@
 
     --config convert.hg.saverev=True          (boolean)
         allow target to preserve source revision ID
+    --config convert.hg.startrev=0            (hg revision identifier)
+        convert start revision and its descendants
 
     CVS Source
     ----------