1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
1
# Copyright (C) 2004, 2005 by 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
revision as _mod_revision,
24
from breezy.tests import TestCaseWithTransport
25
from breezy.revisionspec import (
33
def spec_in_history(spec, branch):
34
"""A simple helper to change a revision spec into a branch search"""
35
return RevisionSpec.from_string(spec).in_history(branch)
38
# Basic class, which just creates a really basic set of revisions
39
class TestRevisionSpec(TestCaseWithTransport):
42
super(TestRevisionSpec, self).setUp()
43
# this sets up a revision graph:
48
self.tree = self.make_branch_and_tree('tree')
49
self.build_tree(['tree/a'])
50
self.tree.lock_write()
51
self.addCleanup(self.tree.unlock)
53
self.tree.commit('a', rev_id=b'r1')
55
self.tree2 = self.tree.controldir.sprout('tree2').open_workingtree()
56
self.tree2.commit('alt', rev_id=b'alt_r2')
58
self.tree.merge_from_branch(self.tree2.branch)
59
self.tree.commit('second', rev_id=b'r2')
61
def get_in_history(self, revision_spec):
62
return spec_in_history(revision_spec, self.tree.branch)
64
def assertInHistoryIs(self, exp_revno, exp_revision_id, revision_spec):
65
rev_info = self.get_in_history(revision_spec)
66
self.assertEqual(exp_revno, rev_info.revno,
67
'Revision spec: %r returned wrong revno: %r != %r'
68
% (revision_spec, exp_revno, rev_info.revno))
69
self.assertEqual(exp_revision_id, rev_info.rev_id,
70
'Revision spec: %r returned wrong revision id:'
72
% (revision_spec, exp_revision_id, rev_info.rev_id))
74
def assertInvalid(self, revision_spec, extra='',
75
invalid_as_revision_id=True):
77
self.get_in_history(revision_spec)
78
except errors.InvalidRevisionSpec as e:
79
self.assertEqual(revision_spec, e.spec)
80
self.assertEqual(extra, e.extra)
82
self.fail('Expected InvalidRevisionSpec to be raised for'
83
' %r.in_history' % (revision_spec,))
84
if invalid_as_revision_id:
86
spec = RevisionSpec.from_string(revision_spec)
87
spec.as_revision_id(self.tree.branch)
88
except errors.InvalidRevisionSpec as e:
89
self.assertEqual(revision_spec, e.spec)
90
self.assertEqual(extra, e.extra)
92
self.fail('Expected InvalidRevisionSpec to be raised for'
93
' %r.as_revision_id' % (revision_spec,))
95
def assertAsRevisionId(self, revision_id, revision_spec):
96
"""Calling as_revision_id() should return the specified id."""
97
spec = RevisionSpec.from_string(revision_spec)
98
self.assertEqual(revision_id,
99
spec.as_revision_id(self.tree.branch))
101
def get_as_tree(self, revision_spec, tree=None):
104
spec = RevisionSpec.from_string(revision_spec)
105
return spec.as_tree(tree.branch)
108
class RevisionSpecMatchOnTrap(RevisionSpec):
110
def _match_on(self, branch, revs):
111
self.last_call = (branch, revs)
112
return super(RevisionSpecMatchOnTrap, self)._match_on(branch, revs)
115
class TestRevisionSpecBase(TestRevisionSpec):
117
def test_wants_no_revision_history(self):
118
# If wants_revision_history = False, then _match_on should get None for
119
# the branch revision history
120
spec = RevisionSpecMatchOnTrap('foo', _internal=True)
121
spec.in_history(self.tree.branch)
123
self.assertEqual((self.tree.branch, None), spec.last_call)
126
class TestOddRevisionSpec(TestRevisionSpec):
127
"""Test things that aren't normally thought of as revision specs"""
130
self.assertInHistoryIs(None, None, None)
132
def test_object(self):
133
self.assertRaises(TypeError, RevisionSpec.from_string, object())
136
class RevisionSpec_bork(RevisionSpec):
138
prefix = 'irrelevant:'
140
def _match_on(self, branch, revs):
141
if self.spec == "bork":
142
return RevisionInfo.from_revision_id(branch, b"r1")
144
raise errors.InvalidRevisionSpec(self.spec, branch)
147
class TestRevisionSpec_dwim(TestRevisionSpec):
149
# Don't need to test revno's explicitly since TRS_revno already
150
# covers that well for us
151
def test_dwim_spec_revno(self):
152
self.assertInHistoryIs(2, b'r2', '2')
153
self.assertAsRevisionId(b'alt_r2', '1.1.1')
155
def test_dwim_spec_revid(self):
156
self.assertInHistoryIs(2, b'r2', 'r2')
158
def test_dwim_spec_tag(self):
159
self.tree.branch.tags.set_tag('footag', b'r1')
160
self.assertAsRevisionId(b'r1', 'footag')
161
self.tree.branch.tags.delete_tag('footag')
162
self.assertRaises(errors.InvalidRevisionSpec,
163
self.get_in_history, 'footag')
165
def test_dwim_spec_tag_that_looks_like_revno(self):
166
# Test that we slip past revno with things that look like revnos,
167
# but aren't. Tags are convenient for testing this since we can
168
# make them look however we want.
169
self.tree.branch.tags.set_tag('3', b'r2')
170
self.assertAsRevisionId(b'r2', '3')
171
self.build_tree(['tree/b'])
173
self.tree.commit('b', rev_id=b'r3')
174
self.assertAsRevisionId(b'r3', '3')
176
def test_dwim_spec_date(self):
177
self.assertAsRevisionId(b'r1', 'today')
179
def test_dwim_spec_branch(self):
180
self.assertInHistoryIs(None, b'alt_r2', 'tree2')
182
def test_dwim_spec_nonexistent(self):
183
self.assertInvalid('somethingrandom', invalid_as_revision_id=False)
184
self.assertInvalid('-1.1', invalid_as_revision_id=False)
185
self.assertInvalid('.1', invalid_as_revision_id=False)
186
self.assertInvalid('1..1', invalid_as_revision_id=False)
187
self.assertInvalid('1.2..1', invalid_as_revision_id=False)
188
self.assertInvalid('1.', invalid_as_revision_id=False)
190
def test_append_dwim_revspec(self):
191
original_dwim_revspecs = list(RevisionSpec_dwim._possible_revspecs)
193
def reset_dwim_revspecs():
194
RevisionSpec_dwim._possible_revspecs = original_dwim_revspecs
195
self.addCleanup(reset_dwim_revspecs)
196
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_bork)
197
self.assertAsRevisionId(b'r1', 'bork')
199
def test_append_lazy_dwim_revspec(self):
200
original_dwim_revspecs = list(RevisionSpec_dwim._possible_revspecs)
202
def reset_dwim_revspecs():
203
RevisionSpec_dwim._possible_revspecs = original_dwim_revspecs
204
self.addCleanup(reset_dwim_revspecs)
205
RevisionSpec_dwim.append_possible_lazy_revspec(
206
"breezy.tests.test_revisionspec", "RevisionSpec_bork")
207
self.assertAsRevisionId(b'r1', 'bork')
210
class TestRevisionSpec_revno(TestRevisionSpec):
212
def test_positive_int(self):
213
self.assertInHistoryIs(0, b'null:', '0')
214
self.assertInHistoryIs(1, b'r1', '1')
215
self.assertInHistoryIs(2, b'r2', '2')
216
self.assertInvalid('3')
218
def test_dotted_decimal(self):
219
self.assertInHistoryIs(None, b'alt_r2', '1.1.1')
220
self.assertInvalid('1.1.123')
222
def test_negative_int(self):
223
self.assertInHistoryIs(2, b'r2', '-1')
224
self.assertInHistoryIs(1, b'r1', '-2')
226
self.assertInHistoryIs(1, b'r1', '-3')
227
self.assertInHistoryIs(1, b'r1', '-4')
228
self.assertInHistoryIs(1, b'r1', '-100')
230
def test_positive(self):
231
self.assertInHistoryIs(0, b'null:', 'revno:0')
232
self.assertInHistoryIs(1, b'r1', 'revno:1')
233
self.assertInHistoryIs(2, b'r2', 'revno:2')
235
self.assertInvalid('revno:3')
237
def test_negative(self):
238
self.assertInHistoryIs(2, b'r2', 'revno:-1')
239
self.assertInHistoryIs(1, b'r1', 'revno:-2')
241
self.assertInHistoryIs(1, b'r1', 'revno:-3')
242
self.assertInHistoryIs(1, b'r1', 'revno:-4')
244
def test_invalid_number(self):
245
# Get the right exception text
248
except ValueError as e:
249
self.assertInvalid('revno:X', extra='\n' + str(e))
253
def test_missing_number_and_branch(self):
254
self.assertInvalid('revno::',
255
extra='\ncannot have an empty revno and no branch')
257
def test_invalid_number_with_branch(self):
260
except ValueError as e:
261
self.assertInvalid('revno:X:tree2', extra='\n' + str(e))
265
def test_non_exact_branch(self):
266
# It seems better to require an exact path to the branch
267
# Branch.open() rather than using Branch.open_containing()
268
spec = RevisionSpec.from_string('revno:2:tree2/a')
269
self.assertRaises(errors.NotBranchError,
270
spec.in_history, self.tree.branch)
272
def test_with_branch(self):
273
# Passing a URL overrides the supplied branch path
274
revinfo = self.get_in_history('revno:2:tree2')
275
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
276
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
277
self.assertEqual(2, revinfo.revno)
278
self.assertEqual(b'alt_r2', revinfo.rev_id)
280
def test_int_with_branch(self):
281
revinfo = self.get_in_history('2:tree2')
282
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
283
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
284
self.assertEqual(2, revinfo.revno)
285
self.assertEqual(b'alt_r2', revinfo.rev_id)
287
def test_with_url(self):
288
url = self.get_url() + '/tree2'
289
revinfo = self.get_in_history('revno:2:%s' % (url,))
290
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
291
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
292
self.assertEqual(2, revinfo.revno)
293
self.assertEqual(b'alt_r2', revinfo.rev_id)
295
def test_negative_with_url(self):
296
url = self.get_url() + '/tree2'
297
revinfo = self.get_in_history('revno:-1:%s' % (url,))
298
self.assertNotEqual(self.tree.branch.base, revinfo.branch.base)
299
self.assertEqual(self.tree2.branch.base, revinfo.branch.base)
300
self.assertEqual(2, revinfo.revno)
301
self.assertEqual(b'alt_r2', revinfo.rev_id)
303
def test_different_history_lengths(self):
304
# Make sure we use the revisions and offsets in the supplied branch
305
# not the ones in the original branch.
306
self.tree2.commit('three', rev_id=b'r3')
307
self.assertInHistoryIs(3, b'r3', 'revno:3:tree2')
308
self.assertInHistoryIs(3, b'r3', 'revno:-1:tree2')
310
def test_invalid_branch(self):
311
self.assertRaises(errors.NotBranchError,
312
self.get_in_history, 'revno:-1:tree3')
314
def test_invalid_revno_in_branch(self):
315
self.tree.commit('three', rev_id=b'r3')
316
self.assertInvalid('revno:3:tree2')
318
def test_revno_n_path(self):
319
"""Old revno:N:path tests"""
320
wta = self.make_branch_and_tree('a')
323
wta.commit('Commit one', rev_id=b'a@r-0-1')
324
wta.commit('Commit two', rev_id=b'a@r-0-2')
325
wta.commit('Commit three', rev_id=b'a@r-0-3')
327
wtb = self.make_branch_and_tree('b')
330
wtb.commit('Commit one', rev_id=b'b@r-0-1')
331
wtb.commit('Commit two', rev_id=b'b@r-0-2')
332
wtb.commit('Commit three', rev_id=b'b@r-0-3')
334
self.assertEqual((1, b'a@r-0-1'),
335
spec_in_history('revno:1:a/', ba))
336
# The argument of in_history should be ignored since it is
337
# redundant with the path in the spec.
338
self.assertEqual((1, b'a@r-0-1'),
339
spec_in_history('revno:1:a/', None))
340
self.assertEqual((1, b'a@r-0-1'),
341
spec_in_history('revno:1:a/', bb))
342
self.assertEqual((2, b'b@r-0-2'),
343
spec_in_history('revno:2:b/', None))
345
def test_as_revision_id(self):
346
self.assertAsRevisionId(b'null:', '0')
347
self.assertAsRevisionId(b'r1', '1')
348
self.assertAsRevisionId(b'r2', '2')
349
self.assertAsRevisionId(b'r1', '-2')
350
self.assertAsRevisionId(b'r2', '-1')
351
self.assertAsRevisionId(b'alt_r2', '1.1.1')
353
def test_as_tree(self):
354
tree = self.get_as_tree('0')
355
self.assertEqual(_mod_revision.NULL_REVISION, tree.get_revision_id())
356
tree = self.get_as_tree('1')
357
self.assertEqual(b'r1', tree.get_revision_id())
358
tree = self.get_as_tree('2')
359
self.assertEqual(b'r2', tree.get_revision_id())
360
tree = self.get_as_tree('-2')
361
self.assertEqual(b'r1', tree.get_revision_id())
362
tree = self.get_as_tree('-1')
363
self.assertEqual(b'r2', tree.get_revision_id())
364
tree = self.get_as_tree('1.1.1')
365
self.assertEqual(b'alt_r2', tree.get_revision_id())
368
class TestRevisionSpec_revid(TestRevisionSpec):
370
def test_in_history(self):
371
# We should be able to access revisions that are directly
373
self.assertInHistoryIs(1, b'r1', 'revid:r1')
374
self.assertInHistoryIs(2, b'r2', 'revid:r2')
376
def test_missing(self):
377
self.assertInvalid('revid:r3', invalid_as_revision_id=False)
379
def test_merged(self):
380
"""We can reach revisions in the ancestry"""
381
self.assertInHistoryIs(None, b'alt_r2', 'revid:alt_r2')
383
def test_not_here(self):
384
self.tree2.commit('alt third', rev_id=b'alt_r3')
385
# It exists in tree2, but not in tree
386
self.assertInvalid('revid:alt_r3', invalid_as_revision_id=False)
388
def test_in_repository(self):
389
"""We can get any revision id in the repository"""
390
# XXX: This may change in the future, but for now, it is true
391
self.tree2.commit('alt third', rev_id=b'alt_r3')
392
self.tree.branch.fetch(self.tree2.branch, b'alt_r3')
393
self.assertInHistoryIs(None, b'alt_r3', 'revid:alt_r3')
395
def test_unicode(self):
396
"""We correctly convert a unicode ui string to an encoded revid."""
397
revision_id = u'\N{SNOWMAN}'.encode('utf-8')
398
self.tree.commit('unicode', rev_id=revision_id)
399
self.assertInHistoryIs(3, revision_id, u'revid:\N{SNOWMAN}')
400
self.assertInHistoryIs(3, revision_id, 'revid:' +
401
revision_id.decode('utf-8'))
403
def test_as_revision_id(self):
404
self.assertAsRevisionId(b'r1', 'revid:r1')
405
self.assertAsRevisionId(b'r2', 'revid:r2')
406
self.assertAsRevisionId(b'alt_r2', 'revid:alt_r2')
409
class TestRevisionSpec_last(TestRevisionSpec):
411
def test_positive(self):
412
self.assertInHistoryIs(2, b'r2', 'last:1')
413
self.assertInHistoryIs(1, b'r1', 'last:2')
414
self.assertInHistoryIs(0, b'null:', 'last:3')
416
def test_empty(self):
417
self.assertInHistoryIs(2, b'r2', 'last:')
419
def test_negative(self):
420
self.assertInvalid('last:-1',
421
extra='\nyou must supply a positive value')
423
def test_missing(self):
424
self.assertInvalid('last:4')
426
def test_no_history(self):
427
tree = self.make_branch_and_tree('tree3')
429
self.assertRaises(errors.NoCommits,
430
spec_in_history, 'last:', tree.branch)
432
def test_not_a_number(self):
436
except ValueError as e:
438
self.assertInvalid('last:Y', extra='\n' + str(last_e))
440
def test_as_revision_id(self):
441
self.assertAsRevisionId(b'r2', 'last:1')
442
self.assertAsRevisionId(b'r1', 'last:2')
445
class TestRevisionSpec_before(TestRevisionSpec):
448
self.assertInHistoryIs(1, b'r1', 'before:2')
449
self.assertInHistoryIs(1, b'r1', 'before:-1')
451
def test_before_one(self):
452
self.assertInHistoryIs(0, b'null:', 'before:1')
454
def test_before_none(self):
455
self.assertInvalid('before:0',
456
extra='\ncannot go before the null: revision')
458
def test_revid(self):
459
self.assertInHistoryIs(1, b'r1', 'before:revid:r2')
462
self.assertInHistoryIs(1, b'r1', 'before:last:1')
464
def test_alt_revid(self):
465
# This will grab the left-most ancestor for alternate histories
466
self.assertInHistoryIs(1, b'r1', 'before:revid:alt_r2')
468
def test_alt_no_parents(self):
469
new_tree = self.make_branch_and_tree('new_tree')
470
new_tree.commit('first', rev_id=b'new_r1')
471
self.tree.branch.fetch(new_tree.branch, b'new_r1')
472
self.assertInHistoryIs(0, b'null:', 'before:revid:new_r1')
474
def test_as_revision_id(self):
475
self.assertAsRevisionId(b'r1', 'before:revid:r2')
476
self.assertAsRevisionId(b'r1', 'before:2')
477
self.assertAsRevisionId(b'r1', 'before:1.1.1')
478
self.assertAsRevisionId(b'r1', 'before:revid:alt_r2')
481
class TestRevisionSpec_tag(TestRevisionSpec):
483
def make_branch_and_tree(self, relpath):
484
# override format as the default one may not support tags
485
return TestRevisionSpec.make_branch_and_tree(
486
self, relpath, format='dirstate-tags')
488
def test_from_string_tag(self):
489
spec = RevisionSpec.from_string('tag:bzr-0.14')
490
self.assertIsInstance(spec, RevisionSpec_tag)
491
self.assertEqual(spec.spec, 'bzr-0.14')
493
def test_lookup_tag(self):
494
self.tree.branch.tags.set_tag('bzr-0.14', b'r1')
495
self.assertInHistoryIs(1, b'r1', 'tag:bzr-0.14')
496
self.tree.branch.tags.set_tag('null_rev', b'null:')
497
self.assertInHistoryIs(0, b'null:', 'tag:null_rev')
499
def test_failed_lookup(self):
500
# tags that don't exist give a specific message: arguably we should
501
# just give InvalidRevisionSpec but I think this is more helpful
502
self.assertRaises(errors.NoSuchTag,
504
'tag:some-random-tag')
506
def test_as_revision_id(self):
507
self.tree.branch.tags.set_tag('my-tag', b'r2')
508
self.tree.branch.tags.set_tag('null_rev', b'null:')
509
self.assertAsRevisionId(b'r2', 'tag:my-tag')
510
self.assertAsRevisionId(b'null:', 'tag:null_rev')
511
self.assertAsRevisionId(b'r1', 'before:tag:my-tag')
514
class TestRevisionSpec_date(TestRevisionSpec):
517
super(TestRevisionSpec, self).setUp()
519
new_tree = self.make_branch_and_tree('new_tree')
520
new_tree.commit('Commit one', rev_id=b'new_r1',
521
timestamp=time.time() - 60 * 60 * 24)
522
new_tree.commit('Commit two', rev_id=b'new_r2')
523
new_tree.commit('Commit three', rev_id=b'new_r3')
527
def test_tomorrow(self):
528
self.assertInvalid('date:tomorrow')
530
def test_today(self):
531
self.assertInHistoryIs(2, b'new_r2', 'date:today')
532
self.assertInHistoryIs(1, b'new_r1', 'before:date:today')
534
def test_yesterday(self):
535
self.assertInHistoryIs(1, b'new_r1', 'date:yesterday')
537
def test_invalid(self):
538
self.assertInvalid('date:foobar', extra='\ninvalid date')
539
# You must have '-' between year/month/day
540
self.assertInvalid('date:20040404', extra='\ninvalid date')
541
# Need 2 digits for each date piece
542
self.assertInvalid('date:2004-4-4', extra='\ninvalid date')
545
now = datetime.datetime.now()
546
self.assertInHistoryIs(2, b'new_r2',
547
'date:%04d-%02d-%02d' % (now.year, now.month, now.day))
549
def test_as_revision_id(self):
550
self.assertAsRevisionId(b'new_r2', 'date:today')
553
class TestRevisionSpec_ancestor(TestRevisionSpec):
555
def test_non_exact_branch(self):
556
# It seems better to require an exact path to the branch
557
# Branch.open() rather than using Branch.open_containing()
558
self.assertRaises(errors.NotBranchError,
559
self.get_in_history, 'ancestor:tree2/a')
561
def test_simple(self):
562
# Common ancestor of trees is 'alt_r2'
563
self.assertInHistoryIs(None, b'alt_r2', 'ancestor:tree2')
565
# Going the other way, we get a valid revno
567
self.tree = self.tree2
569
self.assertInHistoryIs(2, b'alt_r2', 'ancestor:tree')
572
self.assertInHistoryIs(2, b'r2', 'ancestor:tree')
574
def test_unrelated(self):
575
new_tree = self.make_branch_and_tree('new_tree')
577
new_tree.commit('Commit one', rev_id=b'new_r1')
578
new_tree.commit('Commit two', rev_id=b'new_r2')
579
new_tree.commit('Commit three', rev_id=b'new_r3')
581
# With no common ancestor, we should raise another user error
582
self.assertRaises(errors.NoCommonAncestor,
583
self.get_in_history, 'ancestor:new_tree')
585
def test_no_commits(self):
586
new_tree = self.make_branch_and_tree('new_tree')
587
self.assertRaises(errors.NoCommits,
588
spec_in_history, 'ancestor:new_tree',
591
self.assertRaises(errors.NoCommits,
592
spec_in_history, 'ancestor:tree',
595
def test_as_revision_id(self):
596
self.assertAsRevisionId(b'alt_r2', 'ancestor:tree2')
598
def test_default(self):
599
# We don't have a parent to default to
600
self.assertRaises(errors.NotBranchError, self.get_in_history,
603
# Create a branch with a parent to default to
604
tree3 = self.tree.controldir.sprout('tree3').open_workingtree()
605
tree3.commit('foo', rev_id=b'r3')
607
self.assertInHistoryIs(2, b'r2', 'ancestor:')
610
class TestRevisionSpec_branch(TestRevisionSpec):
612
def test_non_exact_branch(self):
613
# It seems better to require an exact path to the branch
614
# Branch.open() rather than using Branch.open_containing()
615
self.assertRaises(errors.NotBranchError,
616
self.get_in_history, 'branch:tree2/a')
618
def test_simple(self):
619
self.assertInHistoryIs(None, b'alt_r2', 'branch:tree2')
622
self.assertInHistoryIs(2, b'r2', 'branch:tree')
624
def test_unrelated(self):
625
new_tree = self.make_branch_and_tree('new_tree')
627
new_tree.commit('Commit one', rev_id=b'new_r1')
628
new_tree.commit('Commit two', rev_id=b'new_r2')
629
new_tree.commit('Commit three', rev_id=b'new_r3')
631
self.assertInHistoryIs(None, b'new_r3', 'branch:new_tree')
633
# XXX: Right now, we use fetch() to make sure the remote revisions
634
# have been pulled into the local branch. We may change that
635
# behavior in the future.
636
self.assertTrue(self.tree.branch.repository.has_revision(b'new_r3'))
638
def test_no_commits(self):
639
self.make_branch_and_tree('new_tree')
640
self.assertRaises(errors.NoCommits,
641
self.get_in_history, 'branch:new_tree')
642
self.assertRaises(errors.NoCommits,
643
self.get_as_tree, 'branch:new_tree')
645
def test_as_revision_id(self):
646
self.assertAsRevisionId(b'alt_r2', 'branch:tree2')
648
def test_as_tree(self):
649
tree = self.get_as_tree('branch:tree', self.tree2)
650
self.assertEqual(b'r2', tree.get_revision_id())
651
self.assertFalse(self.tree2.branch.repository.has_revision(b'r2'))
654
class TestRevisionSpec_submit(TestRevisionSpec):
656
def test_submit_branch(self):
657
# Common ancestor of trees is 'alt_r2'
658
self.assertRaises(errors.NoSubmitBranch, self.get_in_history,
660
self.tree.branch.set_parent('../tree2')
661
self.assertInHistoryIs(None, b'alt_r2', 'submit:')
662
self.tree.branch.set_parent('bogus')
663
self.assertRaises(errors.NotBranchError, self.get_in_history,
665
# submit branch overrides parent branch
666
self.tree.branch.set_submit_branch('tree2')
667
self.assertInHistoryIs(None, b'alt_r2', 'submit:')
669
def test_as_revision_id(self):
670
self.tree.branch.set_submit_branch('tree2')
671
self.assertAsRevisionId(b'alt_r2', 'branch:tree2')
674
class TestRevisionSpec_mainline(TestRevisionSpec):
676
def test_as_revision_id(self):
677
self.assertAsRevisionId(b'r1', 'mainline:1')
678
self.assertAsRevisionId(b'r2', 'mainline:1.1.1')
679
self.assertAsRevisionId(b'r2', 'mainline:revid:alt_r2')
680
spec = RevisionSpec.from_string('mainline:revid:alt_r22')
681
e = self.assertRaises(errors.InvalidRevisionSpec,
682
spec.as_revision_id, self.tree.branch)
683
self.assertContainsRe(str(e),
684
"Requested revision: 'mainline:revid:alt_r22' does not exist in"
687
def test_in_history(self):
688
self.assertInHistoryIs(2, b'r2', 'mainline:revid:alt_r2')
691
class TestRevisionSpec_annotate(TestRevisionSpec):
694
super(TestRevisionSpec_annotate, self).setUp()
695
self.tree = self.make_branch_and_tree('annotate-tree')
696
self.build_tree_contents([('annotate-tree/file1', b'1\n')])
697
self.tree.add('file1')
698
self.tree.commit('r1', rev_id=b'r1')
699
self.build_tree_contents([('annotate-tree/file1', b'2\n1\n')])
700
self.tree.commit('r2', rev_id=b'r2')
701
self.build_tree_contents([('annotate-tree/file1', b'2\n1\n3\n')])
703
def test_as_revision_id_r1(self):
704
self.assertAsRevisionId(b'r1', 'annotate:annotate-tree/file1:2')
706
def test_as_revision_id_r2(self):
707
self.assertAsRevisionId(b'r2', 'annotate:annotate-tree/file1:1')
709
def test_as_revision_id_uncommitted(self):
710
spec = RevisionSpec.from_string('annotate:annotate-tree/file1:3')
711
e = self.assertRaises(errors.InvalidRevisionSpec,
712
spec.as_revision_id, self.tree.branch)
713
self.assertContainsRe(str(e),
714
r"Requested revision: \'annotate:annotate-tree/file1:3\' does not"
715
" exist in branch: .*\nLine 3 has not been committed.")
717
def test_non_existent_line(self):
718
spec = RevisionSpec.from_string('annotate:annotate-tree/file1:4')
719
e = self.assertRaises(errors.InvalidRevisionSpec,
720
spec.as_revision_id, self.tree.branch)
721
self.assertContainsRe(str(e),
722
r"Requested revision: \'annotate:annotate-tree/file1:4\' does not"
723
" exist in branch: .*\nNo such line: 4")
725
def test_invalid_line(self):
726
spec = RevisionSpec.from_string('annotate:annotate-tree/file1:q')
727
e = self.assertRaises(errors.InvalidRevisionSpec,
728
spec.as_revision_id, self.tree.branch)
729
self.assertContainsRe(str(e),
730
r"Requested revision: \'annotate:annotate-tree/file1:q\' does not"
731
" exist in branch: .*\nNo such line: q")
733
def test_no_such_file(self):
734
spec = RevisionSpec.from_string('annotate:annotate-tree/file2:1')
735
e = self.assertRaises(errors.InvalidRevisionSpec,
736
spec.as_revision_id, self.tree.branch)
737
self.assertContainsRe(str(e),
738
r"Requested revision: \'annotate:annotate-tree/file2:1\' does not"
739
" exist in branch: .*\nFile 'file2' is not versioned")
741
def test_no_such_file_with_colon(self):
742
spec = RevisionSpec.from_string('annotate:annotate-tree/fi:le2:1')
743
e = self.assertRaises(errors.InvalidRevisionSpec,
744
spec.as_revision_id, self.tree.branch)
745
self.assertContainsRe(str(e),
746
r"Requested revision: \'annotate:annotate-tree/fi:le2:1\' does not"
747
" exist in branch: .*\nFile 'fi:le2' is not versioned")
20
from bzrlib.builtins import merge
21
from bzrlib.branch import Branch
22
from bzrlib.tests import TestCaseWithTransport
23
from bzrlib.errors import NoCommonAncestor, NoCommits
24
from bzrlib.errors import NoSuchRevision
25
from bzrlib.revisionspec import RevisionSpec
28
class TestRevisionNamespaces(TestCaseWithTransport):
30
def test_revision_namespaces(self):
31
"""Test revision specifiers.
33
These identify revisions by date, etc."""
34
wt = self.make_branch_and_tree('.')
37
wt.commit('Commit one', rev_id='a@r-0-1', timestamp=time.time() - 60*60*24)
38
wt.commit('Commit two', rev_id='a@r-0-2')
39
wt.commit('Commit three', rev_id='a@r-0-3')
41
self.assertEquals(RevisionSpec(None).in_history(b), (0, None))
42
self.assertEquals(RevisionSpec(1).in_history(b), (1, 'a@r-0-1'))
43
self.assertEquals(RevisionSpec('revno:1').in_history(b),
45
self.assertEquals(RevisionSpec('revid:a@r-0-1').in_history(b),
47
self.assertRaises(NoSuchRevision,
48
RevisionSpec('revid:a@r-0-0').in_history, b)
49
self.assertRaises(TypeError, RevisionSpec, object)
51
self.assertEquals(RevisionSpec('date:today').in_history(b),
53
self.assertEquals(RevisionSpec('date:yesterday').in_history(b),
55
self.assertEquals(RevisionSpec('before:date:today').in_history(b),
58
self.assertEquals(RevisionSpec('last:1').in_history(b),
60
self.assertEquals(RevisionSpec('-1').in_history(b), (3, 'a@r-0-3'))
61
# self.assertEquals(b.get_revision_info('last:1'), (3, 'a@r-0-3'))
62
# self.assertEquals(b.get_revision_info('-1'), (3, 'a@r-0-3'))
64
self.assertEquals(RevisionSpec('ancestor:.').in_history(b).rev_id,
68
wt2 = self.make_branch_and_tree('newbranch')
70
self.assertRaises(NoCommits, RevisionSpec('ancestor:.').in_history, b2)
72
d3 = b.bzrdir.sprout('copy')
74
wt3 = d3.open_workingtree()
75
wt3.commit('Commit four', rev_id='b@r-0-4')
76
self.assertEquals(RevisionSpec('ancestor:.').in_history(b3).rev_id,
78
merge(['copy', -1], [None, None])
79
wt.commit('Commit five', rev_id='a@r-0-4')
80
self.assertEquals(RevisionSpec('ancestor:copy').in_history(b).rev_id,
82
self.assertEquals(RevisionSpec('ancestor:.').in_history(b3).rev_id,
85
# This should be in the revision store, but not in revision-history
86
self.assertEquals((None, 'b@r-0-4'),
87
RevisionSpec('revid:b@r-0-4').in_history(b))
89
def test_branch_namespace(self):
90
"""Ensure that the branch namespace pulls in the requisite content."""
91
self.build_tree(['branch1/', 'branch1/file', 'branch2/'])
92
wt = self.make_branch_and_tree('branch1')
96
d2 = branch.bzrdir.sprout('branch2')
97
print >> open('branch2/file', 'w'), 'new content'
98
branch2 = d2.open_branch()
99
d2.open_workingtree().commit('update file', rev_id='A')
100
spec = RevisionSpec('branch:./branch2/.bzr/../')
101
rev_info = spec.in_history(branch)
102
self.assertEqual(rev_info, (None, 'A'))