14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
20
18
from .lazy_import import lazy_import
21
19
lazy_import(globals(), """
40
class InvalidRevisionSpec(errors.BzrError):
42
_fmt = ("Requested revision: '%(spec)s' does not exist in branch:"
43
" %(branch_url)s%(extra)s")
45
def __init__(self, spec, branch, extra=None):
46
errors.BzrError.__init__(self, branch=branch, spec=spec)
47
self.branch_url = getattr(branch, 'user_url', str(branch))
49
self.extra = '\n' + str(extra)
45
54
class RevisionInfo(object):
144
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
153
dwim_catchable_exceptions = (InvalidRevisionSpec,)
145
154
"""Exceptions that RevisionSpec_dwim._match_on will catch.
147
156
If the revspec is part of ``dwim_revspecs``, it may be tried with an
162
171
return RevisionSpec(None, _internal=True)
163
if not isinstance(spec, (str, text_type)):
172
if not isinstance(spec, str):
164
173
raise TypeError("revision spec needs to be text")
165
174
match = revspec_registry.get_prefix(spec)
166
175
if match is not None:
201
210
# special case - nothing supplied
203
212
elif self.prefix:
204
raise errors.InvalidRevisionSpec(self.user_spec, branch)
213
raise InvalidRevisionSpec(self.user_spec, branch)
206
raise errors.InvalidRevisionSpec(self.spec, branch)
215
raise InvalidRevisionSpec(self.spec, branch)
208
217
def in_history(self, branch):
209
218
return self._match_on_and_check(branch, revs=None)
318
327
# Well, I dunno what it is. Note that we don't try to keep track of the
319
328
# first of last exception raised during the DWIM tries as none seems
320
329
# really relevant.
321
raise errors.InvalidRevisionSpec(self.spec, branch)
330
raise InvalidRevisionSpec(self.spec, branch)
324
333
def append_possible_revspec(cls, revspec):
379
388
if revno_spec == '':
380
389
if not branch_spec:
381
raise errors.InvalidRevisionSpec(self.user_spec,
382
branch, 'cannot have an empty revno and no branch')
390
raise InvalidRevisionSpec(
391
self.user_spec, branch,
392
'cannot have an empty revno and no branch')
393
403
match_revno = tuple((int(number)
394
404
for number in revno_spec.split('.')))
395
405
except ValueError as e:
396
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
406
raise InvalidRevisionSpec(self.user_spec, branch, e)
406
416
revision_id = branch.dotted_revno_to_revision_id(match_revno,
407
417
_cache_reverse=True)
408
418
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
409
raise errors.InvalidRevisionSpec(self.user_spec, branch)
419
raise InvalidRevisionSpec(self.user_spec, branch)
411
421
# there is no traditional 'revno' for dotted-decimal revnos.
412
422
# so for API compatibility we return None.
424
434
revision_id = branch.get_rev_id(revno)
425
435
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
426
raise errors.InvalidRevisionSpec(self.user_spec, branch)
436
raise InvalidRevisionSpec(self.user_spec, branch)
427
437
return branch, revno, revision_id
429
439
def _as_revision_id(self, context_branch):
471
481
# self.spec comes straight from parsing the command line arguments,
472
482
# so we expect it to be a Unicode string. Switch it to the internal
473
483
# representation.
474
if isinstance(self.spec, text_type):
484
if isinstance(self.spec, str):
475
485
return cache_utf8.encode(self.spec)
507
517
offset = int(self.spec)
508
518
except ValueError as e:
509
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
519
raise InvalidRevisionSpec(self.user_spec, context_branch, e)
512
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
513
'you must supply a positive value')
522
raise InvalidRevisionSpec(
523
self.user_spec, context_branch,
524
'you must supply a positive value')
515
526
revno = last_revno - offset + 1
517
528
revision_id = context_branch.get_rev_id(revno)
518
529
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
519
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
530
raise InvalidRevisionSpec(self.user_spec, context_branch)
520
531
return revno, revision_id
522
533
def _as_revision_id(self, context_branch):
553
564
def _match_on(self, branch, revs):
554
565
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')
567
raise InvalidRevisionSpec(
568
self.user_spec, branch,
569
'cannot go before the null: revision')
558
570
if r.revno is None:
559
571
# We need to use the repository history here
560
572
rev = branch.repository.get_revision(r.rev_id)
569
581
revision_id = branch.get_rev_id(revno, revs)
570
582
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
571
raise errors.InvalidRevisionSpec(self.user_spec,
583
raise InvalidRevisionSpec(self.user_spec, branch)
573
584
return RevisionInfo(branch, revno, revision_id)
575
586
def _as_revision_id(self, context_branch):
576
587
base_revision_id = RevisionSpec.from_string(
577
588
self.spec)._as_revision_id(context_branch)
578
589
if base_revision_id == revision.NULL_REVISION:
579
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
580
'cannot go before the null: revision')
590
raise InvalidRevisionSpec(
591
self.user_spec, context_branch,
592
'cannot go before the null: revision')
581
593
context_repo = context_branch.repository
582
594
with context_repo.lock_read():
583
595
parent_map = context_repo.get_parent_map([base_revision_id])
584
596
if base_revision_id not in parent_map:
585
597
# Ghost, or unknown revision id
586
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
587
'cannot find the matching revision')
598
raise InvalidRevisionSpec(
599
self.user_spec, context_branch, 'cannot find the matching revision')
588
600
parents = parent_map[base_revision_id]
589
601
if len(parents) < 1:
590
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
591
'No parents for revision.')
602
raise errors.InvalidRevisionSpec(
603
self.user_spec, context_branch, 'No parents for revision.')
592
604
return parents[0]
679
691
m = self._date_regex.match(self.spec)
680
692
if not m or (not m.group('date') and not m.group('time')):
681
raise errors.InvalidRevisionSpec(self.user_spec,
682
branch, 'invalid date')
693
raise InvalidRevisionSpec(
694
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(
717
self.user_spec, branch, 'invalid date')
707
719
dt = datetime.datetime(year=year, month=month, day=day,
708
720
hour=hour, minute=minute, second=second)
709
721
with branch.lock_read():
710
722
rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
711
723
if rev == branch.revno():
712
raise errors.InvalidRevisionSpec(self.user_spec, branch)
724
raise InvalidRevisionSpec(self.user_spec, branch)
713
725
return RevisionInfo(branch, rev)
884
896
def _raise_invalid(self, numstring, context_branch):
885
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
886
'No such line: %s' % numstring)
897
raise InvalidRevisionSpec(
898
self.user_spec, context_branch,
899
'No such line: %s' % numstring)
888
901
def _as_revision_id(self, context_branch):
889
902
path, numstring = self.spec.rsplit(':', 1)
894
907
tree, file_path = workingtree.WorkingTree.open_containing(path)
895
908
with tree.lock_read():
896
909
if not tree.has_filename(file_path):
897
raise errors.InvalidRevisionSpec(self.user_spec,
898
context_branch, "File '%s' is not versioned." %
910
raise InvalidRevisionSpec(
911
self.user_spec, context_branch,
912
"File '%s' is not versioned." % file_path)
900
913
revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
902
915
revision_id = revision_ids[index]
903
916
except IndexError:
904
917
self._raise_invalid(numstring, context_branch)
905
918
if revision_id == revision.CURRENT_REVISION:
906
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
907
'Line %s has not been committed.' % numstring)
919
raise InvalidRevisionSpec(
920
self.user_spec, context_branch,
921
'Line %s has not been committed.' % numstring)
908
922
return revision_id
928
942
result = graph.find_lefthand_merger(revision_id,
929
943
context_branch.last_revision())
930
944
if result is None:
931
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
945
raise InvalidRevisionSpec(self.user_spec, context_branch)