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
18
from .lazy_import import lazy_import
17
from __future__ import absolute_import
20
from brzlib.lazy_import import lazy_import
19
21
lazy_import(globals(), """
24
26
branch as _mod_branch,
29
from breezy.i18n import gettext
32
from brzlib.i18n import gettext
68
71
if not self._has_revno and self.rev_id is not None:
70
73
self._revno = self.branch.revision_id_to_revno(self.rev_id)
71
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
74
except errors.NoSuchRevision:
73
76
self._has_revno = True
79
def __nonzero__(self):
77
80
if self.rev_id is None:
79
82
# TODO: otherwise, it should depend on how I was built -
81
84
# if it's in_store(branch), do the check below
82
85
return self.branch.repository.has_revision(self.rev_id)
84
__nonzero__ = __bool__
89
90
def __getitem__(self, index):
91
if index == 0: return self.revno
92
if index == 1: return self.rev_id
94
93
raise IndexError(index)
99
98
def __eq__(self, other):
100
99
if type(other) not in (tuple, list, type(self)):
102
if isinstance(other, type(self)) and self.branch is not other.branch:
101
if type(other) is type(self) and self.branch is not other.branch:
104
103
return tuple(self) == tuple(other)
106
105
def __repr__(self):
107
return '<breezy.revisionspec.RevisionInfo object %s, %s for %r>' % (
106
return '<brzlib.revisionspec.RevisionInfo object %s, %s for %r>' % (
108
107
self.revno, self.rev_id, self.branch)
111
def from_revision_id(branch, revision_id):
110
def from_revision_id(branch, revision_id, revs=symbol_versioning.DEPRECATED_PARAMETER):
112
111
"""Construct a RevisionInfo given just the id.
114
113
Use this if you don't know or care what the revno is.
115
if symbol_versioning.deprecated_passed(revs):
116
symbol_versioning.warn(
117
'RevisionInfo.from_revision_id(revs) was deprecated in 2.5.',
116
120
return RevisionInfo(branch, revno=None, rev_id=revision_id)
143
# wants_revision_history has been deprecated in 2.5.
144
wants_revision_history = False
139
145
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
140
146
"""Exceptions that RevisionSpec_dwim._match_on will catch.
153
159
:return: A RevisionSpec object that understands how to parse the
154
160
supplied notation.
162
if not isinstance(spec, (type(None), basestring)):
163
raise TypeError('error')
157
166
return RevisionSpec(None, _internal=True)
158
if not isinstance(spec, str):
159
raise TypeError("revision spec needs to be text")
160
167
match = revspec_registry.get_prefix(spec)
161
168
if match is not None:
162
169
spectype, specsuffix = match
176
183
called directly. Only from RevisionSpec.from_string()
178
185
if not _internal:
179
raise AssertionError(
180
'Creating a RevisionSpec directly is not supported. '
181
'Use RevisionSpec.from_string() instead.')
186
symbol_versioning.warn('Creating a RevisionSpec directly has'
187
' been deprecated in version 0.11. Use'
188
' RevisionSpec.from_string()'
190
DeprecationWarning, stacklevel=2)
182
191
self.user_spec = spec
183
192
if self.prefix and spec.startswith(self.prefix):
184
193
spec = spec[len(self.prefix):]
201
210
raise errors.InvalidRevisionSpec(self.spec, branch)
203
212
def in_history(self, branch):
204
return self._match_on_and_check(branch, revs=None)
214
if self.wants_revision_history:
215
symbol_versioning.warn(
216
"RevisionSpec.wants_revision_history was "
217
"deprecated in 2.5 (%s)." % self.__class__.__name__,
221
graph = branch.repository.get_graph()
222
revs = list(graph.iter_lefthand_ancestry(
223
branch.last_revision(), [revision.NULL_REVISION]))
230
# this should never trigger.
231
# TODO: make it a deprecated code path. RBC 20060928
233
return self._match_on_and_check(branch, revs)
206
235
# FIXME: in_history is somewhat broken,
207
236
# it will return non-history revisions in many
310
339
except rs_class.dwim_catchable_exceptions:
342
# Try the old (deprecated) dwim list:
343
for rs_class in dwim_revspecs:
345
return self._try_spectype(rs_class, branch)
346
except rs_class.dwim_catchable_exceptions:
313
349
# Well, I dunno what it is. Note that we don't try to keep track of the
314
350
# first of last exception raised during the DWIM tries as none seems
315
351
# really relevant.
369
405
branch_spec = None
371
407
revno_spec = self.spec[:loc]
372
branch_spec = self.spec[loc + 1:]
408
branch_spec = self.spec[loc+1:]
374
410
if revno_spec == '':
375
411
if not branch_spec:
376
412
raise errors.InvalidRevisionSpec(self.user_spec,
377
branch, 'cannot have an empty revno and no branch')
413
branch, 'cannot have an empty revno and no branch')
385
421
# but the from_string method is a little primitive
386
422
# right now - RBC 20060928
388
match_revno = tuple((int(number)
389
for number in revno_spec.split('.')))
390
except ValueError as e:
424
match_revno = tuple((int(number) for number in revno_spec.split('.')))
425
except ValueError, e:
391
426
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
401
436
revision_id = branch.dotted_revno_to_revision_id(match_revno,
403
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
438
except errors.NoSuchRevision:
404
439
raise errors.InvalidRevisionSpec(self.user_spec, branch)
406
441
# there is no traditional 'revno' for dotted-decimal revnos.
417
452
revno = last_revno + revno + 1
419
454
revision_id = branch.get_rev_id(revno)
420
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
455
except errors.NoSuchRevision:
421
456
raise errors.InvalidRevisionSpec(self.user_spec, branch)
422
457
return branch, revno, revision_id
466
500
# self.spec comes straight from parsing the command line arguments,
467
501
# so we expect it to be a Unicode string. Switch it to the internal
468
502
# representation.
469
if isinstance(self.spec, str):
470
return cache_utf8.encode(self.spec)
503
return osutils.safe_revision_id(self.spec, warn=False)
474
507
class RevisionSpec_last(RevisionSpec):
510
543
revno = last_revno - offset + 1
512
545
revision_id = context_branch.get_rev_id(revno)
513
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
546
except errors.NoSuchRevision:
514
547
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
515
548
return revno, revision_id
549
583
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
551
585
raise errors.InvalidRevisionSpec(self.user_spec, branch,
552
'cannot go before the null: revision')
586
'cannot go before the null: revision')
553
587
if r.revno is None:
554
588
# We need to use the repository history here
555
589
rev = branch.repository.get_revision(r.rev_id)
562
596
revno = r.revno - 1
564
598
revision_id = branch.get_rev_id(revno, revs)
565
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
599
except errors.NoSuchRevision:
566
600
raise errors.InvalidRevisionSpec(self.user_spec,
568
602
return RevisionInfo(branch, revno, revision_id)
570
604
def _as_revision_id(self, context_branch):
571
base_revision_id = RevisionSpec.from_string(
572
self.spec)._as_revision_id(context_branch)
605
base_revision_id = RevisionSpec.from_string(self.spec)._as_revision_id(context_branch)
573
606
if base_revision_id == revision.NULL_REVISION:
574
607
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
575
'cannot go before the null: revision')
608
'cannot go before the null: revision')
576
609
context_repo = context_branch.repository
577
with context_repo.lock_read():
610
context_repo.lock_read()
578
612
parent_map = context_repo.get_parent_map([base_revision_id])
614
context_repo.unlock()
579
615
if base_revision_id not in parent_map:
580
616
# Ghost, or unknown revision id
581
617
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
582
'cannot find the matching revision')
618
'cannot find the matching revision')
583
619
parents = parent_map[base_revision_id]
584
620
if len(parents) < 1:
585
621
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
586
'No parents for revision.')
622
'No parents for revision.')
587
623
return parents[0]
590
627
class RevisionSpec_tag(RevisionSpec):
591
628
"""Select a revision identified by tag name"""
601
638
def _match_on(self, branch, revs):
602
639
# Can raise tags not supported, NoSuchTag, etc
603
640
return RevisionInfo.from_revision_id(branch,
604
branch.tags.lookup_tag(self.spec))
641
branch.tags.lookup_tag(self.spec))
606
643
def _as_revision_id(self, context_branch):
607
644
return context_branch.tags.lookup_tag(self.spec)
610
648
class _RevListToTimestamps(object):
611
649
"""This takes a list of revisions, and allows you to bisect by date"""
649
687
_date_regex = lazy_regex.lazy_compile(
650
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
652
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
688
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
690
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
655
693
def _match_on(self, branch, revs):
662
700
# XXX: This doesn't actually work
663
701
# So the proper way of saying 'give me all entries for today' is:
664
702
# -r date:yesterday..date:today
665
today = datetime.datetime.fromordinal(
666
datetime.date.today().toordinal())
703
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
667
704
if self.spec.lower() == 'yesterday':
668
705
dt = today - datetime.timedelta(days=1)
669
706
elif self.spec.lower() == 'today':
697
hour, minute, second = 0, 0, 0
734
hour, minute, second = 0,0,0
698
735
except ValueError:
699
736
raise errors.InvalidRevisionSpec(self.user_spec,
700
737
branch, 'invalid date')
702
739
dt = datetime.datetime(year=year, month=month, day=day,
703
hour=hour, minute=minute, second=second)
704
with branch.lock_read():
740
hour=hour, minute=minute, second=second)
705
743
rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
706
746
if rev == branch.revno():
707
747
raise errors.InvalidRevisionSpec(self.user_spec, branch)
708
748
return RevisionInfo(branch, rev)
711
752
class RevisionSpec_ancestor(RevisionSpec):
712
753
"""Selects a common ancestor with a second branch."""
747
788
def _find_revision_id(branch, other_location):
748
from .branch import Branch
789
from brzlib.branch import Branch
750
with branch.lock_read():
751
793
revision_a = revision.ensure_null(branch.last_revision())
752
794
if revision_a == revision.NULL_REVISION:
753
795
raise errors.NoCommits(branch)
754
796
if other_location == '':
755
797
other_location = branch.get_parent()
756
798
other_branch = Branch.open(other_location)
757
with other_branch.lock_read():
799
other_branch.lock_read()
758
801
revision_b = revision.ensure_null(other_branch.last_revision())
759
802
if revision_b == revision.NULL_REVISION:
760
803
raise errors.NoCommits(other_branch)
761
804
graph = branch.repository.get_graph(other_branch.repository)
762
805
rev_id = graph.find_unique_lca(revision_a, revision_b)
807
other_branch.unlock()
763
808
if rev_id == revision.NULL_REVISION:
764
809
raise errors.NoCommonAncestor(revision_a, revision_b)
768
817
class RevisionSpec_branch(RevisionSpec):
780
829
dwim_catchable_exceptions = (errors.NotBranchError,)
782
831
def _match_on(self, branch, revs):
783
from .branch import Branch
832
from brzlib.branch import Branch
784
833
other_branch = Branch.open(self.spec)
785
834
revision_b = other_branch.last_revision()
786
835
if revision_b in (None, revision.NULL_REVISION):
796
845
return RevisionInfo(branch, None, revision_b)
798
847
def _as_revision_id(self, context_branch):
799
from .branch import Branch
848
from brzlib.branch import Branch
800
849
other_branch = Branch.open(self.spec)
801
850
last_revision = other_branch.last_revision()
802
851
last_revision = revision.ensure_null(last_revision)
806
855
return last_revision
808
857
def _as_tree(self, context_branch):
809
from .branch import Branch
858
from brzlib.branch import Branch
810
859
other_branch = Branch.open(self.spec)
811
860
last_revision = other_branch.last_revision()
812
861
last_revision = revision.ensure_null(last_revision)
851
901
if submit_location is None:
852
902
raise errors.NoSubmitBranch(branch)
853
903
trace.note(gettext('Using {0} {1}').format(location_type,
855
905
return submit_location
857
907
def _match_on(self, branch, revs):
858
908
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
859
909
return self._find_revision_info(branch,
860
self._get_submit_location(branch))
910
self._get_submit_location(branch))
862
912
def _as_revision_id(self, context_branch):
863
913
return self._find_revision_id(context_branch,
864
self._get_submit_location(context_branch))
914
self._get_submit_location(context_branch))
867
917
class RevisionSpec_annotate(RevisionIDSpec):
879
929
def _raise_invalid(self, numstring, context_branch):
880
930
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
881
'No such line: %s' % numstring)
931
'No such line: %s' % numstring)
883
933
def _as_revision_id(self, context_branch):
884
934
path, numstring = self.spec.rsplit(':', 1)
887
937
except ValueError:
888
938
self._raise_invalid(numstring, context_branch)
889
939
tree, file_path = workingtree.WorkingTree.open_containing(path)
890
with tree.lock_read():
891
if not tree.has_filename(file_path):
942
file_id = tree.path2id(file_path)
892
944
raise errors.InvalidRevisionSpec(self.user_spec,
893
context_branch, "File '%s' is not versioned." %
895
revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
945
context_branch, "File '%s' is not versioned." %
947
revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
897
951
revision_id = revision_ids[index]
898
952
except IndexError:
899
953
self._raise_invalid(numstring, context_branch)
900
954
if revision_id == revision.CURRENT_REVISION:
901
955
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
902
'Line %s has not been committed.' % numstring)
956
'Line %s has not been committed.' % numstring)
903
957
return revision_id
930
984
# The order in which we want to DWIM a revision spec without any prefix.
931
985
# revno is always tried first and isn't listed here, this is used by
932
986
# RevisionSpec_dwim._match_on
987
dwim_revspecs = symbol_versioning.deprecated_list(
988
symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
933
990
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
934
991
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
935
992
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
936
993
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
938
995
revspec_registry = registry.Registry()
941
996
def _register_revspec(revspec):
942
997
revspec_registry.register(revspec.prefix, revspec)
945
999
_register_revspec(RevisionSpec_revno)
946
1000
_register_revspec(RevisionSpec_revid)
947
1001
_register_revspec(RevisionSpec_last)