1
# Copyright (C) 2004, 2005 by Canonical Ltd
1
# Copyright (C) 2005-2011 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
from bzrlib.branch import Branch
21
from bzrlib.tests import TestCaseInTempDir
22
from bzrlib.errors import NoCommonAncestor, NoCommits
23
from bzrlib.errors import NoSuchRevision
24
from bzrlib.merge import merge
25
from bzrlib.revisionspec import RevisionSpec
27
class TestRevisionNamespaces(TestCaseInTempDir):
29
def test_revision_namespaces(self):
30
"""Test revision specifiers.
32
These identify revisions by date, etc."""
34
b = Branch.initialize(u'.')
36
b.working_tree().commit('Commit one', rev_id='a@r-0-1', timestamp=time.time() - 60*60*24)
37
b.working_tree().commit('Commit two', rev_id='a@r-0-2')
38
b.working_tree().commit('Commit three', rev_id='a@r-0-3')
40
self.assertEquals(RevisionSpec(None).in_history(b), (0, None))
41
self.assertEquals(RevisionSpec(1).in_history(b), (1, 'a@r-0-1'))
42
self.assertEquals(RevisionSpec('revno:1').in_history(b),
44
self.assertEquals(RevisionSpec('revid:a@r-0-1').in_history(b),
46
self.assertRaises(NoSuchRevision,
47
RevisionSpec('revid:a@r-0-0').in_history, b)
48
self.assertRaises(TypeError, RevisionSpec, object)
50
self.assertEquals(RevisionSpec('date:today').in_history(b),
52
self.assertEquals(RevisionSpec('date:yesterday').in_history(b),
54
self.assertEquals(RevisionSpec('before:date:today').in_history(b),
57
self.assertEquals(RevisionSpec('last:1').in_history(b),
59
self.assertEquals(RevisionSpec('-1').in_history(b), (3, 'a@r-0-3'))
60
# self.assertEquals(b.get_revision_info('last:1'), (3, 'a@r-0-3'))
61
# self.assertEquals(b.get_revision_info('-1'), (3, 'a@r-0-3'))
63
self.assertEquals(RevisionSpec('ancestor:.').in_history(b).rev_id,
67
b2 = Branch.initialize('newbranch')
68
self.assertRaises(NoCommits, RevisionSpec('ancestor:.').in_history, b2)
72
b3.working_tree().commit('Commit four', rev_id='b@r-0-4')
73
self.assertEquals(RevisionSpec('ancestor:.').in_history(b3).rev_id,
75
merge(['copy', -1], [None, None])
76
b.working_tree().commit('Commit five', rev_id='a@r-0-4')
77
self.assertEquals(RevisionSpec('ancestor:copy').in_history(b).rev_id,
79
self.assertEquals(RevisionSpec('ancestor:.').in_history(b3).rev_id,
82
# This should be in the revision store, but not in revision-history
83
self.assertEquals((None, 'b@r-0-4'),
84
RevisionSpec('revid:b@r-0-4').in_history(b))
86
def test_branch_namespace(self):
87
"""Ensure that the branch namespace pulls in the requisite content."""
88
self.build_tree(['branch1/', 'branch1/file', 'branch2/'])
89
branch = Branch.initialize('branch1')
90
branch.working_tree().add(['file'])
91
branch.working_tree().commit('add file')
92
branch.clone('branch2')
93
print >> open('branch2/file', 'w'), 'new content'
94
branch2 = Branch.open('branch2')
95
branch2.working_tree().commit('update file', rev_id='A')
96
spec = RevisionSpec('branch:./branch2/.bzr/../')
97
rev_info = spec.in_history(branch)
98
self.assertEqual(rev_info, (None, 'A'))
22
revision as _mod_revision,
25
from bzrlib.tests import TestCaseWithTransport
26
from bzrlib.revisionspec import (
34
def spec_in_history(spec, branch):
35
"""A simple helper to change a revision spec into a branch search"""
36
return RevisionSpec.from_string(spec).in_history(branch)
39
# Basic class, which just creates a really basic set of revisions
40
class TestRevisionSpec(TestCaseWithTransport):
43
super(TestRevisionSpec, self).setUp()
44
# this sets up a revision graph:
49
self.tree = self.make_branch_and_tree('tree')
50
self.build_tree(['tree/a'])
51
self.tree.lock_write()
52
self.addCleanup(self.tree.unlock)
54
self.tree.commit('a', rev_id='r1')
56
self.tree2 = self.tree.bzrdir.sprout('tree2').open_workingtree()
57
self.tree2.commit('alt', rev_id='alt_r2')
59
self.tree.merge_from_branch(self.tree2.branch)
60
self.tree.commit('second', rev_id='r2')
62
def get_in_history(self, revision_spec):
63
return spec_in_history(revision_spec, self.tree.branch)
65
def assertInHistoryIs(self, exp_revno, exp_revision_id, revision_spec):
66
rev_info = self.get_in_history(revision_spec)
67
self.assertEqual(exp_revno, rev_info.revno,
68
'Revision spec: %r returned wrong revno: %r != %r'
69
% (revision_spec, exp_revno, rev_info.revno))
70
self.assertEqual(exp_revision_id, rev_info.rev_id,
71
'Revision spec: %r returned wrong revision id:'
73
% (revision_spec, exp_revision_id, rev_info.rev_id))
75
def assertInvalid(self, revision_spec, extra='',
76
invalid_as_revision_id=True):
78
self.get_in_history(revision_spec)
79
except errors.InvalidRevisionSpec, e:
80
self.assertEqual(revision_spec, e.spec)
81
self.assertEqual(extra, e.extra)
83
self.fail('Expected InvalidRevisionSpec to be raised for'
84
' %r.in_history' % (revision_spec,))
85
if invalid_as_revision_id:
87
spec = RevisionSpec.from_string(revision_spec)
88
spec.as_revision_id(self.tree.branch)
89
except errors.InvalidRevisionSpec, e:
90
self.assertEqual(revision_spec, e.spec)
91
self.assertEqual(extra, e.extra)
93
self.fail('Expected InvalidRevisionSpec to be raised for'
94
' %r.as_revision_id' % (revision_spec,))
96
def assertAsRevisionId(self, revision_id, revision_spec):
97
"""Calling as_revision_id() should return the specified id."""
98
spec = RevisionSpec.from_string(revision_spec)
99
self.assertEqual(revision_id,
100
spec.as_revision_id(self.tree.branch))
102
def get_as_tree(self, revision_spec, tree=None):
105
spec = RevisionSpec.from_string(revision_spec)
106
return spec.as_tree(tree.branch)
109
class RevisionSpecMatchOnTrap(RevisionSpec):
111
def _match_on(self, branch, revs):
112
self.last_call = (branch, revs)
113
return super(RevisionSpecMatchOnTrap, self)._match_on(branch, revs)
116
class TestRevisionSpecBase(TestRevisionSpec):
118
def test_wants_revision_history(self):
119
# If wants_revision_history = True, then _match_on should get the
120
# branch revision history
121
spec = RevisionSpecMatchOnTrap('foo', _internal=True)
122
spec.wants_revision_history = True
123
self.callDeprecated(['RevisionSpec.wants_revision_history was '
124
'deprecated in 2.5 (RevisionSpecMatchOnTrap).'],
125
spec.in_history, self.tree.branch)
127
self.assertEqual((self.tree.branch, ['r1' ,'r2']),
130
def test_wants_no_revision_history(self):
131
# If wants_revision_history = False, then _match_on should get None for
132
# the branch revision history
133
spec = RevisionSpecMatchOnTrap('foo', _internal=True)
134
spec.in_history(self.tree.branch)
136
self.assertEqual((self.tree.branch, None), spec.last_call)
140
class TestOddRevisionSpec(TestRevisionSpec):
141
"""Test things that aren't normally thought of as revision specs"""
144
self.assertInHistoryIs(None, None, None)
146
def test_object(self):
147
self.assertRaises(TypeError, RevisionSpec.from_string, object())
150
class RevisionSpec_bork(RevisionSpec):
152
prefix = 'irrelevant:'
154
def _match_on(self, branch, revs):
155
if self.spec == "bork":
156
return RevisionInfo.from_revision_id(branch, "r1")
158
raise errors.InvalidRevisionSpec(self.spec, branch)
161
class TestRevisionSpec_dwim(TestRevisionSpec):
163
# Don't need to test revno's explicitly since TRS_revno already
164
# covers that well for us
165
def test_dwim_spec_revno(self):
166
self.assertInHistoryIs(2, 'r2', '2')
167
self.assertAsRevisionId('alt_r2', '1.1.1')
169
def test_dwim_spec_revid(self):
170
self.assertInHistoryIs(2, 'r2', 'r2')
172
def test_dwim_spec_tag(self):
173
self.tree.branch.tags.set_tag('footag', 'r1')
174
self.assertAsRevisionId('r1', 'footag')
175
self.tree.branch.tags.delete_tag('footag')
176
self.assertRaises(errors.InvalidRevisionSpec,
177
self.get_in_history, 'footag')
179
def test_dwim_spec_tag_that_looks_like_revno(self):
180
# Test that we slip past revno with things that look like revnos,
181
# but aren't. Tags are convenient for testing this since we can
182
# make them look however we want.
183
self.tree.branch.tags.set_tag('3', 'r2')
184
self.assertAsRevisionId('r2', '3')
185
self.build_tree(['tree/b'])
187
self.tree.commit('b', rev_id='r3')
188
self.assertAsRevisionId('r3', '3')
190
def test_dwim_spec_date(self):
191
self.assertAsRevisionId('r1', 'today')
193
def test_dwim_spec_branch(self):
194
self.assertInHistoryIs(None, 'alt_r2', 'tree2')
196
def test_dwim_spec_nonexistent(self):
197
self.assertInvalid('somethingrandom', invalid_as_revision_id=False)
198
self.assertInvalid('-1.1', invalid_as_revision_id=False)
199
self.assertInvalid('.1', invalid_as_revision_id=False)
200
self.assertInvalid('1..1', invalid_as_revision_id=False)
201
self.assertInvalid('1.2..1', invalid_as_revision_id=False)
202
self.assertInvalid('1.', invalid_as_revision_id=False)
204
def test_append_dwim_revspec(self):
205
original_dwim_revspecs = list(RevisionSpec_dwim._possible_revspecs)
206
def reset_dwim_revspecs():
207
RevisionSpec_dwim._possible_revspecs = original_dwim_revspecs
208
self.addCleanup(reset_dwim_revspecs)
209
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_bork)
210
self.assertAsRevisionId('r1', 'bork')
212
def test_append_lazy_dwim_revspec(self):
213
original_dwim_revspecs = list(RevisionSpec_dwim._possible_revspecs)
214
def reset_dwim_revspecs():
215
RevisionSpec_dwim._possible_revspecs = original_dwim_revspecs
216
self.addCleanup(reset_dwim_revspecs)
217
RevisionSpec_dwim.append_possible_lazy_revspec(
218
"bzrlib.tests.test_revisionspec", "RevisionSpec_bork")
219
self.assertAsRevisionId('r1', 'bork')
222
class TestRevisionSpec_revno(TestRevisionSpec):
224
def test_positive_int(self):
225
self.assertInHistoryIs(0, 'null:', '0')
226
self.assertInHistoryIs(1, 'r1', '1')
227
self.assertInHistoryIs(2, 'r2', '2')
228
self.assertInvalid('3')
230
def test_dotted_decimal(self):
231
self.assertInHistoryIs(None, 'alt_r2', '1.1.1')
232
self.assertInvalid('1.1.123')
234
def test_negative_int(self):
235
self.assertInHistoryIs(2, 'r2', '-1')
236
self.assertInHistoryIs(1, 'r1', '-2')
238
self.assertInHistoryIs(1, 'r1', '-3')
239
self.assertInHistoryIs(1, 'r1', '-4')
240
self.assertInHistoryIs(1, 'r1', '-100')
242
def test_positive(self):
243
self.assertInHistoryIs(0, 'null:', 'revno:0')
244
self.assertInHistoryIs(1, 'r1', 'revno:1')
245
self.assertInHistoryIs(2, 'r2', 'revno:2')
247
self.assertInvalid('revno:3')
249
def test_negative(self):
250
self.assertInHistoryIs(2, 'r2', 'revno:-1')
251
self.assertInHistoryIs(1, 'r1', 'revno:-2')
253
self.assertInHistoryIs(1, 'r1', 'revno:-3')
254
self.assertInHistoryIs(1, 'r1', 'revno:-4')
256
def test_invalid_number(self):
257
# Get the right exception text
260
except ValueError, e:
262
self.assertInvalid('revno:X', extra='\n' + str(e))
264
def test_missing_number_and_branch(self):
265
self.assertInvalid('revno::',
266
extra='\ncannot have an empty revno and no branch')
268
def test_invalid_number_with_branch(self):
271
except ValueError, e:
273
self.assertInvalid('revno:X:tree2', extra='\n' + str(e))
275
def test_non_exact_branch(self):
276
# It seems better to require an exact path to the branch
277
# Branch.open() rather than using Branch.open_containing()
278
spec = RevisionSpec.from_string('revno:2:tree2/a')
279
self.assertRaises(errors.NotBranchError,
280
spec.in_history, self.tree.branch)
282
def test_with_branch(self):
283
# Passing a URL overrides the supplied branch path
284
revinfo = self.get_in_history('revno:2:tree2')
285
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
286
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
287
self.assertEqual(2, revinfo.revno)
288
self.assertEqual('alt_r2', revinfo.rev_id)
290
def test_int_with_branch(self):
291
revinfo = self.get_in_history('2:tree2')
292
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
293
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
294
self.assertEqual(2, revinfo.revno)
295
self.assertEqual('alt_r2', revinfo.rev_id)
297
def test_with_url(self):
298
url = self.get_url() + '/tree2'
299
revinfo = self.get_in_history('revno:2:%s' % (url,))
300
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
301
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
302
self.assertEqual(2, revinfo.revno)
303
self.assertEqual('alt_r2', revinfo.rev_id)
305
def test_negative_with_url(self):
306
url = self.get_url() + '/tree2'
307
revinfo = self.get_in_history('revno:-1:%s' % (url,))
308
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
309
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
310
self.assertEqual(2, revinfo.revno)
311
self.assertEqual('alt_r2', revinfo.rev_id)
313
def test_different_history_lengths(self):
314
# Make sure we use the revisions and offsets in the supplied branch
315
# not the ones in the original branch.
316
self.tree2.commit('three', rev_id='r3')
317
self.assertInHistoryIs(3, 'r3', 'revno:3:tree2')
318
self.assertInHistoryIs(3, 'r3', 'revno:-1:tree2')
320
def test_invalid_branch(self):
321
self.assertRaises(errors.NotBranchError,
322
self.get_in_history, 'revno:-1:tree3')
324
def test_invalid_revno_in_branch(self):
325
self.tree.commit('three', rev_id='r3')
326
self.assertInvalid('revno:3:tree2')
328
def test_revno_n_path(self):
329
"""Old revno:N:path tests"""
330
wta = self.make_branch_and_tree('a')
333
wta.commit('Commit one', rev_id='a@r-0-1')
334
wta.commit('Commit two', rev_id='a@r-0-2')
335
wta.commit('Commit three', rev_id='a@r-0-3')
337
wtb = self.make_branch_and_tree('b')
340
wtb.commit('Commit one', rev_id='b@r-0-1')
341
wtb.commit('Commit two', rev_id='b@r-0-2')
342
wtb.commit('Commit three', rev_id='b@r-0-3')
345
self.assertEqual((1, 'a@r-0-1'),
346
spec_in_history('revno:1:a/', ba))
347
# The argument of in_history should be ignored since it is
348
# redundant with the path in the spec.
349
self.assertEqual((1, 'a@r-0-1'),
350
spec_in_history('revno:1:a/', None))
351
self.assertEqual((1, 'a@r-0-1'),
352
spec_in_history('revno:1:a/', bb))
353
self.assertEqual((2, 'b@r-0-2'),
354
spec_in_history('revno:2:b/', None))
356
def test_as_revision_id(self):
357
self.assertAsRevisionId('null:', '0')
358
self.assertAsRevisionId('r1', '1')
359
self.assertAsRevisionId('r2', '2')
360
self.assertAsRevisionId('r1', '-2')
361
self.assertAsRevisionId('r2', '-1')
362
self.assertAsRevisionId('alt_r2', '1.1.1')
364
def test_as_tree(self):
365
tree = self.get_as_tree('0')
366
self.assertEquals(_mod_revision.NULL_REVISION, tree.get_revision_id())
367
tree = self.get_as_tree('1')
368
self.assertEquals('r1', tree.get_revision_id())
369
tree = self.get_as_tree('2')
370
self.assertEquals('r2', tree.get_revision_id())
371
tree = self.get_as_tree('-2')
372
self.assertEquals('r1', tree.get_revision_id())
373
tree = self.get_as_tree('-1')
374
self.assertEquals('r2', tree.get_revision_id())
375
tree = self.get_as_tree('1.1.1')
376
self.assertEquals('alt_r2', tree.get_revision_id())
379
class TestRevisionSpec_revid(TestRevisionSpec):
381
def test_in_history(self):
382
# We should be able to access revisions that are directly
384
self.assertInHistoryIs(1, 'r1', 'revid:r1')
385
self.assertInHistoryIs(2, 'r2', 'revid:r2')
387
def test_missing(self):
388
self.assertInvalid('revid:r3', invalid_as_revision_id=False)
390
def test_merged(self):
391
"""We can reach revisions in the ancestry"""
392
self.assertInHistoryIs(None, 'alt_r2', 'revid:alt_r2')
394
def test_not_here(self):
395
self.tree2.commit('alt third', rev_id='alt_r3')
396
# It exists in tree2, but not in tree
397
self.assertInvalid('revid:alt_r3', invalid_as_revision_id=False)
399
def test_in_repository(self):
400
"""We can get any revision id in the repository"""
401
# XXX: This may change in the future, but for now, it is true
402
self.tree2.commit('alt third', rev_id='alt_r3')
403
self.tree.branch.fetch(self.tree2.branch, 'alt_r3')
404
self.assertInHistoryIs(None, 'alt_r3', 'revid:alt_r3')
406
def test_unicode(self):
407
"""We correctly convert a unicode ui string to an encoded revid."""
408
revision_id = u'\N{SNOWMAN}'.encode('utf-8')
409
self.tree.commit('unicode', rev_id=revision_id)
410
self.assertInHistoryIs(3, revision_id, u'revid:\N{SNOWMAN}')
411
self.assertInHistoryIs(3, revision_id, 'revid:' + revision_id)
413
def test_as_revision_id(self):
414
self.assertAsRevisionId('r1', 'revid:r1')
415
self.assertAsRevisionId('r2', 'revid:r2')
416
self.assertAsRevisionId('alt_r2', 'revid:alt_r2')
419
class TestRevisionSpec_last(TestRevisionSpec):
421
def test_positive(self):
422
self.assertInHistoryIs(2, 'r2', 'last:1')
423
self.assertInHistoryIs(1, 'r1', 'last:2')
424
self.assertInHistoryIs(0, 'null:', 'last:3')
426
def test_empty(self):
427
self.assertInHistoryIs(2, 'r2', 'last:')
429
def test_negative(self):
430
self.assertInvalid('last:-1',
431
extra='\nyou must supply a positive value')
433
def test_missing(self):
434
self.assertInvalid('last:4')
436
def test_no_history(self):
437
tree = self.make_branch_and_tree('tree3')
439
self.assertRaises(errors.NoCommits,
440
spec_in_history, 'last:', tree.branch)
442
def test_not_a_number(self):
445
except ValueError, e:
447
self.assertInvalid('last:Y', extra='\n' + str(e))
449
def test_as_revision_id(self):
450
self.assertAsRevisionId('r2', 'last:1')
451
self.assertAsRevisionId('r1', 'last:2')
454
class TestRevisionSpec_before(TestRevisionSpec):
457
self.assertInHistoryIs(1, 'r1', 'before:2')
458
self.assertInHistoryIs(1, 'r1', 'before:-1')
460
def test_before_one(self):
461
self.assertInHistoryIs(0, 'null:', 'before:1')
463
def test_before_none(self):
464
self.assertInvalid('before:0',
465
extra='\ncannot go before the null: revision')
467
def test_revid(self):
468
self.assertInHistoryIs(1, 'r1', 'before:revid:r2')
471
self.assertInHistoryIs(1, 'r1', 'before:last:1')
473
def test_alt_revid(self):
474
# This will grab the left-most ancestor for alternate histories
475
self.assertInHistoryIs(1, 'r1', 'before:revid:alt_r2')
477
def test_alt_no_parents(self):
478
new_tree = self.make_branch_and_tree('new_tree')
479
new_tree.commit('first', rev_id='new_r1')
480
self.tree.branch.fetch(new_tree.branch, 'new_r1')
481
self.assertInHistoryIs(0, 'null:', 'before:revid:new_r1')
483
def test_as_revision_id(self):
484
self.assertAsRevisionId('r1', 'before:revid:r2')
485
self.assertAsRevisionId('r1', 'before:2')
486
self.assertAsRevisionId('r1', 'before:1.1.1')
487
self.assertAsRevisionId('r1', 'before:revid:alt_r2')
490
class TestRevisionSpec_tag(TestRevisionSpec):
492
def make_branch_and_tree(self, relpath):
493
# override format as the default one may not support tags
494
return TestRevisionSpec.make_branch_and_tree(
495
self, relpath, format='dirstate-tags')
497
def test_from_string_tag(self):
498
spec = RevisionSpec.from_string('tag:bzr-0.14')
499
self.assertIsInstance(spec, RevisionSpec_tag)
500
self.assertEqual(spec.spec, 'bzr-0.14')
502
def test_lookup_tag(self):
503
self.tree.branch.tags.set_tag('bzr-0.14', 'r1')
504
self.assertInHistoryIs(1, 'r1', 'tag:bzr-0.14')
505
self.tree.branch.tags.set_tag('null_rev', 'null:')
506
self.assertInHistoryIs(0, 'null:', 'tag:null_rev')
508
def test_failed_lookup(self):
509
# tags that don't exist give a specific message: arguably we should
510
# just give InvalidRevisionSpec but I think this is more helpful
511
self.assertRaises(errors.NoSuchTag,
513
'tag:some-random-tag')
515
def test_as_revision_id(self):
516
self.tree.branch.tags.set_tag('my-tag', 'r2')
517
self.tree.branch.tags.set_tag('null_rev', 'null:')
518
self.assertAsRevisionId('r2', 'tag:my-tag')
519
self.assertAsRevisionId('null:', 'tag:null_rev')
520
self.assertAsRevisionId('r1', 'before:tag:my-tag')
523
class TestRevisionSpec_date(TestRevisionSpec):
526
super(TestRevisionSpec, self).setUp()
528
new_tree = self.make_branch_and_tree('new_tree')
529
new_tree.commit('Commit one', rev_id='new_r1',
530
timestamp=time.time() - 60*60*24)
531
new_tree.commit('Commit two', rev_id='new_r2')
532
new_tree.commit('Commit three', rev_id='new_r3')
536
def test_tomorrow(self):
537
self.assertInvalid('date:tomorrow')
539
def test_today(self):
540
self.assertInHistoryIs(2, 'new_r2', 'date:today')
541
self.assertInHistoryIs(1, 'new_r1', 'before:date:today')
543
def test_yesterday(self):
544
self.assertInHistoryIs(1, 'new_r1', 'date:yesterday')
546
def test_invalid(self):
547
self.assertInvalid('date:foobar', extra='\ninvalid date')
548
# You must have '-' between year/month/day
549
self.assertInvalid('date:20040404', extra='\ninvalid date')
550
# Need 2 digits for each date piece
551
self.assertInvalid('date:2004-4-4', extra='\ninvalid date')
554
now = datetime.datetime.now()
555
self.assertInHistoryIs(2, 'new_r2',
556
'date:%04d-%02d-%02d' % (now.year, now.month, now.day))
558
def test_as_revision_id(self):
559
self.assertAsRevisionId('new_r2', 'date:today')
562
class TestRevisionSpec_ancestor(TestRevisionSpec):
564
def test_non_exact_branch(self):
565
# It seems better to require an exact path to the branch
566
# Branch.open() rather than using Branch.open_containing()
567
self.assertRaises(errors.NotBranchError,
568
self.get_in_history, 'ancestor:tree2/a')
570
def test_simple(self):
571
# Common ancestor of trees is 'alt_r2'
572
self.assertInHistoryIs(None, 'alt_r2', 'ancestor:tree2')
574
# Going the other way, we get a valid revno
576
self.tree = self.tree2
578
self.assertInHistoryIs(2, 'alt_r2', 'ancestor:tree')
581
self.assertInHistoryIs(2, 'r2', 'ancestor:tree')
583
def test_unrelated(self):
584
new_tree = self.make_branch_and_tree('new_tree')
586
new_tree.commit('Commit one', rev_id='new_r1')
587
new_tree.commit('Commit two', rev_id='new_r2')
588
new_tree.commit('Commit three', rev_id='new_r3')
590
# With no common ancestor, we should raise another user error
591
self.assertRaises(errors.NoCommonAncestor,
592
self.get_in_history, 'ancestor:new_tree')
594
def test_no_commits(self):
595
new_tree = self.make_branch_and_tree('new_tree')
596
self.assertRaises(errors.NoCommits,
597
spec_in_history, 'ancestor:new_tree',
600
self.assertRaises(errors.NoCommits,
601
spec_in_history, 'ancestor:tree',
604
def test_as_revision_id(self):
605
self.assertAsRevisionId('alt_r2', 'ancestor:tree2')
607
def test_default(self):
608
# We don't have a parent to default to
609
self.assertRaises(errors.NotBranchError, self.get_in_history,
612
# Create a branch with a parent to default to
613
tree3 = self.tree.bzrdir.sprout('tree3').open_workingtree()
614
tree3.commit('foo', rev_id='r3')
616
self.assertInHistoryIs(2, 'r2', 'ancestor:')
619
class TestRevisionSpec_branch(TestRevisionSpec):
621
def test_non_exact_branch(self):
622
# It seems better to require an exact path to the branch
623
# Branch.open() rather than using Branch.open_containing()
624
self.assertRaises(errors.NotBranchError,
625
self.get_in_history, 'branch:tree2/a')
627
def test_simple(self):
628
self.assertInHistoryIs(None, 'alt_r2', 'branch:tree2')
631
self.assertInHistoryIs(2, 'r2', 'branch:tree')
633
def test_unrelated(self):
634
new_tree = self.make_branch_and_tree('new_tree')
636
new_tree.commit('Commit one', rev_id='new_r1')
637
new_tree.commit('Commit two', rev_id='new_r2')
638
new_tree.commit('Commit three', rev_id='new_r3')
640
self.assertInHistoryIs(None, 'new_r3', 'branch:new_tree')
642
# XXX: Right now, we use fetch() to make sure the remote revisions
643
# have been pulled into the local branch. We may change that
644
# behavior in the future.
645
self.assertTrue(self.tree.branch.repository.has_revision('new_r3'))
647
def test_no_commits(self):
648
new_tree = self.make_branch_and_tree('new_tree')
649
self.assertRaises(errors.NoCommits,
650
self.get_in_history, 'branch:new_tree')
651
self.assertRaises(errors.NoCommits,
652
self.get_as_tree, 'branch:new_tree')
654
def test_as_revision_id(self):
655
self.assertAsRevisionId('alt_r2', 'branch:tree2')
657
def test_as_tree(self):
658
tree = self.get_as_tree('branch:tree', self.tree2)
659
self.assertEquals('r2', tree.get_revision_id())
660
self.assertFalse(self.tree2.branch.repository.has_revision('r2'))
663
class TestRevisionSpec_submit(TestRevisionSpec):
665
def test_submit_branch(self):
666
# Common ancestor of trees is 'alt_r2'
667
self.assertRaises(errors.NoSubmitBranch, self.get_in_history,
669
self.tree.branch.set_parent('../tree2')
670
self.assertInHistoryIs(None, 'alt_r2', 'submit:')
671
self.tree.branch.set_parent('bogus')
672
self.assertRaises(errors.NotBranchError, self.get_in_history,
674
# submit branch overrides parent branch
675
self.tree.branch.set_submit_branch('tree2')
676
self.assertInHistoryIs(None, 'alt_r2', 'submit:')
678
def test_as_revision_id(self):
679
self.tree.branch.set_submit_branch('tree2')
680
self.assertAsRevisionId('alt_r2', 'branch:tree2')
683
class TestRevisionSpec_mainline(TestRevisionSpec):
685
def test_as_revision_id(self):
686
self.assertAsRevisionId('r1', 'mainline:1')
687
self.assertAsRevisionId('r2', 'mainline:1.1.1')
688
self.assertAsRevisionId('r2', 'mainline:revid:alt_r2')
689
spec = RevisionSpec.from_string('mainline:revid:alt_r22')
690
e = self.assertRaises(errors.InvalidRevisionSpec,
691
spec.as_revision_id, self.tree.branch)
692
self.assertContainsRe(str(e),
693
"Requested revision: 'mainline:revid:alt_r22' does not exist in"
696
def test_in_history(self):
697
self.assertInHistoryIs(2, 'r2', 'mainline:revid:alt_r2')
700
class TestRevisionSpec_annotate(TestRevisionSpec):
703
super(TestRevisionSpec_annotate, self).setUp()
704
self.tree = self.make_branch_and_tree('annotate-tree')
705
self.build_tree_contents([('annotate-tree/file1', '1\n')])
706
self.tree.add('file1')
707
self.tree.commit('r1', rev_id='r1')
708
self.build_tree_contents([('annotate-tree/file1', '2\n1\n')])
709
self.tree.commit('r2', rev_id='r2')
710
self.build_tree_contents([('annotate-tree/file1', '2\n1\n3\n')])
712
def test_as_revision_id_r1(self):
713
self.assertAsRevisionId('r1', 'annotate:annotate-tree/file1:2')
715
def test_as_revision_id_r2(self):
716
self.assertAsRevisionId('r2', 'annotate:annotate-tree/file1:1')
718
def test_as_revision_id_uncommitted(self):
719
spec = RevisionSpec.from_string('annotate:annotate-tree/file1:3')
720
e = self.assertRaises(errors.InvalidRevisionSpec,
721
spec.as_revision_id, self.tree.branch)
722
self.assertContainsRe(str(e),
723
r"Requested revision: \'annotate:annotate-tree/file1:3\' does not"
724
" exist in branch: .*\nLine 3 has not been committed.")
726
def test_non_existent_line(self):
727
spec = RevisionSpec.from_string('annotate:annotate-tree/file1:4')
728
e = self.assertRaises(errors.InvalidRevisionSpec,
729
spec.as_revision_id, self.tree.branch)
730
self.assertContainsRe(str(e),
731
r"Requested revision: \'annotate:annotate-tree/file1:4\' does not"
732
" exist in branch: .*\nNo such line: 4")
734
def test_invalid_line(self):
735
spec = RevisionSpec.from_string('annotate:annotate-tree/file1:q')
736
e = self.assertRaises(errors.InvalidRevisionSpec,
737
spec.as_revision_id, self.tree.branch)
738
self.assertContainsRe(str(e),
739
r"Requested revision: \'annotate:annotate-tree/file1:q\' does not"
740
" exist in branch: .*\nNo such line: q")
742
def test_no_such_file(self):
743
spec = RevisionSpec.from_string('annotate:annotate-tree/file2:1')
744
e = self.assertRaises(errors.InvalidRevisionSpec,
745
spec.as_revision_id, self.tree.branch)
746
self.assertContainsRe(str(e),
747
r"Requested revision: \'annotate:annotate-tree/file2:1\' does not"
748
" exist in branch: .*\nFile 'file2' is not versioned")
750
def test_no_such_file_with_colon(self):
751
spec = RevisionSpec.from_string('annotate:annotate-tree/fi:le2:1')
752
e = self.assertRaises(errors.InvalidRevisionSpec,
753
spec.as_revision_id, self.tree.branch)
754
self.assertContainsRe(str(e),
755
r"Requested revision: \'annotate:annotate-tree/fi:le2:1\' does not"
756
" exist in branch: .*\nFile 'fi:le2' is not versioned")