# HG changeset patch # User Augie Fackler # Date 1246477366 18000 # Node ID bb612e625be6e2d88033eeed335cac021d989970 # Parent ae35c389cdefa32c0e0e3bc806eb6adfb3bfbdac tags: handle copyfrom old versions of tags more correctly diff --git a/hgsubversion/maps.py b/hgsubversion/maps.py --- 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) diff --git a/hgsubversion/svncommands.py b/hgsubversion/svncommands.py --- 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:') diff --git a/hgsubversion/svnmeta.py b/hgsubversion/svnmeta.py --- a/hgsubversion/svnmeta.py +++ b/hgsubversion/svnmeta.py @@ -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: @@ -349,7 +350,8 @@ class SVNMeta(object): added_tags[t_name] = branch, src_rev elif file: t_name = t_name[:-(len(file)+1)] - if src_rev > added_tags[t_name][1]: + if (t_name in added_tags + and src_rev > added_tags[t_name][1]): added_tags[t_name] = branch, src_rev elif (paths[p].action == 'D' and p.endswith(t_name) and t_name in self.tags): @@ -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): @@ -487,7 +489,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): diff --git a/tests/fixtures/commit-to-tag.sh b/tests/fixtures/commit-to-tag.sh --- 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 diff --git a/tests/fixtures/commit-to-tag.svndump b/tests/fixtures/commit-to-tag.svndump --- 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 diff --git a/tests/test_rebuildmeta.py b/tests/test_rebuildmeta.py --- 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'))) diff --git a/tests/test_tags.py b/tests/test_tags.py --- 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)