# HG changeset patch # User Augie Fackler # Date 1245810820 18000 # Node ID fbc7cf4fd7018095024c11784c0c7cb418b32b0e # Parent 0d3b5acb1d5178ee121f06d56bfaa8ac429dbae5 tags: reinstate a tag map file in a better way diff --git a/hgsubversion/editor.py b/hgsubversion/editor.py --- 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 diff --git a/hgsubversion/maps.py b/hgsubversion/maps.py --- 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 diff --git a/hgsubversion/svncommands.py b/hgsubversion/svncommands.py --- 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:') diff --git a/hgsubversion/svnmeta.py b/hgsubversion/svnmeta.py --- 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): diff --git a/tests/test_rebuildmeta.py b/tests/test_rebuildmeta.py --- 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)