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
18
20
from .lazy_import import lazy_import
19
21
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)
54
class InvalidRevisionSpec(errors.BzrError):
56
_fmt = ("Requested revision: '%(spec)s' does not exist in branch:"
57
" %(branch_url)s%(extra)s")
59
def __init__(self, spec, branch, extra=None):
60
errors.BzrError.__init__(self, branch=branch, spec=spec)
61
self.branch_url = getattr(branch, 'user_url', str(branch))
63
self.extra = '\n' + str(extra)
68
45
class RevisionInfo(object):
96
73
if not self._has_revno and self.rev_id is not None:
98
75
self._revno = self.branch.revision_id_to_revno(self.rev_id)
99
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
76
except errors.NoSuchRevision:
100
77
self._revno = None
101
78
self._has_revno = True
102
79
return self._revno
167
dwim_catchable_exceptions = (InvalidRevisionSpec,)
144
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
168
145
"""Exceptions that RevisionSpec_dwim._match_on will catch.
170
147
If the revspec is part of ``dwim_revspecs``, it may be tried with an
185
162
return RevisionSpec(None, _internal=True)
186
if not isinstance(spec, str):
163
if not isinstance(spec, (str, text_type)):
187
164
raise TypeError("revision spec needs to be text")
188
165
match = revspec_registry.get_prefix(spec)
189
166
if match is not None:
224
201
# special case - nothing supplied
226
203
elif self.prefix:
227
raise InvalidRevisionSpec(self.user_spec, branch)
204
raise errors.InvalidRevisionSpec(self.user_spec, branch)
229
raise InvalidRevisionSpec(self.spec, branch)
206
raise errors.InvalidRevisionSpec(self.spec, branch)
231
208
def in_history(self, branch):
232
209
return self._match_on_and_check(branch, revs=None)
341
318
# Well, I dunno what it is. Note that we don't try to keep track of the
342
319
# first of last exception raised during the DWIM tries as none seems
343
320
# really relevant.
344
raise InvalidRevisionSpec(self.spec, branch)
321
raise errors.InvalidRevisionSpec(self.spec, branch)
347
324
def append_possible_revspec(cls, revspec):
402
379
if revno_spec == '':
403
380
if not branch_spec:
404
raise InvalidRevisionSpec(
405
self.user_spec, branch,
406
'cannot have an empty revno and no branch')
381
raise errors.InvalidRevisionSpec(self.user_spec,
382
branch, 'cannot have an empty revno and no branch')
417
393
match_revno = tuple((int(number)
418
394
for number in revno_spec.split('.')))
419
395
except ValueError as e:
420
raise InvalidRevisionSpec(self.user_spec, branch, e)
396
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
430
406
revision_id = branch.dotted_revno_to_revision_id(match_revno,
431
407
_cache_reverse=True)
432
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
433
raise InvalidRevisionSpec(self.user_spec, branch)
408
except errors.NoSuchRevision:
409
raise errors.InvalidRevisionSpec(self.user_spec, branch)
435
411
# there is no traditional 'revno' for dotted-decimal revnos.
436
412
# so for API compatibility we return None.
446
422
revno = last_revno + revno + 1
448
424
revision_id = branch.get_rev_id(revno)
449
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
450
raise InvalidRevisionSpec(self.user_spec, branch)
425
except errors.NoSuchRevision:
426
raise errors.InvalidRevisionSpec(self.user_spec, branch)
451
427
return branch, revno, revision_id
453
429
def _as_revision_id(self, context_branch):
495
471
# self.spec comes straight from parsing the command line arguments,
496
472
# so we expect it to be a Unicode string. Switch it to the internal
497
473
# representation.
498
if isinstance(self.spec, str):
474
if isinstance(self.spec, text_type):
499
475
return cache_utf8.encode(self.spec)
531
507
offset = int(self.spec)
532
508
except ValueError as e:
533
raise InvalidRevisionSpec(self.user_spec, context_branch, e)
509
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
536
raise InvalidRevisionSpec(
537
self.user_spec, context_branch,
538
'you must supply a positive value')
512
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
513
'you must supply a positive value')
540
515
revno = last_revno - offset + 1
542
517
revision_id = context_branch.get_rev_id(revno)
543
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
544
raise InvalidRevisionSpec(self.user_spec, context_branch)
518
except errors.NoSuchRevision:
519
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
545
520
return revno, revision_id
547
522
def _as_revision_id(self, context_branch):
578
553
def _match_on(self, branch, revs):
579
554
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
581
raise InvalidRevisionSpec(
582
self.user_spec, branch,
583
'cannot go before the null: revision')
556
raise errors.InvalidRevisionSpec(self.user_spec, branch,
557
'cannot go before the null: revision')
584
558
if r.revno is None:
585
559
# We need to use the repository history here
586
560
rev = branch.repository.get_revision(r.rev_id)
593
567
revno = r.revno - 1
595
569
revision_id = branch.get_rev_id(revno, revs)
596
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
597
raise InvalidRevisionSpec(self.user_spec, branch)
570
except errors.NoSuchRevision:
571
raise errors.InvalidRevisionSpec(self.user_spec,
598
573
return RevisionInfo(branch, revno, revision_id)
600
575
def _as_revision_id(self, context_branch):
601
576
base_revision_id = RevisionSpec.from_string(
602
577
self.spec)._as_revision_id(context_branch)
603
578
if base_revision_id == revision.NULL_REVISION:
604
raise InvalidRevisionSpec(
605
self.user_spec, context_branch,
606
'cannot go before the null: revision')
579
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
580
'cannot go before the null: revision')
607
581
context_repo = context_branch.repository
608
with context_repo.lock_read():
582
context_repo.lock_read()
609
584
parent_map = context_repo.get_parent_map([base_revision_id])
586
context_repo.unlock()
610
587
if base_revision_id not in parent_map:
611
588
# Ghost, or unknown revision id
612
raise InvalidRevisionSpec(
613
self.user_spec, context_branch, 'cannot find the matching revision')
589
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
590
'cannot find the matching revision')
614
591
parents = parent_map[base_revision_id]
615
592
if len(parents) < 1:
616
raise InvalidRevisionSpec(
617
self.user_spec, context_branch, 'No parents for revision.')
593
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
594
'No parents for revision.')
618
595
return parents[0]
705
682
m = self._date_regex.match(self.spec)
706
683
if not m or (not m.group('date') and not m.group('time')):
707
raise InvalidRevisionSpec(
708
self.user_spec, branch, 'invalid date')
684
raise errors.InvalidRevisionSpec(self.user_spec,
685
branch, 'invalid date')
711
688
if m.group('date'):
728
705
hour, minute, second = 0, 0, 0
729
706
except ValueError:
730
raise InvalidRevisionSpec(
731
self.user_spec, branch, 'invalid date')
707
raise errors.InvalidRevisionSpec(self.user_spec,
708
branch, 'invalid date')
733
710
dt = datetime.datetime(year=year, month=month, day=day,
734
711
hour=hour, minute=minute, second=second)
735
712
with branch.lock_read():
736
713
rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
737
714
if rev == branch.revno():
738
raise InvalidRevisionSpec(self.user_spec, branch)
715
raise errors.InvalidRevisionSpec(self.user_spec, branch)
739
716
return RevisionInfo(branch, rev)
910
887
def _raise_invalid(self, numstring, context_branch):
911
raise InvalidRevisionSpec(
912
self.user_spec, context_branch,
913
'No such line: %s' % numstring)
888
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
889
'No such line: %s' % numstring)
915
891
def _as_revision_id(self, context_branch):
916
892
path, numstring = self.spec.rsplit(':', 1)
921
897
tree, file_path = workingtree.WorkingTree.open_containing(path)
922
898
with tree.lock_read():
923
899
if not tree.has_filename(file_path):
924
raise InvalidRevisionSpec(
925
self.user_spec, context_branch,
926
"File '%s' is not versioned." % file_path)
900
raise errors.InvalidRevisionSpec(self.user_spec,
901
context_branch, "File '%s' is not versioned." %
927
903
revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
929
905
revision_id = revision_ids[index]
930
906
except IndexError:
931
907
self._raise_invalid(numstring, context_branch)
932
908
if revision_id == revision.CURRENT_REVISION:
933
raise InvalidRevisionSpec(
934
self.user_spec, context_branch,
935
'Line %s has not been committed.' % numstring)
909
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
910
'Line %s has not been committed.' % numstring)
936
911
return revision_id
956
931
result = graph.find_lefthand_merger(revision_id,
957
932
context_branch.last_revision())
958
933
if result is None:
959
raise InvalidRevisionSpec(self.user_spec, context_branch)
934
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)