Rev 2222: Add tag command and basic implementation in file:///home/mbp/bzr/Work/tags/
Martin Pool
mbp at sourcefrog.net
Tue Jan 16 13:21:42 GMT 2007
------------------------------------------------------------
revno: 2222
revision-id: mbp at sourcefrog.net-20070116132138-vhziz9hpfc8za0uj
parent: mbp at sourcefrog.net-20070114085612-q90lx2g0firzu7z9
committer: Martin Pool <mbp at sourcefrog.net>
branch nick: tags
timestamp: Wed 2007-01-17 00:21:38 +1100
message:
Add tag command and basic implementation
added:
bzrlib/tests/blackbox/test_tags.py test_tags.py-20070116132048-5h4qak2cm22jlb9e-1
modified:
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/errors.py errors.py-20050309040759-20512168c4e14fbd
bzrlib/repository.py rev_storage.py-20051111201905-119e9401e46257e3
bzrlib/tests/blackbox/__init__.py __init__.py-20051128053524-eba30d8255e08dc3
bzrlib/tests/repository_implementations/test_tags.py test_tags.py-20070114073919-azsbo9l26ph1rr09-1
bzrlib/tests/test_repository.py test_repository.py-20060131075918-65c555b881612f4d
=== added file 'bzrlib/tests/blackbox/test_tags.py'
--- a/bzrlib/tests/blackbox/test_tags.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/blackbox/test_tags.py 2007-01-16 13:21:38 +0000
@@ -0,0 +1,51 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""Tests for commands related to tags"""
+
+from bzrlib.branch import Branch
+from bzrlib.bzrdir import BzrDir
+from bzrlib.tests import TestCaseWithTransport
+from bzrlib.repository import RepositoryFormatKnit2
+
+
+class TestTagging(TestCaseWithTransport):
+
+ # as of 0.14, the default format doesn't do tags so we need to use a
+ # specific format
+
+ def make_branch_and_tree(self, relpath):
+ control = BzrDir.create(relpath)
+ repo = RepositoryFormatKnit2().initialize(control)
+ control.create_branch()
+ return control.create_workingtree()
+
+ def test_tag_command_help(self):
+ out, err = self.run_bzr_captured(['help', 'tag'])
+ self.assertContainsRe(out, 'Create a tag')
+
+ def test_tag_current_rev(self):
+ ## b = self.make_branch('branch')
+ ## b.repository.tag
+ t = self.make_branch_and_tree('branch')
+ t.commit(allow_pointless=True, message='initial commit',
+ rev_id='first-revid')
+ # make a tag through the command line
+ out, err = self.run_bzr('tag', '-d', 'branch', 'NEWTAG')
+ self.assertContainsRe(out, 'created tag NEWTAG')
+ # tag should be observable through the api
+ repo = t.branch.repository
+ self.assertEquals(repo.get_tag_dict(), dict(NEWTAG='first-revid'))
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2007-01-03 02:06:33 +0000
+++ b/bzrlib/builtins.py 2007-01-16 13:21:38 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2004, 2005, 2006 Canonical Ltd
+# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -2994,6 +2994,27 @@
server.serve()
+class cmd_tag(Command):
+ """Create a tag naming a revision"""
+
+ takes_args = ['tag_name']
+ takes_options = [
+ Option('directory',
+ help='branch or repository to tag',
+ short_name='d',
+ type=unicode,
+ ),
+ ]
+
+ def run(self, tag_name, directory='.'):
+ branch, relpath = Branch.open_containing(directory)
+ revision_id = branch.last_revision()
+ branch.repository.make_tag(tag_name, revision_id)
+ self.outf.write('created tag %s' % tag_name)
+
+
+
+
# command-line interpretation helper for merge-related commands
def _merge_helper(other_revision, base_revision,
check_clean=True, ignore_zero=False,
=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py 2006-12-21 20:07:34 +0000
+++ b/bzrlib/errors.py 2007-01-16 13:21:38 +0000
@@ -1602,3 +1602,11 @@
def __init__(self, name):
BzrError.__init__(self)
self.name = name
+
+
+class NoSuchTag(BzrError):
+
+ _fmt = "No such tag: %(tag_name)s"
+
+ def __init__(self, tag_name):
+ self.tag_name = tag_name
=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py 2007-01-14 08:56:12 +0000
+++ b/bzrlib/repository.py 2007-01-16 13:21:38 +0000
@@ -1090,10 +1090,15 @@
class KnitRepository2(KnitRepository):
"""Experimental enhanced knit format"""
+ # corresponds to RepositoryFormatKnit2
+
+ # TODO: within a lock scope, we could keep the tags in memory...
+
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
control_store, text_store):
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
_revision_store, control_store, text_store)
+ self._transport = control_files._transport
self._serializer = xml6.serializer_v6
def deserialise_inventory(self, revision_id, xml):
@@ -1133,8 +1138,37 @@
return RootCommitBuilder(self, parents, config, timestamp, timezone,
committer, revprops, revision_id)
- def get_tags(self):
- return None
+ @needs_read_lock
+ def get_tag_dict(self):
+ tag_content = self.control_files.get_utf8('tags').read()
+ return self._format._deserialize_tag_dict(tag_content)
+
+ @needs_write_lock
+ def _set_tag_dict(self, new_dict):
+ """Replace all tag definitions
+
+ :param new_dict: Dictionary from tag name to target.
+ """
+ self.control_files.put_utf8('tags', self._format._serialize_tag_dict(new_dict))
+
+ @needs_write_lock
+ def make_tag(self, tag_name, tag_target):
+ """Add a tag definition to the repository.
+
+ Behaviour if the tag is already present is not defined (yet).
+ """
+ # all done with a write lock held, so this looks atomic
+ td = self.get_tag_dict()
+ td[tag_name] = tag_target
+ self._set_tag_dict(td)
+
+ def lookup_tag(self, tag_name):
+ """Return the referent string of a tag"""
+ td = self.get_tag_dict()
+ try:
+ return td[tag_name]
+ except KeyError:
+ raise errors.NoSuchTag(tag_name)
class RepositoryFormat(object):
@@ -1222,7 +1256,7 @@
from bzrlib.store.revision.text import TextRevisionStore
dir_mode = control_files._dir_mode
file_mode = control_files._file_mode
- text_store =TextStore(transport.clone(name),
+ text_store = TextStore(transport.clone(name),
prefixed=prefixed,
compressed=compressed,
dir_mode=dir_mode,
@@ -1801,11 +1835,31 @@
control_files=control_files,
_revision_store=_revision_store,
control_store=control_store,
- text_store=text_store)
+ text_store=text_store)
+
+ def initialize(self, a_bzrdir, shared=False):
+ repo = super(RepositoryFormatKnit2, self).initialize(a_bzrdir, shared)
+ repo._transport.put_bytes_non_atomic('tags', '')
+ return repo
def supports_tags(self):
return True
+ def _serialize_tag_dict(self, tag_dict):
+ s = []
+ for tag, target in sorted(tag_dict.items()):
+ # TODO: check that tag names and targets are acceptable
+ s.append(tag + '\t' + target + '\n')
+ return ''.join(s)
+
+ def _deserialize_tag_dict(self, tag_content):
+ """Convert the tag file into a dictionary of tags"""
+ d = {}
+ for l in tag_content.splitlines():
+ tag, target = l.split('\t', 1)
+ d[tag] = target
+ return d
+
# formats which have no format string are not discoverable
=== modified file 'bzrlib/tests/blackbox/__init__.py'
--- a/bzrlib/tests/blackbox/__init__.py 2006-11-18 22:38:16 +0000
+++ b/bzrlib/tests/blackbox/__init__.py 2007-01-16 13:21:38 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -87,6 +87,7 @@
'bzrlib.tests.blackbox.test_shared_repository',
'bzrlib.tests.blackbox.test_sign_my_commits',
'bzrlib.tests.blackbox.test_status',
+ 'bzrlib.tests.blackbox.test_tags',
'bzrlib.tests.blackbox.test_testament',
'bzrlib.tests.blackbox.test_too_much',
'bzrlib.tests.blackbox.test_uncommit',
=== modified file 'bzrlib/tests/repository_implementations/test_tags.py'
--- a/bzrlib/tests/repository_implementations/test_tags.py 2007-01-14 08:56:12 +0000
+++ b/bzrlib/tests/repository_implementations/test_tags.py 2007-01-16 13:21:38 +0000
@@ -23,9 +23,11 @@
import bzrlib
from bzrlib import bzrdir, errors, repository
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
+from bzrlib.repository import Repository
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
from bzrlib.trace import mutter
from bzrlib.workingtree import WorkingTree
+
from bzrlib.tests.repository_implementations.test_repository \
import TestCaseWithRepository
@@ -44,6 +46,33 @@
raise TestSkipped("format %s doesn't support tags" % fmt)
TestCaseWithRepository.setUp(self)
- def test_foo_tags(self):
- repo = self.make_repository('repo')
- tags = repo.get_tags()
+ def test_tags_initially_empty(self):
+ repo = self.make_repository('repo')
+ tags = repo.get_tag_dict()
+ self.assertEqual(tags, {})
+
+ def test_set_get_tags(self):
+ # add two tags,
+ repo = self.make_repository('repo')
+ td = dict(stable='stable-revid', boring='boring-revid')
+ repo._set_tag_dict(td)
+ # then reopen the repo and see they're still there
+ repo = Repository.open('repo')
+ self.assertEqual(repo.get_tag_dict(), td)
+
+ def test_make_and_lookup_tag(self):
+ repo = self.make_repository('repo')
+ repo.make_tag('tag-name', 'target-revid-1')
+ result = repo.lookup_tag('tag-name')
+ self.assertEqual(result, 'target-revid-1')
+
+ def test_no_such_tag(self):
+ repo = self.make_repository('repo')
+ try:
+ repo.lookup_tag('bosko')
+ except errors.NoSuchTag, e:
+ self.assertEquals(e.tag_name, 'bosko')
+ self.assertEquals(str(e), 'No such tag: bosko')
+ else:
+ self.fail("didn't get expected exception")
+
=== modified file 'bzrlib/tests/test_repository.py'
--- a/bzrlib/tests/test_repository.py 2006-10-12 14:29:32 +0000
+++ b/bzrlib/tests/test_repository.py 2007-01-16 13:21:38 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2007 Canonical Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -321,7 +321,6 @@
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
self.check_knits(t)
-
class InterString(repository.InterRepository):
"""An inter-repository optimised code path for strings.
@@ -458,3 +457,15 @@
tree.commit("Another dull commit", rev_id='dull2')
revision_tree = tree.branch.repository.revision_tree('dull2')
self.assertEqual('dull', revision_tree.inventory.root.revision)
+
+ def test_tag_serialization(self):
+ """Test the precise representation of tag dicts."""
+ # Don't change this after we commit to this format, as it checks
+ # that the format is stable and compatible across releases
+ format = repository.RepositoryFormatKnit2()
+ td = dict(stable='stable-revid', boring='boring-revid')
+ packed = format._serialize_tag_dict(td)
+ expected = 'boring\tboring-revid\nstable\tstable-revid\n'
+ self.assertEqualDiff(packed, expected)
+ self.assertEqual(format._deserialize_tag_dict(packed), td)
+
More information about the bazaar-commits
mailing list