141
wants_revision_history = True
142
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
143
"""Exceptions that RevisionSpec_dwim._match_on will catch.
145
If the revspec is part of ``dwim_revspecs``, it may be tried with an
146
invalid revspec and raises some exception. The exceptions mentioned here
147
will not be reported to the user but simply ignored without stopping the
152
def from_string(spec):
153
"""Parse a revision spec string into a RevisionSpec object.
155
:param spec: A string specified by the user
156
:return: A RevisionSpec object that understands how to parse the
108
def __new__(cls, spec, foo=_marker):
109
"""Parse a revision specifier.
159
if not isinstance(spec, (type(None), basestring)):
160
raise TypeError('error')
163
return RevisionSpec(None, _internal=True)
164
match = revspec_registry.get_prefix(spec)
165
if match is not None:
166
spectype, specsuffix = match
167
trace.mutter('Returning RevisionSpec %s for %s',
168
spectype.__name__, spec)
169
return spectype(spec, _internal=True)
112
return object.__new__(RevisionSpec, spec)
119
if isinstance(spec, int):
120
return object.__new__(RevisionSpec_int, spec)
121
elif isinstance(spec, basestring):
171
122
for spectype in SPEC_TYPES:
172
123
if spec.startswith(spectype.prefix):
173
trace.mutter('Returning RevisionSpec %s for %s',
174
spectype.__name__, spec)
175
return spectype(spec, _internal=True)
176
# Otherwise treat it as a DWIM, build the RevisionSpec object and
177
# wait for _match_on to be called.
178
return RevisionSpec_dwim(spec, _internal=True)
180
def __init__(self, spec, _internal=False):
181
"""Create a RevisionSpec referring to the Null revision.
183
:param spec: The original spec supplied by the user
184
:param _internal: Used to ensure that RevisionSpec is not being
185
called directly. Only from RevisionSpec.from_string()
188
symbol_versioning.warn('Creating a RevisionSpec directly has'
189
' been deprecated in version 0.11. Use'
190
' RevisionSpec.from_string()'
192
DeprecationWarning, stacklevel=2)
193
self.user_spec = spec
124
return object.__new__(spectype, spec)
126
raise BzrError('No namespace registered for string: %r' %
129
raise TypeError('Unhandled revision type %s' % spec)
131
def __init__(self, spec):
194
132
if self.prefix and spec.startswith(self.prefix):
195
133
spec = spec[len(self.prefix):]
198
136
def _match_on(self, branch, revs):
199
trace.mutter('Returning RevisionSpec._match_on: None')
200
return RevisionInfo(branch, None, None)
137
return RevisionInfo(branch, 0, None)
202
139
def _match_on_and_check(self, branch, revs):
203
140
info = self._match_on(branch, revs)
206
elif info == (None, None):
207
# special case - nothing supplied
143
elif info == (0, None):
144
# special case - the empty tree
209
146
elif self.prefix:
210
raise errors.InvalidRevisionSpec(self.user_spec, branch)
147
raise NoSuchRevision(branch, self.prefix + str(self.spec))
212
raise errors.InvalidRevisionSpec(self.spec, branch)
149
raise NoSuchRevision(branch, str(self.spec))
214
151
def in_history(self, branch):
216
if self.wants_revision_history:
217
revs = branch.revision_history()
153
revs = branch.revision_history()
221
# this should never trigger.
222
# TODO: make it a deprecated code path. RBC 20060928
224
156
return self._match_on_and_check(branch, revs)
232
164
# will do what you expect.
233
165
in_store = in_history
234
166
in_branch = in_store
236
def as_revision_id(self, context_branch):
237
"""Return just the revision_id for this revisions spec.
239
Some revision specs require a context_branch to be able to determine
240
their value. Not all specs will make use of it.
242
return self._as_revision_id(context_branch)
244
def _as_revision_id(self, context_branch):
245
"""Implementation of as_revision_id()
247
Classes should override this function to provide appropriate
248
functionality. The default is to just call '.in_history().rev_id'
250
return self.in_history(context_branch).rev_id
252
def as_tree(self, context_branch):
253
"""Return the tree object for this revisions spec.
255
Some revision specs require a context_branch to be able to determine
256
the revision id and access the repository. Not all specs will make
259
return self._as_tree(context_branch)
261
def _as_tree(self, context_branch):
262
"""Implementation of as_tree().
264
Classes should override this function to provide appropriate
265
functionality. The default is to just call '.as_revision_id()'
266
and get the revision tree from context_branch's repository.
268
revision_id = self.as_revision_id(context_branch)
269
return context_branch.repository.revision_tree(revision_id)
271
168
def __repr__(self):
272
169
# this is mostly for helping with testing
273
return '<%s %s>' % (self.__class__.__name__,
276
def needs_branch(self):
277
"""Whether this revision spec needs a branch.
279
Set this to False the branch argument of _match_on is not used.
283
def get_branch(self):
284
"""When the revision specifier contains a branch location, return it.
286
Otherwise, return None.
170
return '<%s %s%s>' % (self.__class__.__name__,
293
class RevisionSpec_dwim(RevisionSpec):
294
"""Provides a DWIMish revision specifier lookup.
296
Note that this does not go in the revspec_registry because by definition
297
there is no prefix to identify it. It's solely called from
298
RevisionSpec.from_string() because the DWIMification happen when _match_on
299
is called so the string describing the revision is kept here until needed.
303
# We don't need to build the revision history ourself, that's delegated to
304
# each revspec we try.
305
wants_revision_history = False
307
def _try_spectype(self, rstype, branch):
308
rs = rstype(self.spec, _internal=True)
309
# Hit in_history to find out if it exists, or we need to try the
311
return rs.in_history(branch)
177
class RevisionSpec_int(RevisionSpec):
178
"""Spec is a number. Special case."""
179
def __init__(self, spec):
180
self.spec = int(spec)
313
182
def _match_on(self, branch, revs):
314
"""Run the lookup and see what we can get."""
316
# First, see if it's a revno
318
if _revno_regex is None:
319
_revno_regex = re.compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
320
if _revno_regex.match(self.spec) is not None:
322
return self._try_spectype(RevisionSpec_revno, branch)
323
except RevisionSpec_revno.dwim_catchable_exceptions:
326
# Next see what has been registered
327
for rs_class in dwim_revspecs:
329
return self._try_spectype(rs_class, branch)
330
except rs_class.dwim_catchable_exceptions:
333
# Well, I dunno what it is. Note that we don't try to keep track of the
334
# first of last exception raised during the DWIM tries as none seems
336
raise errors.InvalidRevisionSpec(self.spec, branch)
184
revno = len(revs) + self.spec + 1
187
rev_id = branch.get_rev_id(revno, revs)
188
return RevisionInfo(branch, revno, rev_id)
339
191
class RevisionSpec_revno(RevisionSpec):
340
"""Selects a revision using a number."""
342
help_txt = """Selects a revision using a number.
344
Use an integer to specify a revision in the history of the branch.
345
Optionally a branch can be specified. A negative number will count
346
from the end of the branch (-1 is the last revision, -2 the previous
347
one). If the negative number is larger than the branch's history, the
348
first revision is returned.
351
revno:1 -> return the first revision of this branch
352
revno:3:/path/to/branch -> return the 3rd revision of
353
the branch '/path/to/branch'
354
revno:-1 -> The last revision in a branch.
355
-2:http://other/branch -> The second to last revision in the
357
-1000000 -> Most likely the first revision, unless
358
your history is very long.
360
192
prefix = 'revno:'
361
wants_revision_history = False
363
194
def _match_on(self, branch, revs):
364
195
"""Lookup a revision by revision number"""
365
branch, revno, revision_id = self._lookup(branch, revs)
366
return RevisionInfo(branch, revno, revision_id)
368
def _lookup(self, branch, revs_or_none):
369
loc = self.spec.find(':')
371
revno_spec = self.spec
374
revno_spec = self.spec[:loc]
375
branch_spec = self.spec[loc+1:]
379
raise errors.InvalidRevisionSpec(self.user_spec,
380
branch, 'cannot have an empty revno and no branch')
384
revno = int(revno_spec)
387
# dotted decimal. This arguably should not be here
388
# but the from_string method is a little primitive
389
# right now - RBC 20060928
391
match_revno = tuple((int(number) for number in revno_spec.split('.')))
392
except ValueError, e:
393
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
398
# the user has override the branch to look in.
399
# we need to refresh the revision_history map and
401
from bzrlib.branch import Branch
402
branch = Branch.open(branch_spec)
407
revision_id = branch.dotted_revno_to_revision_id(match_revno,
409
except errors.NoSuchRevision:
410
raise errors.InvalidRevisionSpec(self.user_spec, branch)
412
# there is no traditional 'revno' for dotted-decimal revnos.
413
# so for API compatability we return None.
414
return branch, None, revision_id
416
last_revno, last_revision_id = branch.last_revision_info()
418
# if get_rev_id supported negative revnos, there would not be a
419
# need for this special case.
420
if (-revno) >= last_revno:
423
revno = last_revno + revno + 1
425
revision_id = branch.get_rev_id(revno, revs_or_none)
426
except errors.NoSuchRevision:
427
raise errors.InvalidRevisionSpec(self.user_spec, branch)
428
return branch, revno, revision_id
430
def _as_revision_id(self, context_branch):
431
# We would have the revno here, but we don't really care
432
branch, revno, revision_id = self._lookup(context_branch, None)
435
def needs_branch(self):
436
return self.spec.find(':') == -1
438
def get_branch(self):
439
196
if self.spec.find(':') == -1:
198
return RevisionInfo(branch, int(self.spec))
200
return RevisionInfo(branch, None)
442
return self.spec[self.spec.find(':')+1:]
445
RevisionSpec_int = RevisionSpec_revno
449
class RevisionIDSpec(RevisionSpec):
451
def _match_on(self, branch, revs):
452
revision_id = self.as_revision_id(branch)
453
return RevisionInfo.from_revision_id(branch, revision_id, revs)
456
class RevisionSpec_revid(RevisionIDSpec):
457
"""Selects a revision using the revision id."""
459
help_txt = """Selects a revision using the revision id.
461
Supply a specific revision id, that can be used to specify any
462
revision id in the ancestry of the branch.
463
Including merges, and pending merges.
466
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
202
from branch import Branch
203
revname = self.spec[self.spec.find(':')+1:]
204
other_branch = Branch.open_containing(revname)[0]
206
revno = int(self.spec[:self.spec.find(':')])
208
return RevisionInfo(other_branch, None)
209
revid = other_branch.get_rev_id(revno)
210
return RevisionInfo(other_branch, revno)
212
SPEC_TYPES.append(RevisionSpec_revno)
215
class RevisionSpec_revid(RevisionSpec):
469
216
prefix = 'revid:'
471
def _as_revision_id(self, context_branch):
472
# self.spec comes straight from parsing the command line arguments,
473
# so we expect it to be a Unicode string. Switch it to the internal
475
return osutils.safe_revision_id(self.spec, warn=False)
218
def _match_on(self, branch, revs):
220
return RevisionInfo(branch, revs.index(self.spec) + 1, self.spec)
222
return RevisionInfo(branch, None, self.spec)
224
SPEC_TYPES.append(RevisionSpec_revid)
479
227
class RevisionSpec_last(RevisionSpec):
480
"""Selects the nth revision from the end."""
482
help_txt = """Selects the nth revision from the end.
484
Supply a positive number to get the nth revision from the end.
485
This is the same as supplying negative numbers to the 'revno:' spec.
488
last:1 -> return the last revision
489
last:3 -> return the revision 2 before the end.
494
231
def _match_on(self, branch, revs):
495
revno, revision_id = self._revno_and_revision_id(branch, revs)
496
return RevisionInfo(branch, revno, revision_id)
498
def _revno_and_revision_id(self, context_branch, revs_or_none):
499
last_revno, last_revision_id = context_branch.last_revision_info()
503
raise errors.NoCommits(context_branch)
504
return last_revno, last_revision_id
507
233
offset = int(self.spec)
508
except ValueError, e:
509
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
512
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
513
'you must supply a positive value')
515
revno = last_revno - offset + 1
517
revision_id = context_branch.get_rev_id(revno, revs_or_none)
518
except errors.NoSuchRevision:
519
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
520
return revno, revision_id
522
def _as_revision_id(self, context_branch):
523
# We compute the revno as part of the process, but we don't really care
525
revno, revision_id = self._revno_and_revision_id(context_branch, None)
235
return RevisionInfo(branch, None)
238
raise BzrError('You must supply a positive value for --revision last:XXX')
239
return RevisionInfo(branch, len(revs) - offset + 1)
241
SPEC_TYPES.append(RevisionSpec_last)
530
244
class RevisionSpec_before(RevisionSpec):
531
"""Selects the parent of the revision specified."""
533
help_txt = """Selects the parent of the revision specified.
535
Supply any revision spec to return the parent of that revision. This is
536
mostly useful when inspecting revisions that are not in the revision history
539
It is an error to request the parent of the null revision (before:0).
543
before:1913 -> Return the parent of revno 1913 (revno 1912)
544
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
546
bzr diff -r before:1913..1913
547
-> Find the changes between revision 1913 and its parent (1912).
548
(What changes did revision 1913 introduce).
549
This is equivalent to: bzr diff -c 1913
552
246
prefix = 'before:'
554
248
def _match_on(self, branch, revs):
555
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
557
raise errors.InvalidRevisionSpec(self.user_spec, branch,
558
'cannot go before the null: revision')
560
# We need to use the repository history here
561
rev = branch.repository.get_revision(r.rev_id)
562
if not rev.parent_ids:
564
revision_id = revision.NULL_REVISION
566
revision_id = rev.parent_ids[0]
568
revno = revs.index(revision_id) + 1
574
revision_id = branch.get_rev_id(revno, revs)
575
except errors.NoSuchRevision:
576
raise errors.InvalidRevisionSpec(self.user_spec,
578
return RevisionInfo(branch, revno, revision_id)
580
def _as_revision_id(self, context_branch):
581
base_revspec = RevisionSpec.from_string(self.spec)
582
base_revision_id = base_revspec.as_revision_id(context_branch)
583
if base_revision_id == revision.NULL_REVISION:
584
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
585
'cannot go before the null: revision')
586
context_repo = context_branch.repository
587
context_repo.lock_read()
589
parent_map = context_repo.get_parent_map([base_revision_id])
591
context_repo.unlock()
592
if base_revision_id not in parent_map:
593
# Ghost, or unknown revision id
594
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
595
'cannot find the matching revision')
596
parents = parent_map[base_revision_id]
598
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
599
'No parents for revision.')
249
r = RevisionSpec(self.spec)._match_on(branch, revs)
250
if (r.revno is None) or (r.revno == 0):
252
return RevisionInfo(branch, r.revno - 1)
254
SPEC_TYPES.append(RevisionSpec_before)
604
257
class RevisionSpec_tag(RevisionSpec):
605
"""Select a revision identified by tag name"""
607
help_txt = """Selects a revision identified by a tag name.
609
Tags are stored in the branch and created by the 'tag' command.
613
dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
615
260
def _match_on(self, branch, revs):
616
# Can raise tags not supported, NoSuchTag, etc
617
return RevisionInfo.from_revision_id(branch,
618
branch.tags.lookup_tag(self.spec),
621
def _as_revision_id(self, context_branch):
622
return context_branch.tags.lookup_tag(self.spec)
626
class _RevListToTimestamps(object):
627
"""This takes a list of revisions, and allows you to bisect by date"""
629
__slots__ = ['revs', 'branch']
261
raise BzrError('tag: namespace registered, but not implemented.')
263
SPEC_TYPES.append(RevisionSpec_tag)
266
class RevisionSpec_revs:
631
267
def __init__(self, revs, branch):
633
269
self.branch = branch
635
270
def __getitem__(self, index):
636
"""Get the date of the index'd item"""
637
271
r = self.branch.repository.get_revision(self.revs[index])
638
272
# TODO: Handle timezone.
639
273
return datetime.datetime.fromtimestamp(r.timestamp)
641
274
def __len__(self):
642
275
return len(self.revs)
645
278
class RevisionSpec_date(RevisionSpec):
646
"""Selects a revision on the basis of a datestamp."""
648
help_txt = """Selects a revision on the basis of a datestamp.
650
Supply a datestamp to select the first revision that matches the date.
651
Date can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
652
Matches the first entry after a given date (either at midnight or
653
at a specified time).
655
One way to display all the changes since yesterday would be::
657
bzr log -r date:yesterday..
661
date:yesterday -> select the first revision since yesterday
662
date:2006-08-14,17:10:14 -> select the first revision after
663
August 14th, 2006 at 5:10pm.
666
280
_date_re = re.compile(
667
281
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
690
305
m = self._date_re.match(self.spec)
691
306
if not m or (not m.group('date') and not m.group('time')):
692
raise errors.InvalidRevisionSpec(self.user_spec,
693
branch, 'invalid date')
697
year = int(m.group('year'))
698
month = int(m.group('month'))
699
day = int(m.group('day'))
706
hour = int(m.group('hour'))
707
minute = int(m.group('minute'))
708
if m.group('second'):
709
second = int(m.group('second'))
713
hour, minute, second = 0,0,0
715
raise errors.InvalidRevisionSpec(self.user_spec,
716
branch, 'invalid date')
307
raise BzrError('Invalid revision date %r' % self.spec)
310
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
312
year, month, day = today.year, today.month, today.day
314
hour = int(m.group('hour'))
315
minute = int(m.group('minute'))
316
if m.group('second'):
317
second = int(m.group('second'))
321
hour, minute, second = 0,0,0
718
323
dt = datetime.datetime(year=year, month=month, day=day,
719
324
hour=hour, minute=minute, second=second)
720
325
branch.lock_read()
722
rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
327
rev = bisect.bisect(RevisionSpec_revs(revs, branch), dt)
725
330
if rev == len(revs):
726
raise errors.InvalidRevisionSpec(self.user_spec, branch)
331
return RevisionInfo(branch, None)
728
333
return RevisionInfo(branch, rev + 1)
335
SPEC_TYPES.append(RevisionSpec_date)
732
338
class RevisionSpec_ancestor(RevisionSpec):
733
"""Selects a common ancestor with a second branch."""
735
help_txt = """Selects a common ancestor with a second branch.
737
Supply the path to a branch to select the common ancestor.
739
The common ancestor is the last revision that existed in both
740
branches. Usually this is the branch point, but it could also be
741
a revision that was merged.
743
This is frequently used with 'diff' to return all of the changes
744
that your branch introduces, while excluding the changes that you
745
have not merged from the remote branch.
749
ancestor:/path/to/branch
750
$ bzr diff -r ancestor:../../mainline/branch
752
339
prefix = 'ancestor:'
754
341
def _match_on(self, branch, revs):
755
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
756
return self._find_revision_info(branch, self.spec)
758
def _as_revision_id(self, context_branch):
759
return self._find_revision_id(context_branch, self.spec)
762
def _find_revision_info(branch, other_location):
763
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
342
from branch import Branch
343
from revision import common_ancestor, MultipleRevisionSources
344
other_branch = Branch.open_containing(self.spec)[0]
345
revision_a = branch.last_revision()
346
revision_b = other_branch.last_revision()
347
for r, b in ((revision_a, branch), (revision_b, other_branch)):
350
revision_source = MultipleRevisionSources(branch.repository,
351
other_branch.repository)
352
rev_id = common_ancestor(revision_a, revision_b, revision_source)
766
revno = branch.revision_id_to_revno(revision_id)
767
except errors.NoSuchRevision:
354
revno = branch.revision_id_to_revno(rev_id)
355
except NoSuchRevision:
769
return RevisionInfo(branch, revno, revision_id)
772
def _find_revision_id(branch, other_location):
773
from bzrlib.branch import Branch
777
revision_a = revision.ensure_null(branch.last_revision())
778
if revision_a == revision.NULL_REVISION:
779
raise errors.NoCommits(branch)
780
if other_location == '':
781
other_location = branch.get_parent()
782
other_branch = Branch.open(other_location)
783
other_branch.lock_read()
785
revision_b = revision.ensure_null(other_branch.last_revision())
786
if revision_b == revision.NULL_REVISION:
787
raise errors.NoCommits(other_branch)
788
graph = branch.repository.get_graph(other_branch.repository)
789
rev_id = graph.find_unique_lca(revision_a, revision_b)
791
other_branch.unlock()
792
if rev_id == revision.NULL_REVISION:
793
raise errors.NoCommonAncestor(revision_a, revision_b)
357
return RevisionInfo(branch, revno, rev_id)
359
SPEC_TYPES.append(RevisionSpec_ancestor)
801
361
class RevisionSpec_branch(RevisionSpec):
802
"""Selects the last revision of a specified branch."""
804
help_txt = """Selects the last revision of a specified branch.
806
Supply the path to a branch to select its last revision.
810
branch:/path/to/branch
362
"""A branch: revision specifier.
364
This takes the path to a branch and returns its tip revision id.
812
366
prefix = 'branch:'
813
dwim_catchable_exceptions = (errors.NotBranchError,)
815
368
def _match_on(self, branch, revs):
816
from bzrlib.branch import Branch
817
other_branch = Branch.open(self.spec)
369
from branch import Branch
370
other_branch = Branch.open_containing(self.spec)[0]
818
371
revision_b = other_branch.last_revision()
819
if revision_b in (None, revision.NULL_REVISION):
820
raise errors.NoCommits(other_branch)
822
branch = other_branch
825
# pull in the remote revisions so we can diff
826
branch.fetch(other_branch, revision_b)
827
except errors.ReadOnlyError:
828
branch = other_branch
372
if revision_b is None:
373
raise NoCommits(other_branch)
374
# pull in the remote revisions so we can diff
375
branch.fetch(other_branch, revision_b)
830
377
revno = branch.revision_id_to_revno(revision_b)
831
except errors.NoSuchRevision:
378
except NoSuchRevision:
833
380
return RevisionInfo(branch, revno, revision_b)
835
def _as_revision_id(self, context_branch):
836
from bzrlib.branch import Branch
837
other_branch = Branch.open(self.spec)
838
last_revision = other_branch.last_revision()
839
last_revision = revision.ensure_null(last_revision)
840
context_branch.fetch(other_branch, last_revision)
841
if last_revision == revision.NULL_REVISION:
842
raise errors.NoCommits(other_branch)
845
def _as_tree(self, context_branch):
846
from bzrlib.branch import Branch
847
other_branch = Branch.open(self.spec)
848
last_revision = other_branch.last_revision()
849
last_revision = revision.ensure_null(last_revision)
850
if last_revision == revision.NULL_REVISION:
851
raise errors.NoCommits(other_branch)
852
return other_branch.repository.revision_tree(last_revision)
854
def needs_branch(self):
857
def get_branch(self):
862
class RevisionSpec_submit(RevisionSpec_ancestor):
863
"""Selects a common ancestor with a submit branch."""
865
help_txt = """Selects a common ancestor with the submit branch.
867
Diffing against this shows all the changes that were made in this branch,
868
and is a good predictor of what merge will do. The submit branch is
869
used by the bundle and merge directive commands. If no submit branch
870
is specified, the parent branch is used instead.
872
The common ancestor is the last revision that existed in both
873
branches. Usually this is the branch point, but it could also be
874
a revision that was merged.
878
$ bzr diff -r submit:
883
def _get_submit_location(self, branch):
884
submit_location = branch.get_submit_branch()
885
location_type = 'submit branch'
886
if submit_location is None:
887
submit_location = branch.get_parent()
888
location_type = 'parent branch'
889
if submit_location is None:
890
raise errors.NoSubmitBranch(branch)
891
trace.note('Using %s %s', location_type, submit_location)
892
return submit_location
894
def _match_on(self, branch, revs):
895
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
896
return self._find_revision_info(branch,
897
self._get_submit_location(branch))
899
def _as_revision_id(self, context_branch):
900
return self._find_revision_id(context_branch,
901
self._get_submit_location(context_branch))
904
class RevisionSpec_annotate(RevisionIDSpec):
908
help_txt = """Select the revision that last modified the specified line.
910
Select the revision that last modified the specified line. Line is
911
specified as path:number. Path is a relative path to the file. Numbers
912
start at 1, and are relative to the current version, not the last-
913
committed version of the file.
916
def _raise_invalid(self, numstring, context_branch):
917
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
918
'No such line: %s' % numstring)
920
def _as_revision_id(self, context_branch):
921
path, numstring = self.spec.rsplit(':', 1)
923
index = int(numstring) - 1
925
self._raise_invalid(numstring, context_branch)
926
tree, file_path = workingtree.WorkingTree.open_containing(path)
929
file_id = tree.path2id(file_path)
931
raise errors.InvalidRevisionSpec(self.user_spec,
932
context_branch, "File '%s' is not versioned." %
934
revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
938
revision_id = revision_ids[index]
940
self._raise_invalid(numstring, context_branch)
941
if revision_id == revision.CURRENT_REVISION:
942
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
943
'Line %s has not been committed.' % numstring)
947
class RevisionSpec_mainline(RevisionIDSpec):
949
help_txt = """Select mainline revision that merged the specified revision.
951
Select the revision that merged the specified revision into mainline.
956
def _as_revision_id(self, context_branch):
957
revspec = RevisionSpec.from_string(self.spec)
958
if revspec.get_branch() is None:
959
spec_branch = context_branch
961
spec_branch = _mod_branch.Branch.open(revspec.get_branch())
962
revision_id = revspec.as_revision_id(spec_branch)
963
graph = context_branch.repository.get_graph()
964
result = graph.find_lefthand_merger(revision_id,
965
context_branch.last_revision())
967
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
971
# The order in which we want to DWIM a revision spec without any prefix.
972
# revno is always tried first and isn't listed here, this is used by
973
# RevisionSpec_dwim._match_on
975
RevisionSpec_tag, # Let's try for a tag
976
RevisionSpec_revid, # Maybe it's a revid?
977
RevisionSpec_date, # Perhaps a date?
978
RevisionSpec_branch, # OK, last try, maybe it's a branch
982
revspec_registry = registry.Registry()
983
def _register_revspec(revspec):
984
revspec_registry.register(revspec.prefix, revspec)
986
_register_revspec(RevisionSpec_revno)
987
_register_revspec(RevisionSpec_revid)
988
_register_revspec(RevisionSpec_last)
989
_register_revspec(RevisionSpec_before)
990
_register_revspec(RevisionSpec_tag)
991
_register_revspec(RevisionSpec_date)
992
_register_revspec(RevisionSpec_ancestor)
993
_register_revspec(RevisionSpec_branch)
994
_register_revspec(RevisionSpec_submit)
995
_register_revspec(RevisionSpec_annotate)
996
_register_revspec(RevisionSpec_mainline)
998
# classes in this list should have a "prefix" attribute, against which
999
# string specs are matched
1000
SPEC_TYPES = symbol_versioning.deprecated_list(
1001
symbol_versioning.deprecated_in((1, 12, 0)), "SPEC_TYPES", [])
382
SPEC_TYPES.append(RevisionSpec_branch)