15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
from .lazy_import import lazy_import
20
from bzrlib.lazy_import import lazy_import
19
21
lazy_import(globals(), """
24
branch as _mod_branch,
29
from breezy.i18n import gettext
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
39
class RevisionInfo(object):
82
53
or treat the result as a tuple.
85
def __init__(self, branch, revno=None, rev_id=None):
56
def __init__(self, branch, revno, rev_id=_marker):
86
57
self.branch = branch
87
self._has_revno = (revno is not None)
90
if self.rev_id is None and self._revno is not None:
91
60
# allow caller to be lazy
92
self.rev_id = branch.get_rev_id(self._revno)
96
if not self._has_revno and self.rev_id is not None:
98
self._revno = self.branch.revision_id_to_revno(self.rev_id)
99
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
101
self._has_revno = True
61
if self.revno is None:
64
self.rev_id = branch.get_rev_id(self.revno)
68
def __nonzero__(self):
69
# first the easy ones...
105
70
if self.rev_id is None:
72
if self.revno is not None:
107
74
# TODO: otherwise, it should depend on how I was built -
108
75
# if it's in_history(branch), then check revision_history(),
109
76
# if it's in_store(branch), do the check below
110
77
return self.branch.repository.has_revision(self.rev_id)
112
__nonzero__ = __bool__
114
79
def __len__(self):
117
82
def __getitem__(self, index):
83
if index == 0: return self.revno
84
if index == 1: return self.rev_id
122
85
raise IndexError(index)
127
90
def __eq__(self, other):
128
91
if type(other) not in (tuple, list, type(self)):
130
if isinstance(other, type(self)) and self.branch is not other.branch:
93
if type(other) is type(self) and self.branch is not other.branch:
132
95
return tuple(self) == tuple(other)
134
97
def __repr__(self):
135
return '<breezy.revisionspec.RevisionInfo object %s, %s for %r>' % (
98
return '<bzrlib.revisionspec.RevisionInfo object %s, %s for %r>' % (
136
99
self.revno, self.rev_id, self.branch)
139
def from_revision_id(branch, revision_id):
102
def from_revision_id(branch, revision_id, revs):
140
103
"""Construct a RevisionInfo given just the id.
142
105
Use this if you don't know or care what the revno is.
144
return RevisionInfo(branch, revno=None, rev_id=revision_id)
107
if revision_id == revision.NULL_REVISION:
108
return RevisionInfo(branch, 0, revision_id)
110
revno = revs.index(revision_id) + 1
113
return RevisionInfo(branch, revno, revision_id)
116
# classes in this list should have a "prefix" attribute, against which
117
# string specs are matched
147
121
class RevisionSpec(object):
150
124
help_txt = """A parsed revision specification.
152
A revision specification is a string, which may be unambiguous about
153
what it represents by giving a prefix like 'date:' or 'revid:' etc,
154
or it may have no prefix, in which case it's tried against several
155
specifier types in sequence to determine what the user meant.
126
A revision specification can be an integer, in which case it is
127
assumed to be a revno (though this will translate negative values
128
into positive ones); or it can be a string, in which case it is
129
parsed for something like 'date:' or 'revid:' etc.
157
131
Revision specs are an UI element, and they have been moved out
158
132
of the branch class to leave "back-end" classes unaware of such
192
160
spectype.__name__, spec)
193
161
return spectype(spec, _internal=True)
195
# Otherwise treat it as a DWIM, build the RevisionSpec object and
196
# wait for _match_on to be called.
197
return RevisionSpec_dwim(spec, _internal=True)
163
for spectype in SPEC_TYPES:
164
if spec.startswith(spectype.prefix):
165
trace.mutter('Returning RevisionSpec %s for %s',
166
spectype.__name__, spec)
167
return spectype(spec, _internal=True)
168
# RevisionSpec_revno is special cased, because it is the only
169
# one that directly handles plain integers
170
# TODO: This should not be special cased rather it should be
171
# a method invocation on spectype.canparse()
173
if _revno_regex is None:
174
_revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
175
if _revno_regex.match(spec) is not None:
176
return RevisionSpec_revno(spec, _internal=True)
178
raise errors.NoSuchRevisionSpec(spec)
199
180
def __init__(self, spec, _internal=False):
200
181
"""Create a RevisionSpec referring to the Null revision.
224
207
# special case - nothing supplied
226
209
elif self.prefix:
227
raise InvalidRevisionSpec(self.user_spec, branch)
210
raise errors.InvalidRevisionSpec(self.user_spec, branch)
229
raise InvalidRevisionSpec(self.spec, branch)
212
raise errors.InvalidRevisionSpec(self.spec, branch)
231
214
def in_history(self, branch):
232
return self._match_on_and_check(branch, revs=None)
216
if self.wants_revision_history:
217
revs = branch.revision_history()
221
# this should never trigger.
222
# TODO: make it a deprecated code path. RBC 20060928
224
return self._match_on_and_check(branch, revs)
234
226
# FIXME: in_history is somewhat broken,
235
227
# it will return non-history revisions in many
301
class RevisionSpec_dwim(RevisionSpec):
302
"""Provides a DWIMish revision specifier lookup.
304
Note that this does not go in the revspec_registry because by definition
305
there is no prefix to identify it. It's solely called from
306
RevisionSpec.from_string() because the DWIMification happen when _match_on
307
is called so the string describing the revision is kept here until needed.
312
_revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
314
# The revspecs to try
315
_possible_revspecs = []
317
def _try_spectype(self, rstype, branch):
318
rs = rstype(self.spec, _internal=True)
319
# Hit in_history to find out if it exists, or we need to try the
321
return rs.in_history(branch)
323
def _match_on(self, branch, revs):
324
"""Run the lookup and see what we can get."""
326
# First, see if it's a revno
327
if self._revno_regex.match(self.spec) is not None:
329
return self._try_spectype(RevisionSpec_revno, branch)
330
except RevisionSpec_revno.dwim_catchable_exceptions:
333
# Next see what has been registered
334
for objgetter in self._possible_revspecs:
335
rs_class = objgetter.get_obj()
337
return self._try_spectype(rs_class, branch)
338
except rs_class.dwim_catchable_exceptions:
341
# Well, I dunno what it is. Note that we don't try to keep track of the
342
# first of last exception raised during the DWIM tries as none seems
344
raise InvalidRevisionSpec(self.spec, branch)
347
def append_possible_revspec(cls, revspec):
348
"""Append a possible DWIM revspec.
350
:param revspec: Revision spec to try.
352
cls._possible_revspecs.append(registry._ObjectGetter(revspec))
355
def append_possible_lazy_revspec(cls, module_name, member_name):
356
"""Append a possible lazily loaded DWIM revspec.
358
:param module_name: Name of the module with the revspec
359
:param member_name: Name of the revspec within the module
361
cls._possible_revspecs.append(
362
registry._LazyObjectGetter(module_name, member_name))
365
293
class RevisionSpec_revno(RevisionSpec):
366
294
"""Selects a revision using a number."""
368
296
help_txt = """Selects a revision using a number.
370
298
Use an integer to specify a revision in the history of the branch.
371
Optionally a branch can be specified. A negative number will count
372
from the end of the branch (-1 is the last revision, -2 the previous
373
one). If the negative number is larger than the branch's history, the
374
first revision is returned.
299
Optionally a branch can be specified. The 'revno:' prefix is optional.
300
A negative number will count from the end of the branch (-1 is the
301
last revision, -2 the previous one). If the negative number is larger
302
than the branch's history, the first revision is returned.
377
305
revno:1 -> return the first revision of this branch
384
312
your history is very long.
386
314
prefix = 'revno:'
315
wants_revision_history = False
388
317
def _match_on(self, branch, revs):
389
318
"""Lookup a revision by revision number"""
390
branch, revno, revision_id = self._lookup(branch)
319
branch, revno, revision_id = self._lookup(branch, revs)
391
320
return RevisionInfo(branch, revno, revision_id)
393
def _lookup(self, branch):
322
def _lookup(self, branch, revs_or_none):
394
323
loc = self.spec.find(':')
396
325
revno_spec = self.spec
397
326
branch_spec = None
399
328
revno_spec = self.spec[:loc]
400
branch_spec = self.spec[loc + 1:]
329
branch_spec = self.spec[loc+1:]
402
331
if revno_spec == '':
403
332
if not branch_spec:
404
raise InvalidRevisionSpec(
405
self.user_spec, branch,
406
'cannot have an empty revno and no branch')
333
raise errors.InvalidRevisionSpec(self.user_spec,
334
branch, 'cannot have an empty revno and no branch')
414
342
# but the from_string method is a little primitive
415
343
# right now - RBC 20060928
417
match_revno = tuple((int(number)
418
for number in revno_spec.split('.')))
419
except ValueError as e:
420
raise InvalidRevisionSpec(self.user_spec, branch, e)
345
match_revno = tuple((int(number) for number in revno_spec.split('.')))
346
except ValueError, e:
347
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
425
# the user has overriden the branch to look in.
426
branch = _mod_branch.Branch.open(branch_spec)
352
# the user has override the branch to look in.
353
# we need to refresh the revision_history map and
355
from bzrlib.branch import Branch
356
branch = Branch.open(branch_spec)
430
361
revision_id = branch.dotted_revno_to_revision_id(match_revno,
432
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
433
raise InvalidRevisionSpec(self.user_spec, branch)
363
except errors.NoSuchRevision:
364
raise errors.InvalidRevisionSpec(self.user_spec, branch)
435
366
# there is no traditional 'revno' for dotted-decimal revnos.
436
# so for API compatibility we return None.
367
# so for API compatability we return None.
437
368
return branch, None, revision_id
439
370
last_revno, last_revision_id = branch.last_revision_info()
446
377
revno = last_revno + revno + 1
448
revision_id = branch.get_rev_id(revno)
449
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
450
raise InvalidRevisionSpec(self.user_spec, branch)
379
revision_id = branch.get_rev_id(revno, revs_or_none)
380
except errors.NoSuchRevision:
381
raise errors.InvalidRevisionSpec(self.user_spec, branch)
451
382
return branch, revno, revision_id
453
384
def _as_revision_id(self, context_branch):
454
385
# We would have the revno here, but we don't really care
455
branch, revno, revision_id = self._lookup(context_branch)
386
branch, revno, revision_id = self._lookup(context_branch, None)
456
387
return revision_id
458
389
def needs_branch(self):
462
393
if self.spec.find(':') == -1:
465
return self.spec[self.spec.find(':') + 1:]
396
return self.spec[self.spec.find(':')+1:]
468
398
# Old compatibility
469
399
RevisionSpec_int = RevisionSpec_revno
472
class RevisionIDSpec(RevisionSpec):
474
def _match_on(self, branch, revs):
475
revision_id = self.as_revision_id(branch)
476
return RevisionInfo.from_revision_id(branch, revision_id)
479
class RevisionSpec_revid(RevisionIDSpec):
403
class RevisionSpec_revid(RevisionSpec):
480
404
"""Selects a revision using the revision id."""
482
406
help_txt = """Selects a revision using the revision id.
492
416
prefix = 'revid:'
494
def _as_revision_id(self, context_branch):
418
def _match_on(self, branch, revs):
495
419
# self.spec comes straight from parsing the command line arguments,
496
420
# so we expect it to be a Unicode string. Switch it to the internal
497
421
# representation.
498
if isinstance(self.spec, str):
499
return cache_utf8.encode(self.spec)
422
revision_id = osutils.safe_revision_id(self.spec, warn=False)
423
return RevisionInfo.from_revision_id(branch, revision_id, revs)
425
def _as_revision_id(self, context_branch):
426
return osutils.safe_revision_id(self.spec, warn=False)
503
430
class RevisionSpec_last(RevisionSpec):
531
458
offset = int(self.spec)
532
except ValueError as e:
533
raise InvalidRevisionSpec(self.user_spec, context_branch, e)
459
except ValueError, e:
460
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')
463
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
464
'you must supply a positive value')
540
466
revno = last_revno - offset + 1
542
revision_id = context_branch.get_rev_id(revno)
543
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
544
raise InvalidRevisionSpec(self.user_spec, context_branch)
468
revision_id = context_branch.get_rev_id(revno, revs_or_none)
469
except errors.NoSuchRevision:
470
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
545
471
return revno, revision_id
547
473
def _as_revision_id(self, context_branch):
548
474
# We compute the revno as part of the process, but we don't really care
550
revno, revision_id = self._revno_and_revision_id(context_branch)
476
revno, revision_id = self._revno_and_revision_id(context_branch, None)
551
477
return revision_id
554
481
class RevisionSpec_before(RevisionSpec):
555
482
"""Selects the parent of the revision specified."""
578
505
def _match_on(self, branch, revs):
579
506
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')
508
raise errors.InvalidRevisionSpec(self.user_spec, branch,
509
'cannot go before the null: revision')
584
510
if r.revno is None:
585
511
# We need to use the repository history here
586
512
rev = branch.repository.get_revision(r.rev_id)
587
513
if not rev.parent_ids:
588
515
revision_id = revision.NULL_REVISION
590
517
revision_id = rev.parent_ids[0]
519
revno = revs.index(revision_id) + 1
593
523
revno = r.revno - 1
595
525
revision_id = branch.get_rev_id(revno, revs)
596
except (errors.NoSuchRevision, errors.RevnoOutOfBounds):
597
raise InvalidRevisionSpec(self.user_spec, branch)
526
except errors.NoSuchRevision:
527
raise errors.InvalidRevisionSpec(self.user_spec,
598
529
return RevisionInfo(branch, revno, revision_id)
600
531
def _as_revision_id(self, context_branch):
601
base_revision_id = RevisionSpec.from_string(
602
self.spec)._as_revision_id(context_branch)
532
base_revspec = RevisionSpec.from_string(self.spec)
533
base_revision_id = base_revspec.as_revision_id(context_branch)
603
534
if base_revision_id == revision.NULL_REVISION:
604
raise InvalidRevisionSpec(
605
self.user_spec, context_branch,
606
'cannot go before the null: revision')
535
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
536
'cannot go before the null: revision')
607
537
context_repo = context_branch.repository
608
with context_repo.lock_read():
538
context_repo.lock_read()
609
540
parent_map = context_repo.get_parent_map([base_revision_id])
542
context_repo.unlock()
610
543
if base_revision_id not in parent_map:
611
544
# Ghost, or unknown revision id
612
raise InvalidRevisionSpec(
613
self.user_spec, context_branch, 'cannot find the matching revision')
545
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
546
'cannot find the matching revision')
614
547
parents = parent_map[base_revision_id]
615
548
if len(parents) < 1:
616
raise InvalidRevisionSpec(
617
self.user_spec, context_branch, 'No parents for revision.')
549
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
550
'No parents for revision.')
618
551
return parents[0]
621
555
class RevisionSpec_tag(RevisionSpec):
622
556
"""Select a revision identified by tag name"""
630
dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
632
565
def _match_on(self, branch, revs):
633
566
# Can raise tags not supported, NoSuchTag, etc
634
567
return RevisionInfo.from_revision_id(branch,
635
branch.tags.lookup_tag(self.spec))
568
branch.tags.lookup_tag(self.spec),
637
571
def _as_revision_id(self, context_branch):
638
572
return context_branch.tags.lookup_tag(self.spec)
641
576
class _RevListToTimestamps(object):
642
577
"""This takes a list of revisions, and allows you to bisect by date"""
644
__slots__ = ['branch']
579
__slots__ = ['revs', 'branch']
646
def __init__(self, branch):
581
def __init__(self, revs, branch):
647
583
self.branch = branch
649
585
def __getitem__(self, index):
650
586
"""Get the date of the index'd item"""
651
r = self.branch.repository.get_revision(self.branch.get_rev_id(index))
587
r = self.branch.repository.get_revision(self.revs[index])
652
588
# TODO: Handle timezone.
653
589
return datetime.datetime.fromtimestamp(r.timestamp)
655
591
def __len__(self):
656
return self.branch.revno()
592
return len(self.revs)
659
595
class RevisionSpec_date(RevisionSpec):
728
hour, minute, second = 0, 0, 0
663
hour, minute, second = 0,0,0
729
664
except ValueError:
730
raise InvalidRevisionSpec(
731
self.user_spec, branch, 'invalid date')
665
raise errors.InvalidRevisionSpec(self.user_spec,
666
branch, 'invalid date')
733
668
dt = datetime.datetime(year=year, month=month, day=day,
734
hour=hour, minute=minute, second=second)
735
with branch.lock_read():
736
rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
737
if rev == branch.revno():
738
raise InvalidRevisionSpec(self.user_spec, branch)
739
return RevisionInfo(branch, rev)
669
hour=hour, minute=minute, second=second)
672
rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
676
raise errors.InvalidRevisionSpec(self.user_spec, branch)
678
return RevisionInfo(branch, rev + 1)
742
682
class RevisionSpec_ancestor(RevisionSpec):
772
712
def _find_revision_info(branch, other_location):
773
713
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
775
return RevisionInfo(branch, None, revision_id)
716
revno = branch.revision_id_to_revno(revision_id)
717
except errors.NoSuchRevision:
719
return RevisionInfo(branch, revno, revision_id)
778
722
def _find_revision_id(branch, other_location):
779
from .branch import Branch
723
from bzrlib.branch import Branch
781
with branch.lock_read():
782
727
revision_a = revision.ensure_null(branch.last_revision())
783
728
if revision_a == revision.NULL_REVISION:
784
729
raise errors.NoCommits(branch)
785
730
if other_location == '':
786
731
other_location = branch.get_parent()
787
732
other_branch = Branch.open(other_location)
788
with other_branch.lock_read():
733
other_branch.lock_read()
789
735
revision_b = revision.ensure_null(other_branch.last_revision())
790
736
if revision_b == revision.NULL_REVISION:
791
737
raise errors.NoCommits(other_branch)
792
738
graph = branch.repository.get_graph(other_branch.repository)
793
739
rev_id = graph.find_unique_lca(revision_a, revision_b)
741
other_branch.unlock()
794
742
if rev_id == revision.NULL_REVISION:
795
743
raise errors.NoCommonAncestor(revision_a, revision_b)
799
751
class RevisionSpec_branch(RevisionSpec):
808
760
branch:/path/to/branch
810
762
prefix = 'branch:'
811
dwim_catchable_exceptions = (errors.NotBranchError,)
813
764
def _match_on(self, branch, revs):
814
from .branch import Branch
765
from bzrlib.branch import Branch
815
766
other_branch = Branch.open(self.spec)
816
767
revision_b = other_branch.last_revision()
817
768
if revision_b in (None, revision.NULL_REVISION):
818
769
raise errors.NoCommits(other_branch)
820
branch = other_branch
823
# pull in the remote revisions so we can diff
824
branch.fetch(other_branch, revision_b)
825
except errors.ReadOnlyError:
826
branch = other_branch
827
return RevisionInfo(branch, None, revision_b)
770
# pull in the remote revisions so we can diff
771
branch.fetch(other_branch, revision_b)
773
revno = branch.revision_id_to_revno(revision_b)
774
except errors.NoSuchRevision:
776
return RevisionInfo(branch, revno, revision_b)
829
778
def _as_revision_id(self, context_branch):
830
from .branch import Branch
779
from bzrlib.branch import Branch
831
780
other_branch = Branch.open(self.spec)
832
781
last_revision = other_branch.last_revision()
833
782
last_revision = revision.ensure_null(last_revision)
881
825
location_type = 'parent branch'
882
826
if submit_location is None:
883
827
raise errors.NoSubmitBranch(branch)
884
trace.note(gettext('Using {0} {1}').format(location_type,
828
trace.note('Using %s %s', location_type, submit_location)
886
829
return submit_location
888
831
def _match_on(self, branch, revs):
889
832
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
890
833
return self._find_revision_info(branch,
891
self._get_submit_location(branch))
834
self._get_submit_location(branch))
893
836
def _as_revision_id(self, context_branch):
894
837
return self._find_revision_id(context_branch,
895
self._get_submit_location(context_branch))
898
class RevisionSpec_annotate(RevisionIDSpec):
902
help_txt = """Select the revision that last modified the specified line.
904
Select the revision that last modified the specified line. Line is
905
specified as path:number. Path is a relative path to the file. Numbers
906
start at 1, and are relative to the current version, not the last-
907
committed version of the file.
910
def _raise_invalid(self, numstring, context_branch):
911
raise InvalidRevisionSpec(
912
self.user_spec, context_branch,
913
'No such line: %s' % numstring)
915
def _as_revision_id(self, context_branch):
916
path, numstring = self.spec.rsplit(':', 1)
918
index = int(numstring) - 1
920
self._raise_invalid(numstring, context_branch)
921
tree, file_path = workingtree.WorkingTree.open_containing(path)
922
with tree.lock_read():
923
if not tree.has_filename(file_path):
924
raise InvalidRevisionSpec(
925
self.user_spec, context_branch,
926
"File '%s' is not versioned." % file_path)
927
revision_ids = [r for (r, l) in tree.annotate_iter(file_path)]
929
revision_id = revision_ids[index]
931
self._raise_invalid(numstring, context_branch)
932
if revision_id == revision.CURRENT_REVISION:
933
raise InvalidRevisionSpec(
934
self.user_spec, context_branch,
935
'Line %s has not been committed.' % numstring)
939
class RevisionSpec_mainline(RevisionIDSpec):
941
help_txt = """Select mainline revision that merged the specified revision.
943
Select the revision that merged the specified revision into mainline.
948
def _as_revision_id(self, context_branch):
949
revspec = RevisionSpec.from_string(self.spec)
950
if revspec.get_branch() is None:
951
spec_branch = context_branch
953
spec_branch = _mod_branch.Branch.open(revspec.get_branch())
954
revision_id = revspec.as_revision_id(spec_branch)
955
graph = context_branch.repository.get_graph()
956
result = graph.find_lefthand_merger(revision_id,
957
context_branch.last_revision())
959
raise InvalidRevisionSpec(self.user_spec, context_branch)
963
# The order in which we want to DWIM a revision spec without any prefix.
964
# revno is always tried first and isn't listed here, this is used by
965
# RevisionSpec_dwim._match_on
966
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
967
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
968
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
969
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
838
self._get_submit_location(context_branch))
971
841
revspec_registry = registry.Registry()
974
842
def _register_revspec(revspec):
975
843
revspec_registry.register(revspec.prefix, revspec)
978
845
_register_revspec(RevisionSpec_revno)
979
846
_register_revspec(RevisionSpec_revid)
980
847
_register_revspec(RevisionSpec_last)