changeset 448:fbc7cf4fd701

tags: reinstate a tag map file in a better way
author Augie Fackler <durin42@gmail.com>
date Tue, 23 Jun 2009 21:33:40 -0500
parents 0d3b5acb1d51
children bda5b47ad2a2
files hgsubversion/editor.py hgsubversion/maps.py hgsubversion/svncommands.py hgsubversion/svnmeta.py tests/test_rebuildmeta.py
diffstat 5 files changed, 85 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/hgsubversion/editor.py
+++ b/hgsubversion/editor.py
@@ -241,7 +241,8 @@ class HgEditor(delta.Editor):
                 self.current.missing.add('%s/' % path)
                 return path
         if tag:
-            source_branch, source_rev = self.meta.tags[tag]
+            ci = self.meta.repo[self.meta.tags[tag]].extra()['convert_revision']
+            source_rev, source_branch, = self.meta.parse_converted_revision(ci)
             cp_f = ''
         else:
             source_rev = copyfrom_revision
--- a/hgsubversion/maps.py
+++ b/hgsubversion/maps.py
@@ -95,6 +95,57 @@ class AuthorMap(dict):
             return author.rsplit('@', 1)[0]
 
 
+class TagMap(dict):
+
+    VERSION = 1
+
+    def __init__(self, repo):
+        dict.__init__(self)
+        self.path = os.path.join(repo.path, 'svn', 'tagmap')
+        self.seen = 0
+        if os.path.isfile(self.path):
+            self._load()
+        else:
+            self._write()
+
+    def _load(self):
+        f = open(self.path)
+        ver = int(f.readline())
+        if ver != self.VERSION:
+            print 'tagmap too new -- please upgrade'
+            raise NotImplementedError
+        for l in f:
+            hash, tag = l.split(' ', 1)
+            tag = tag[:-1]
+            dict.__setitem__(self, tag, node.bin(hash))
+        f.close()
+
+    def _write(self):
+        f = open(self.path, 'w')
+        f.write('%s\n' % self.VERSION)
+        f.flush()
+        f.close()
+
+    def update(self, other):
+        for k,v in other.iteritems():
+            self[k] = v
+
+    def __contains__(self, tag):
+        return dict.__contains__(self, tag) and dict.__getitem__(self, tag) != node.nullid
+
+    def __getitem__(self, tag):
+        if tag in self:
+            return dict.__getitem__(self, tag)
+        raise KeyError()
+
+    def __setitem__(self, tag, hash):
+        f = open(self.path, 'a')
+        f.write(node.hex(hash) + ' ' + tag + '\n')
+        f.flush()
+        f.close()
+        dict.__setitem__(self, tag, hash)
+
+
 class RevMap(dict):
 
     VERSION = 1
--- a/hgsubversion/svncommands.py
+++ b/hgsubversion/svncommands.py
@@ -5,6 +5,7 @@ from mercurial import hg
 from mercurial import node
 from mercurial import util as hgutil
 
+import maps
 import svnwrap
 import svnmeta
 import util
@@ -85,12 +86,22 @@ def rebuildmeta(ui, repo, hg_repo_path, 
     last_rev = -1
     branchinfo = {}
     noderevnums = {}
+    tags = maps.TagMap(repo)
     for rev in repo:
 
         ctx = repo[rev]
         convinfo = ctx.extra().get('convert_revision', None)
         if not convinfo:
             continue
+        if '.hgtags' in ctx.files():
+            parent = ctx.parents()[0]
+            parentdata = ''
+            if '.hgtags' in parent:
+                parentdata = parent.filectx('.hgtags').data()
+            newdata = ctx.filectx('.hgtags').data()
+            for newtag in newdata[len(parentdata):-1].split('\n'):
+                ha, tag = newtag.split(' ', 1)
+                tags[tag] = node.bin(ha)
 
         # check that the conversion metadata matches expectations
         assert convinfo.startswith('svn:')
--- a/hgsubversion/svnmeta.py
+++ b/hgsubversion/svnmeta.py
@@ -62,7 +62,7 @@ class SVNMeta(object):
             f = open(self.branch_info_file)
             self.branches = pickle.load(f)
             f.close()
-        self.tags = {}
+        self.tags = maps.TagMap(repo)
         if os.path.exists(self.tag_locations_file):
             f = open(self.tag_locations_file)
             self.tag_locations = pickle.load(f)
@@ -263,7 +263,8 @@ class SVNMeta(object):
             if src_tag != False or src_file == '': # case 2
                 ln = self.localname(p)
                 if src_tag != False:
-                    src_branch, src_rev = self.tags[src_tag]
+                    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)}
         return {}
 
@@ -311,13 +312,18 @@ class SVNMeta(object):
         '''
         tag = self.is_path_tag(self.remotename(branch))
         if tag and tag in self.tags:
-            br, r = self.tags[tag]
+            ha = self.tags[tag]
+            r, br = self.parse_converted_revision(self.repo[ha].extra()['convert_revision'])
         else:
             r, br = self.get_parent_svn_branch_and_rev(number, branch)
         if r is not None:
             return self.revmap[r, br]
         return revlog.nullid
 
+    def parse_converted_revision(self, convertedrev):
+        branch, revnum = convertedrev[40:].rsplit('@', 1)
+        return int(revnum), self.localname(self.normalize(branch))
+
     def update_branch_tag_map_for_rev(self, revision):
         paths = revision.paths
         added_branches = {}
@@ -338,7 +344,8 @@ class SVNMeta(object):
                         from_tag = self.is_path_tag(src_p)
                         if not from_tag:
                             continue
-                        branch, src_rev = self.tags[from_tag]
+                        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:
@@ -395,21 +402,18 @@ class SVNMeta(object):
                     and branch not in added_branches):
                     parent = {branch: (None, 0, revision.revnum)}
             added_branches.update(parent)
-        rmtags = dict((t, self.tags[t][0]) for t in tags_to_delete)
+        def branchoftag(tag):
+            cr = self.repo[self.tags[tag]].extra()['convert_revision']
+            return self.parse_converted_revision(cr)[1]
+        rmtags = dict((t, branchoftag(t)) for t in tags_to_delete)
         return {
             'tags': (added_tags, rmtags),
             'branches': (added_branches, self.closebranches),
         }
 
     def save_tbdelta(self, tbdelta):
-        for t in tbdelta['tags'][1]:
-            del self.tags[t]
         for br in tbdelta['branches'][1]:
             del self.branches[br]
-        for t, info in tbdelta['tags'][0].items():
-            self.ui.status('Tagged %s@%s as %s\n' %
-                           (info[0] or 'trunk', info[1], t))
-        self.tags.update(tbdelta['tags'][0])
         self.branches.update(tbdelta['branches'][0])
 
     def movetag(self, tag, hash, branch, rev, date):
@@ -438,11 +442,10 @@ class SVNMeta(object):
                              date,
                              pextra)
         new_hash = self.repo.commitctx(ctx)
-        prefix, revnum = pextra['convert_revision'].rsplit('@', 1)
-        branch = self.localname(self.normalize('/' + prefix.split('/', 1)[1]))
-        assert self.revmap[int(revnum), branch] == parentctx.node()
-        self.revmap[int(revnum), branch] = new_hash
-
+        revnum, branch = self.parse_converted_revision(pextra['convert_revision'])
+        assert self.revmap[revnum, branch] == parentctx.node()
+        self.revmap[revnum, branch] = new_hash
+        self.tags[tag] = hash
         util.describe_commit(self.ui, new_hash, branch)
 
     def committags(self, delta, rev, endbranches):
@@ -471,6 +474,7 @@ class SVNMeta(object):
                 elif op == 'rm':
                     tagged = node.hex(node.nullid)
                 src += '%s %s\n' % (tagged, tag)
+                self.tags[tag] = node.bin(tagged)
 
             # add new changeset containing updated .hgtags
             def fctxfun(repo, memctx, path):
--- a/tests/test_rebuildmeta.py
+++ b/tests/test_rebuildmeta.py
@@ -27,7 +27,7 @@ def _do_case(self, name, stupid):
     self.assertTrue(os.path.isdir(os.path.join(src.path, 'svn')),
                     'no .hg/svn directory in the destination!')
     dest = hg.repository(u, os.path.dirname(dest.path))
-    for tf in ('rev_map', 'uuid'):
+    for tf in ('rev_map', 'uuid', 'tagmap', ):
         stf = os.path.join(src.path, 'svn', tf)
         self.assertTrue(os.path.isfile(stf), '%r is missing!' % stf)
         dtf = os.path.join(dest.path, 'svn', tf)