45
class InvalidRevisionSpec(errors.BzrError):
47
_fmt = ("Requested revision: '%(spec)s' does not exist in branch:"
48
" %(branch_url)s%(extra)s")
50
def __init__(self, spec, branch, extra=None):
51
errors.BzrError.__init__(self, branch=branch, spec=spec)
52
self.branch_url = getattr(branch, 'user_url', str(branch))
54
self.extra = '\n' + str(extra)
45
59
class RevisionInfo(object):
46
60
"""The results of applying a revision specification to a branch."""
144
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
158
dwim_catchable_exceptions = (InvalidRevisionSpec,)
145
159
"""Exceptions that RevisionSpec_dwim._match_on will catch.
147
161
If the revspec is part of ``dwim_revspecs``, it may be tried with an
201
215
# special case - nothing supplied
203
217
elif self.prefix:
204
raise errors.InvalidRevisionSpec(self.user_spec, branch)
218
raise InvalidRevisionSpec(self.user_spec, branch)
206
raise errors.InvalidRevisionSpec(self.spec, branch)
220
raise InvalidRevisionSpec(self.spec, branch)
208
222
def in_history(self, branch):
209
223
return self._match_on_and_check(branch, revs=None)
318
332
# Well, I dunno what it is. Note that we don't try to keep track of the
319
333
# first of last exception raised during the DWIM tries as none seems
320
334
# really relevant.
321
raise errors.InvalidRevisionSpec(self.spec, branch)
335
raise InvalidRevisionSpec(self.spec, branch)
324
338
def append_possible_revspec(cls, revspec):
379
393
if revno_spec == '':
380
394
if not branch_spec:
381
raise errors.InvalidRevisionSpec(self.user_spec,
382
branch, 'cannot have an empty revno and no branch')
395
raise InvalidRevisionSpec(
396
self.user_spec, branch, 'cannot have an empty revno and no branch')
393
407
match_revno = tuple((int(number)
394
408
for number in revno_spec.split('.')))
395
409
except ValueError as e:
396
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
410
raise InvalidRevisionSpec(self.user_spec, branch, e)
406
420
revision_id = branch.dotted_revno_to_revision_id(match_revno,
407
421
_cache_reverse=True)
408
422
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
409
raise errors.InvalidRevisionSpec(self.user_spec, branch)
423
raise InvalidRevisionSpec(self.user_spec, branch)
411
425
# there is no traditional 'revno' for dotted-decimal revnos.
412
426
# so for API compatibility we return None.
424
438
revision_id = branch.get_rev_id(revno)
425
439
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
426
raise errors.InvalidRevisionSpec(self.user_spec, branch)
440
raise InvalidRevisionSpec(self.user_spec, branch)
427
441
return branch, revno, revision_id
429
443
def _as_revision_id(self, context_branch):
507
521
offset = int(self.spec)
508
522
except ValueError as e:
509
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
523
raise InvalidRevisionSpec(self.user_spec, context_branch, e)
512
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
526
raise InvalidRevisionSpec(self.user_spec, context_branch,
513
527
'you must supply a positive value')
515
529
revno = last_revno - offset + 1
517
531
revision_id = context_branch.get_rev_id(revno)
518
532
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
519
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
533
raise InvalidRevisionSpec(self.user_spec, context_branch)
520
534
return revno, revision_id
522
536
def _as_revision_id(self, context_branch):
553
567
def _match_on(self, branch, revs):
554
568
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
556
raise errors.InvalidRevisionSpec(self.user_spec, branch,
557
'cannot go before the null: revision')
570
raise InvalidRevisionSpec(self.user_spec, branch, 'cannot go before the null: revision')
558
571
if r.revno is None:
559
572
# We need to use the repository history here
560
573
rev = branch.repository.get_revision(r.rev_id)
569
582
revision_id = branch.get_rev_id(revno, revs)
570
583
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
571
raise errors.InvalidRevisionSpec(self.user_spec,
584
raise InvalidRevisionSpec(self.user_spec, branch)
573
585
return RevisionInfo(branch, revno, revision_id)
575
587
def _as_revision_id(self, context_branch):
576
588
base_revision_id = RevisionSpec.from_string(
577
589
self.spec)._as_revision_id(context_branch)
578
590
if base_revision_id == revision.NULL_REVISION:
579
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
580
'cannot go before the null: revision')
591
raise InvalidRevisionSpec(
592
self.user_spec, context_branch,
593
'cannot go before the null: revision')
581
594
context_repo = context_branch.repository
582
595
with context_repo.lock_read():
583
596
parent_map = context_repo.get_parent_map([base_revision_id])
584
597
if base_revision_id not in parent_map:
585
598
# Ghost, or unknown revision id
586
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
587
'cannot find the matching revision')
599
raise InvalidRevisionSpec(
600
self.user_spec, context_branch, 'cannot find the matching revision')
588
601
parents = parent_map[base_revision_id]
589
602
if len(parents) < 1:
590
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
591
'No parents for revision.')
603
raise InvalidRevisionSpec(
604
self.user_spec, context_branch, 'No parents for revision.')
592
605
return parents[0]
679
692
m = self._date_regex.match(self.spec)
680
693
if not m or (not m.group('date') and not m.group('time')):
681
raise errors.InvalidRevisionSpec(self.user_spec,
682
branch, 'invalid date')
694
raise InvalidRevisionSpec(self.user_spec, branch, 'invalid date')
685
697
if m.group('date'):
702
714
hour, minute, second = 0, 0, 0
703
715
except ValueError:
704
raise errors.InvalidRevisionSpec(self.user_spec,
705
branch, 'invalid date')
716
raise InvalidRevisionSpec(self.user_spec, branch, 'invalid date')
707
718
dt = datetime.datetime(year=year, month=month, day=day,
708
719
hour=hour, minute=minute, second=second)
709
720
with branch.lock_read():
710
721
rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
711
722
if rev == branch.revno():
712
raise errors.InvalidRevisionSpec(self.user_spec, branch)
723
raise InvalidRevisionSpec(self.user_spec, branch)
713
724
return RevisionInfo(branch, rev)
884
895
def _raise_invalid(self, numstring, context_branch):
885
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
886
'No such line: %s' % numstring)
896
raise InvalidRevisionSpec(self.user_spec, context_branch,
897
'No such line: %s' % numstring)
888
899
def _as_revision_id(self, context_branch):
889
900
path, numstring = self.spec.rsplit(':', 1)
894
905
tree, file_path = workingtree.WorkingTree.open_containing(path)
895
906
with tree.lock_read():
896
907
if not tree.has_filename(file_path):
897
raise errors.InvalidRevisionSpec(self.user_spec,
898
context_branch, "File '%s' is not versioned." %
908
raise InvalidRevisionSpec(
909
self.user_spec, context_branch,
910
"File '%s' is not versioned." % file_path)
900
911
revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
902
913
revision_id = revision_ids[index]
903
914
except IndexError:
904
915
self._raise_invalid(numstring, context_branch)
905
916
if revision_id == revision.CURRENT_REVISION:
906
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
907
'Line %s has not been committed.' % numstring)
917
raise InvalidRevisionSpec(
918
self.user_spec, context_branch,
919
'Line %s has not been committed.' % numstring)
908
920
return revision_id
928
940
result = graph.find_lefthand_merger(revision_id,
929
941
context_branch.last_revision())
930
942
if result is None:
931
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
943
raise InvalidRevisionSpec(self.user_spec, context_branch)