changeset 457:8ad6dfd51f72

Merge with mpedersen.
author Augie Fackler <durin42@gmail.com>
date Sun, 05 Jul 2009 21:13:13 -0500
parents b5b4095d2993 (current diff) 29fe7212cae0 (diff)
children 974102998578
files hgsubversion/svnmeta.py
diffstat 7 files changed, 113 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/hgsubversion/maps.py
+++ b/hgsubversion/maps.py
@@ -4,6 +4,8 @@ import os
 from mercurial import util as hgutil
 from mercurial import node
 
+import svncommands
+
 class AuthorMap(dict):
     '''A mapping from Subversion-style authors to Mercurial-style
     authors, and back. The data is stored persistently on disk.
@@ -97,30 +99,41 @@ class AuthorMap(dict):
 
 class TagMap(dict):
 
-    VERSION = 1
+    VERSION = 2
 
-    def __init__(self, repo):
+    def __init__(self, repo, endrev=None):
         dict.__init__(self)
         self.path = os.path.join(repo.path, 'svn', 'tagmap')
         self.seen = 0
+        self.endrev=endrev
         if os.path.isfile(self.path):
-            self._load()
+            self._load(repo)
         else:
             self._write()
 
-    def _load(self):
+    def _load(self, repo):
         f = open(self.path)
         ver = int(f.readline())
-        if ver != self.VERSION:
+        if ver < self.VERSION:
+            repo.ui.warn('tag map outdated, running rebuildmeta...')
+            f.close()
+            os.unlink(self.path)
+            svncommands.rebuildmeta(repo.ui, repo, os.path.dirname(repo.path), ())
+            return
+        elif ver != self.VERSION:
             print 'tagmap too new -- please upgrade'
             raise NotImplementedError
         for l in f:
-            hash, tag = l.split(' ', 1)
+            hash, revision, tag = l.split(' ', 2)
+            revision = int(revision)
             tag = tag[:-1]
+            if self.endrev is not None and revision > self.endrev:
+                break
             dict.__setitem__(self, tag, node.bin(hash))
         f.close()
 
     def _write(self):
+        assert self.endrev is None
         f = open(self.path, 'w')
         f.write('%s\n' % self.VERSION)
         f.flush()
@@ -138,9 +151,10 @@ class TagMap(dict):
             return dict.__getitem__(self, tag)
         raise KeyError()
 
-    def __setitem__(self, tag, hash):
+    def __setitem__(self, tag, info):
+        hash, revision = info
         f = open(self.path, 'a')
-        f.write(node.hex(hash) + ' ' + tag + '\n')
+        f.write('%s %s %s\n' % (node.hex(hash), revision, tag))
         f.flush()
         f.close()
         dict.__setitem__(self, tag, hash)
--- a/hgsubversion/svncommands.py
+++ b/hgsubversion/svncommands.py
@@ -67,11 +67,12 @@ def verify(ui, repo, *args, **opts):
 def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
     """rebuild hgsubversion metadata using values stored in revisions
     """
-    if len(args) != 1:
+    dest = None
+    if len(args) == 1:
         dest = args[0]
-        url = repo.ui.expandpath(dest or 'default-push', dest or 'default')
-    else:
-        url = args[0]
+    elif len(args) > 1:
+        raise hgutil.Abort('rebuildmeta takes 1 or no arguments')
+    url = repo.ui.expandpath(dest or 'default-push', dest or 'default')
     uuid = None
     url = util.normalize_url(url.rstrip('/'))
     user, passwd = util.getuserpass(opts)
@@ -104,7 +105,18 @@ def rebuildmeta(ui, repo, hg_repo_path, 
             newdata = ctx.filectx('.hgtags').data()
             for newtag in newdata[len(parentdata):-1].split('\n'):
                 ha, tag = newtag.split(' ', 1)
-                tags[tag] = node.bin(ha)
+                tagged = repo[ha].extra().get('convert_revision', None)
+                if tagged is None:
+                    tagged = -1
+                else:
+                    tagged = int(tagged[40:].split('@')[1])
+                # This is max(tagged rev, tagging rev) because if it is a normal
+                # tag, the tagging revision has the right rev number. However, if it
+                # was an edited tag, then the tagged revision has the correct revision
+                # number.
+                tagging = int(convinfo[40:].split('@')[1])
+                tagrev = max(tagged, tagging)
+                tags[tag] = node.bin(ha), tagrev
 
         # check that the conversion metadata matches expectations
         assert convinfo.startswith('svn:')
--- a/hgsubversion/svnmeta.py
+++ b/hgsubversion/svnmeta.py
@@ -262,7 +262,7 @@ class SVNMeta(object):
             src_tag = self.is_path_tag(src_path)
             if src_tag != False or src_file == '': # case 2
                 ln = self.localname(p)
-                if src_tag != False:
+                if src_tag != False and src_tag in self.tags:
                     ci = self.repo[self.tags[src_tag]].extra()['convert_revision']
                     src_rev, src_branch, = self.parse_converted_revision(ci)
                 return {ln: (src_branch, src_rev, revnum)}
@@ -311,8 +311,9 @@ class SVNMeta(object):
         '''Get the parent revision hash for a commit on a specific branch.
         '''
         tag = self.is_path_tag(self.remotename(branch))
-        if tag and tag in self.tags:
-            ha = self.tags[tag]
+        limitedtags = maps.TagMap(self.repo, endrev=number-1)
+        if tag and tag in limitedtags:
+            ha = limitedtags[tag]
             return ha
         r, br = self.get_parent_svn_branch_and_rev(number, branch)
         if r is not None:
@@ -343,8 +344,9 @@ class SVNMeta(object):
                         from_tag = self.is_path_tag(src_p)
                         if not from_tag:
                             continue
-                        ci = self.repo[self.tags[from_tag]].extra()['convert_revision']
-                        src_rev, branch, = self.parse_converted_revision(ci)
+                        if from_tag in self.tags:
+                            ci = self.repo[self.tags[from_tag]].extra()['convert_revision']
+                            src_rev, branch, = self.parse_converted_revision(ci)
                     if t_name not in added_tags and file is '':
                         added_tags[t_name] = branch, src_rev
                     elif file:
@@ -455,7 +457,7 @@ class SVNMeta(object):
         if not newparent:
             assert self.revmap[revnum, branch] == parentctx.node()
             self.revmap[revnum, branch] = new_hash
-        self.tags[tag] = hash
+        self.tags[tag] = hash, rev.revnum
         util.describe_commit(self.ui, new_hash, branch)
 
     def committags(self, delta, rev, endbranches):
@@ -488,7 +490,7 @@ class SVNMeta(object):
                 elif op == 'rm':
                     tagged = node.hex(node.nullid)
                 src += '%s %s\n' % (tagged, tag)
-                self.tags[tag] = node.bin(tagged)
+                self.tags[tag] = node.bin(tagged), rev.revnum
 
             # add new changeset containing updated .hgtags
             def fctxfun(repo, memctx, path):
--- a/tests/fixtures/commit-to-tag.sh
+++ b/tests/fixtures/commit-to-tag.sh
@@ -59,6 +59,12 @@ svn up
 echo boofar > tags/edit-later/delta
 svn ci -m 'Edit this tag after its parent closed'
 
+# try and revert will-edit to its original state
+svn up
+svn merge -r9:8 $REPOPATH .
+svn ci -m 'Revert revision 9.'
+
+
 cd ../..
 svnadmin dump temp/repo > commit-to-tag.svndump
 echo
--- a/tests/fixtures/commit-to-tag.svndump
+++ b/tests/fixtures/commit-to-tag.svndump
@@ -387,8 +387,8 @@ not omega
 
 
 Revision-number: 13
-Prop-content-length: 122
-Content-length: 122
+Prop-content-length: 119
+Content-length: 119
 
 K 7
 svn:log
@@ -415,8 +415,8 @@ more stupidity
 
 
 Revision-number: 14
-Prop-content-length: 120
-Content-length: 120
+Prop-content-length: 117
+Content-length: 117
 
 K 7
 svn:log
@@ -440,8 +440,8 @@ Node-copyfrom-path: tags/also-edit
 
 
 Revision-number: 15
-Prop-content-length: 143
-Content-length: 143
+Prop-content-length: 140
+Content-length: 140
 
 K 7
 svn:log
@@ -465,8 +465,8 @@ Node-copyfrom-path: branches/magic
 
 
 Revision-number: 16
-Prop-content-length: 139
-Content-length: 139
+Prop-content-length: 136
+Content-length: 136
 
 K 7
 svn:log
@@ -490,8 +490,8 @@ Node-copyfrom-path: branches/closeme
 
 
 Revision-number: 17
-Prop-content-length: 119
-Content-length: 119
+Prop-content-length: 116
+Content-length: 116
 
 K 7
 svn:log
@@ -504,7 +504,7 @@ durin
 K 8
 svn:date
 V 27
-2009-06-26T19:26:28.149674Z
+2009-06-29T00:12:57.367624Z
 PROPS-END
 
 Node-path: branches/closeme
@@ -512,8 +512,8 @@ Node-action: delete
 
 
 Revision-number: 18
-Prop-content-length: 140
-Content-length: 140
+Prop-content-length: 137
+Content-length: 137
 
 K 7
 svn:log
@@ -537,3 +537,29 @@ Text-content-md5: 5bbd00dab68c937673171d
 Content-length: 7
 
 boofar
+
+
+Revision-number: 19
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 18
+Revert revision 9.
+K 10
+svn:author
+V 5
+durin
+K 8
+svn:date
+V 27
+2009-06-29T00:13:01.537589Z
+PROPS-END
+
+Node-path: tags/will-edit/alpha
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 8
+Node-copyfrom-path: tags/will-edit/alpha
+Text-copy-source-md5: 9f9f90dbe3e5ee1218c86b8839db1995
--- a/tests/test_rebuildmeta.py
+++ b/tests/test_rebuildmeta.py
@@ -34,7 +34,7 @@ def _do_case(self, name, stupid):
         self.assertTrue(os.path.isfile(dtf), '%r is missing!' % tf)
         old, new = open(stf).read(), open(dtf).read()
         # uncomment next line for easy-ish debugging.
-        #os.system('diff -u %s %s' % (stf, dtf))
+        os.system('diff -u %s %s' % (stf, dtf))
         self.assertEqual(old, new)
         self.assertEqual(src.branchtags(), dest.branchtags())
     srcbi = pickle.load(open(os.path.join(src.path, 'svn', 'branch_info')))
--- a/tests/test_tags.py
+++ b/tests/test_tags.py
@@ -1,5 +1,7 @@
+import os
 import unittest
 
+from mercurial import commands
 from mercurial import hg
 from mercurial import node
 from mercurial import ui
@@ -92,28 +94,30 @@ class TestTags(test_util.TestBase):
     def test_edited_tag(self, stupid=False):
        repo = self._load_fixture_and_fetch('commit-to-tag.svndump',
                                            stupid=stupid)
-       self.assertEqual(len(repo.heads()), 4)
+       self.assertEqual(len(repo.heads()), 5)
        heads = repo.heads()
        openheads = [h for h in heads if not repo[h].extra().get('close', False)]
        closedheads = set(heads) - set(openheads)
-       self.assertEqual(len(openheads), 0)
+       self.assertEqual(len(openheads), 1)
        self.assertEqual(len(closedheads), 4)
-       closedheads = sorted(list(closedheads), cmp=lambda x,y: cmp(repo[x].rev(),
-                                                                   repo[y].rev()))
+       closedheads = sorted(list(closedheads),
+                            cmp=lambda x,y: cmp(repo[x].rev(), repo[y].rev()))
+
        # closeme has no open heads
        for h in openheads:
            self.assertNotEqual('closeme', repo[openheads[0]].branch())
 
        self.assertEqual(1, len(self.repo.branchheads('magic')))
 
-       willedit, alsoedit, editlater, closeme = closedheads
+       alsoedit, editlater, closeme, willedit, = closedheads
        self.assertEqual(
            repo[willedit].extra(),
            {'close': '1',
             'branch': 'magic',
-            'convert_revision': 'svn:af82cc90-c2d2-43cd-b1aa-c8a78449440a/tags/will-edit@9'})
+            'convert_revision': 'svn:af82cc90-c2d2-43cd-b1aa-c8a78449440a/tags/will-edit@19'})
        self.assertEqual(willedit, repo.tags()['will-edit'])
-       self.assertEqual(repo['will-edit'].manifest().keys(), ['beta',
+       self.assertEqual(repo['will-edit'].manifest().keys(), ['alpha',
+                                                              'beta',
                                                               'gamma',
                                                               ])
        self.assertEqual(
@@ -154,5 +158,13 @@ class TestTags(test_util.TestBase):
              'magic2': '\xa3\xa2D\x86aM\xc0v\xb9\xb0\x18\x14\xad\xacwBUi}\xe2',
              })
 
+    def test_old_tag_map_rebuilds(self):
+        repo = self._load_fixture_and_fetch('tag_name_same_as_branch.svndump')
+        tm = os.path.join(repo.path, 'svn', 'tagmap')
+        open(tm, 'w').write('1\n')
+        commands.pull(repo.ui, repo)
+        self.assertEqual(open(tm).read().splitlines()[0], '2')
+
+
 def suite():
     return unittest.TestLoader().loadTestsFromTestCase(TestTags)