1
# Copyright (C) 2007-2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for commands related to tags"""
23
from bzrlib.branch import (
26
from bzrlib.bzrdir import BzrDir
27
from bzrlib.tests import TestCaseWithTransport
28
from bzrlib.repository import (
31
from bzrlib.workingtree import WorkingTree
34
class TestTagging(TestCaseWithTransport):
36
# as of 0.14, the default format doesn't do tags so we need to use a
39
def make_branch_and_tree(self, relpath):
40
format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
41
return TestCaseWithTransport.make_branch_and_tree(self, relpath,
44
def test_tag_command_help(self):
45
out, err = self.run_bzr('help tag')
46
self.assertContainsRe(out, 'Create, remove or modify a tag')
48
def test_cannot_tag_range(self):
49
out, err = self.run_bzr('tag -r1..10 name', retcode=3)
50
self.assertContainsRe(err,
51
"Tags can only be placed on a single revision")
53
def test_no_tag_name(self):
54
out, err = self.run_bzr('tag -d branch', retcode=3)
55
self.assertContainsRe(err, 'Please specify a tag name.')
57
def test_automatic_tag_name(self):
58
def get_tag_name(branch, revid):
60
Branch.hooks.install_named_hook('automatic_tag_name',
61
get_tag_name, 'get tag name')
62
out, err = self.run_bzr('tag -d branch')
63
self.assertContainsRe(out, 'Created tag mytag.')
65
def test_tag_current_rev(self):
66
t = self.make_branch_and_tree('branch')
67
t.commit(allow_pointless=True, message='initial commit',
69
# make a tag through the command line
70
out, err = self.run_bzr('tag -d branch NEWTAG')
71
self.assertContainsRe(out, 'Created tag NEWTAG.')
72
# tag should be observable through the api
73
self.assertEquals(t.branch.tags.get_tag_dict(),
74
dict(NEWTAG='first-revid'))
75
# can also create tags using -r
76
self.run_bzr('tag -d branch tag2 -r1')
77
self.assertEquals(t.branch.tags.lookup_tag('tag2'), 'first-revid')
78
# regression test: make sure a unicode revision from the user
79
# gets turned into a str object properly. The use of a unicode
80
# object for the revid is intentional.
81
self.run_bzr(['tag', '-d', 'branch', 'tag3', u'-rrevid:first-revid'])
82
self.assertEquals(t.branch.tags.lookup_tag('tag3'), 'first-revid')
83
# can also delete an existing tag
84
out, err = self.run_bzr('tag --delete -d branch tag2')
85
# cannot replace an existing tag normally
86
out, err = self.run_bzr('tag -d branch NEWTAG', retcode=3)
87
self.assertContainsRe(err, 'Tag NEWTAG already exists\\.')
88
# ... but can if you use --force
89
out, err = self.run_bzr('tag -d branch NEWTAG --force')
91
def test_tag_delete_requires_name(self):
92
out, err = self.run_bzr('tag -d branch', retcode=3)
93
self.assertContainsRe(err, 'Please specify a tag name\\.')
95
def test_branch_push_pull_merge_copies_tags(self):
96
t = self.make_branch_and_tree('branch1')
97
t.commit(allow_pointless=True, message='initial commit',
100
b1.tags.set_tag('tag1', 'first-revid')
101
# branching copies the tag across
102
self.run_bzr('branch branch1 branch2')
103
b2 = Branch.open('branch2')
104
self.assertEquals(b2.tags.lookup_tag('tag1'), 'first-revid')
105
# make a new tag and pull it
106
b1.tags.set_tag('tag2', 'twa')
107
self.run_bzr('pull -d branch2 branch1')
108
self.assertEquals(b2.tags.lookup_tag('tag2'), 'twa')
109
# make a new tag and push it
110
b1.tags.set_tag('tag3', 'san')
111
self.run_bzr('push -d branch1 branch2')
112
self.assertEquals(b2.tags.lookup_tag('tag3'), 'san')
113
# make a new tag and merge it
114
t.commit(allow_pointless=True, message='second commit',
115
rev_id='second-revid')
116
t2 = WorkingTree.open('branch2')
117
t2.commit(allow_pointless=True, message='commit in second')
118
b1.tags.set_tag('tag4', 'second-revid')
119
self.run_bzr('merge -d branch2 branch1')
120
self.assertEquals(b2.tags.lookup_tag('tag4'), 'second-revid')
121
# pushing to a new location copies the tag across
122
self.run_bzr('push -d branch1 branch3')
123
b3 = Branch.open('branch3')
124
self.assertEquals(b3.tags.lookup_tag('tag1'), 'first-revid')
126
def test_list_tags(self):
127
tree1 = self.make_branch_and_tree('branch1')
128
tree1.commit(allow_pointless=True, message='revision 1',
129
rev_id='revid-1', timestamp=10)
130
tree1.commit(allow_pointless=True, message='revision 2',
131
rev_id='revid-2', timestamp=15)
134
# note how the tag for revid-1 sorts after the one for revid-2
135
b1.tags.set_tag(u'tagA\u30d0', 'revid-2')
136
b1.tags.set_tag(u'tagB\u30d0', 'missing') # not present in repository
137
b1.tags.set_tag(u'tagC\u30d0', 'revid-1')
139
# lexicographical order
140
out, err = self.run_bzr('tags -d branch1', encoding='utf-8')
141
self.assertEquals(err, '')
142
self.assertContainsRe(out, (u'^tagA\u30d0 *2\ntagB\u30d0 *\\?\n' +
143
u'tagC\u30d0 *1\n').encode('utf-8'))
145
out, err = self.run_bzr('tags --show-ids -d branch1', encoding='utf-8')
146
self.assertEquals(err, '')
147
self.assertContainsRe(out, (u'^tagA\u30d0 *revid-2\n' +
148
u'tagB\u30d0 *missing\ntagC\u30d0 *revid-1\n').encode('utf-8'))
150
# chronological order
151
out, err = self.run_bzr('tags --sort=time -d branch1',
153
self.assertEquals(err, '')
154
self.assertContainsRe(out, (u'^tagC\u30d0 *1\ntagA\u30d0 *2\n' +
155
u'tagB\u30d0 *\\?\n').encode('utf-8'))
157
out, err = self.run_bzr('tags --sort=time --show-ids -d branch1',
159
self.assertEquals(err, '')
160
self.assertContainsRe(out, (u'^tagC\u30d0 *revid-1\n' +
161
u'tagA\u30d0 *revid-2\ntagB\u30d0 *missing\n').encode('utf-8'))
163
# now test dotted revnos
164
tree2 = tree1.bzrdir.sprout('branch2').open_workingtree()
165
tree1.commit(allow_pointless=True, message='revision 3 in branch1',
167
tree2.commit(allow_pointless=True, message='revision 3 in branch2',
171
b2.tags.set_tag('tagD', 'revid-3b')
172
self.run_bzr('merge -d branch1 branch2')
173
tree1.commit('merge', rev_id='revid-4')
175
out, err = self.run_bzr('tags -d branch1', encoding='utf-8')
176
self.assertEquals(err, '')
177
self.assertContainsRe(out, r'tagD *2\.1\.1\n')
178
out, err = self.run_bzr('tags -d branch2', encoding='utf-8')
179
self.assertEquals(err, '')
180
self.assertContainsRe(out, r'tagD *3\n')
182
def test_list_tags_revision_filtering(self):
183
tree1 = self.make_branch_and_tree('.')
184
tree1.commit(allow_pointless=True, message='revision 1',
186
tree1.commit(allow_pointless=True, message='revision 2',
188
tree1.commit(allow_pointless=True, message='revision 3',
190
tree1.commit(allow_pointless=True, message='revision 4',
193
b1.tags.set_tag(u'tag 1', 'revid-1')
194
b1.tags.set_tag(u'tag 2', 'revid-2')
195
b1.tags.set_tag(u'tag 3', 'revid-3')
196
b1.tags.set_tag(u'tag 4', 'revid-4')
197
self._check_tag_filter('', (1, 2, 3, 4))
198
self._check_tag_filter('-r ..', (1, 2, 3, 4))
199
self._check_tag_filter('-r ..2', (1, 2))
200
self._check_tag_filter('-r 2..', (2, 3, 4))
201
self._check_tag_filter('-r 2..3', (2, 3))
202
self._check_tag_filter('-r 3..2', ())
203
self.run_bzr_error(args="tags -r 123",
204
error_regexes=["bzr: ERROR: Requested revision: '123' "
205
"does not exist in branch:"])
206
self.run_bzr_error(args="tags -r ..123",
207
error_regexes=["bzr: ERROR: Requested revision: '123' "
208
"does not exist in branch:"])
209
self.run_bzr_error(args="tags -r 123.123",
210
error_regexes=["bzr: ERROR: Requested revision: '123.123' "
211
"does not exist in branch:"])
213
def _check_tag_filter(self, argstr, expected_revnos):
214
#upper bound of laziness
215
out, err = self.run_bzr('tags ' + argstr)
216
self.assertEquals(err, '')
217
self.assertContainsRe(out, "^" + ''.join(["tag %s +%s\n" % (
218
revno, revno) for revno in expected_revnos]) + "$")
220
def test_conflicting_tags(self):
221
# setup two empty branches with different tags
222
t1 = self.make_branch_and_tree('one')
223
t2 = self.make_branch_and_tree('two')
226
tagname = u'\u30d0zaar'
227
b1.tags.set_tag(tagname, 'revid1')
228
b2.tags.set_tag(tagname, 'revid2')
229
# push should give a warning about the tags
230
out, err = self.run_bzr('push -d one two', encoding='utf-8')
231
self.assertContainsRe(out,
232
'Conflicting tags:\n.*' + tagname.encode('utf-8'))
233
# pull should give a warning about the tags
234
out, err = self.run_bzr('pull -d one two', encoding='utf-8')
235
self.assertContainsRe(out,
236
'Conflicting tags:\n.*' + tagname.encode('utf-8'))
237
# merge should give a warning about the tags -- not implemented yet
238
## out, err = self.run_bzr('merge -d one two', encoding='utf-8')
239
## self.assertContainsRe(out,
240
## 'Conflicting tags:\n.*' + tagname.encode('utf-8'))