108
def __new__(cls, spec, foo=_marker):
109
"""Parse a revision specifier.
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
159
if not isinstance(spec, (type(None), basestring)):
160
raise TypeError('error')
112
return object.__new__(RevisionSpec, spec)
119
if isinstance(spec, int):
120
return object.__new__(RevisionSpec_int, spec)
121
elif isinstance(spec, basestring):
122
for spectype in SPEC_TYPES:
123
if spec.startswith(spectype.prefix):
124
return object.__new__(spectype, spec)
126
raise BzrError('No namespace registered for string: %r' %
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)
129
raise TypeError('Unhandled revision type %s' % spec)
131
def __init__(self, spec):
171
# Otherwise treat it as a DWIM, build the RevisionSpec object and
172
# wait for _match_on to be called.
173
return RevisionSpec_dwim(spec, _internal=True)
175
def __init__(self, spec, _internal=False):
176
"""Create a RevisionSpec referring to the Null revision.
178
:param spec: The original spec supplied by the user
179
:param _internal: Used to ensure that RevisionSpec is not being
180
called directly. Only from RevisionSpec.from_string()
183
symbol_versioning.warn('Creating a RevisionSpec directly has'
184
' been deprecated in version 0.11. Use'
185
' RevisionSpec.from_string()'
187
DeprecationWarning, stacklevel=2)
188
self.user_spec = spec
132
189
if self.prefix and spec.startswith(self.prefix):
133
190
spec = spec[len(self.prefix):]
136
193
def _match_on(self, branch, revs):
137
return RevisionInfo(branch, 0, None)
194
trace.mutter('Returning RevisionSpec._match_on: None')
195
return RevisionInfo(branch, None, None)
139
197
def _match_on_and_check(self, branch, revs):
140
198
info = self._match_on(branch, revs)
143
elif info == (0, None):
144
# special case - the empty tree
201
elif info == (None, None):
202
# special case - nothing supplied
146
204
elif self.prefix:
147
raise NoSuchRevision(branch, self.prefix + str(self.spec))
205
raise errors.InvalidRevisionSpec(self.user_spec, branch)
149
raise NoSuchRevision(branch, str(self.spec))
207
raise errors.InvalidRevisionSpec(self.spec, branch)
151
209
def in_history(self, branch):
152
revs = branch.revision_history()
211
if self.wants_revision_history:
212
revs = branch.revision_history()
216
# this should never trigger.
217
# TODO: make it a deprecated code path. RBC 20060928
153
219
return self._match_on_and_check(branch, revs)
155
221
# FIXME: in_history is somewhat broken,
161
227
# will do what you expect.
162
228
in_store = in_history
163
229
in_branch = in_store
231
def as_revision_id(self, context_branch):
232
"""Return just the revision_id for this revisions spec.
234
Some revision specs require a context_branch to be able to determine
235
their value. Not all specs will make use of it.
237
return self._as_revision_id(context_branch)
239
def _as_revision_id(self, context_branch):
240
"""Implementation of as_revision_id()
242
Classes should override this function to provide appropriate
243
functionality. The default is to just call '.in_history().rev_id'
245
return self.in_history(context_branch).rev_id
247
def as_tree(self, context_branch):
248
"""Return the tree object for this revisions spec.
250
Some revision specs require a context_branch to be able to determine
251
the revision id and access the repository. Not all specs will make
254
return self._as_tree(context_branch)
256
def _as_tree(self, context_branch):
257
"""Implementation of as_tree().
259
Classes should override this function to provide appropriate
260
functionality. The default is to just call '.as_revision_id()'
261
and get the revision tree from context_branch's repository.
263
revision_id = self.as_revision_id(context_branch)
264
return context_branch.repository.revision_tree(revision_id)
165
266
def __repr__(self):
166
267
# this is mostly for helping with testing
167
return '<%s %s%s>' % (self.__class__.__name__,
268
return '<%s %s>' % (self.__class__.__name__,
271
def needs_branch(self):
272
"""Whether this revision spec needs a branch.
274
Set this to False the branch argument of _match_on is not used.
278
def get_branch(self):
279
"""When the revision specifier contains a branch location, return it.
281
Otherwise, return None.
174
class RevisionSpec_int(RevisionSpec):
175
"""Spec is a number. Special case."""
176
def __init__(self, spec):
177
self.spec = int(spec)
288
class RevisionSpec_dwim(RevisionSpec):
289
"""Provides a DWIMish revision specifier lookup.
291
Note that this does not go in the revspec_registry because by definition
292
there is no prefix to identify it. It's solely called from
293
RevisionSpec.from_string() because the DWIMification happen when _match_on
294
is called so the string describing the revision is kept here until needed.
298
# We don't need to build the revision history ourself, that's delegated to
299
# each revspec we try.
300
wants_revision_history = False
302
_revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
304
# The revspecs to try
305
_possible_revspecs = []
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)
179
313
def _match_on(self, branch, revs):
181
revno = len(revs) + self.spec + 1
184
rev_id = branch.get_rev_id(revno, revs)
185
return RevisionInfo(branch, revno, rev_id)
314
"""Run the lookup and see what we can get."""
316
# First, see if it's a revno
317
if self._revno_regex.match(self.spec) is not None:
319
return self._try_spectype(RevisionSpec_revno, branch)
320
except RevisionSpec_revno.dwim_catchable_exceptions:
323
# Next see what has been registered
324
for objgetter in self._possible_revspecs:
325
rs_class = objgetter.get_obj()
327
return self._try_spectype(rs_class, branch)
328
except rs_class.dwim_catchable_exceptions:
331
# Try the old (deprecated) dwim list:
332
for rs_class in dwim_revspecs:
334
return self._try_spectype(rs_class, branch)
335
except rs_class.dwim_catchable_exceptions:
338
# Well, I dunno what it is. Note that we don't try to keep track of the
339
# first of last exception raised during the DWIM tries as none seems
341
raise errors.InvalidRevisionSpec(self.spec, branch)
344
def append_possible_revspec(cls, revspec):
345
"""Append a possible DWIM revspec.
347
:param revspec: Revision spec to try.
349
cls._possible_revspecs.append(registry._ObjectGetter(revspec))
352
def append_possible_lazy_revspec(cls, module_name, member_name):
353
"""Append a possible lazily loaded DWIM revspec.
355
:param module_name: Name of the module with the revspec
356
:param member_name: Name of the revspec within the module
358
cls._possible_revspecs.append(
359
registry._LazyObjectGetter(module_name, member_name))
188
362
class RevisionSpec_revno(RevisionSpec):
363
"""Selects a revision using a number."""
365
help_txt = """Selects a revision using a number.
367
Use an integer to specify a revision in the history of the branch.
368
Optionally a branch can be specified. A negative number will count
369
from the end of the branch (-1 is the last revision, -2 the previous
370
one). If the negative number is larger than the branch's history, the
371
first revision is returned.
374
revno:1 -> return the first revision of this branch
375
revno:3:/path/to/branch -> return the 3rd revision of
376
the branch '/path/to/branch'
377
revno:-1 -> The last revision in a branch.
378
-2:http://other/branch -> The second to last revision in the
380
-1000000 -> Most likely the first revision, unless
381
your history is very long.
189
383
prefix = 'revno:'
384
wants_revision_history = False
191
386
def _match_on(self, branch, revs):
192
387
"""Lookup a revision by revision number"""
194
return RevisionInfo(branch, int(self.spec))
196
return RevisionInfo(branch, None)
198
SPEC_TYPES.append(RevisionSpec_revno)
201
class RevisionSpec_revid(RevisionSpec):
388
branch, revno, revision_id = self._lookup(branch, revs)
389
return RevisionInfo(branch, revno, revision_id)
391
def _lookup(self, branch, revs_or_none):
392
loc = self.spec.find(':')
394
revno_spec = self.spec
397
revno_spec = self.spec[:loc]
398
branch_spec = self.spec[loc+1:]
402
raise errors.InvalidRevisionSpec(self.user_spec,
403
branch, 'cannot have an empty revno and no branch')
407
revno = int(revno_spec)
410
# dotted decimal. This arguably should not be here
411
# but the from_string method is a little primitive
412
# right now - RBC 20060928
414
match_revno = tuple((int(number) for number in revno_spec.split('.')))
415
except ValueError, e:
416
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
421
# the user has override the branch to look in.
422
# we need to refresh the revision_history map and
424
from bzrlib.branch import Branch
425
branch = Branch.open(branch_spec)
430
revision_id = branch.dotted_revno_to_revision_id(match_revno,
432
except errors.NoSuchRevision:
433
raise errors.InvalidRevisionSpec(self.user_spec, branch)
435
# there is no traditional 'revno' for dotted-decimal revnos.
436
# so for API compatability we return None.
437
return branch, None, revision_id
439
last_revno, last_revision_id = branch.last_revision_info()
441
# if get_rev_id supported negative revnos, there would not be a
442
# need for this special case.
443
if (-revno) >= last_revno:
446
revno = last_revno + revno + 1
448
revision_id = branch.get_rev_id(revno, revs_or_none)
449
except errors.NoSuchRevision:
450
raise errors.InvalidRevisionSpec(self.user_spec, branch)
451
return branch, revno, revision_id
453
def _as_revision_id(self, context_branch):
454
# We would have the revno here, but we don't really care
455
branch, revno, revision_id = self._lookup(context_branch, None)
458
def needs_branch(self):
459
return self.spec.find(':') == -1
461
def get_branch(self):
462
if self.spec.find(':') == -1:
465
return self.spec[self.spec.find(':')+1:]
468
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, revs)
479
class RevisionSpec_revid(RevisionIDSpec):
480
"""Selects a revision using the revision id."""
482
help_txt = """Selects a revision using the revision id.
484
Supply a specific revision id, that can be used to specify any
485
revision id in the ancestry of the branch.
486
Including merges, and pending merges.
489
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
202
492
prefix = 'revid:'
204
def _match_on(self, branch, revs):
206
return RevisionInfo(branch, revs.index(self.spec) + 1, self.spec)
208
return RevisionInfo(branch, None, self.spec)
494
def _as_revision_id(self, context_branch):
495
# self.spec comes straight from parsing the command line arguments,
496
# so we expect it to be a Unicode string. Switch it to the internal
498
return osutils.safe_revision_id(self.spec, warn=False)
210
SPEC_TYPES.append(RevisionSpec_revid)
213
502
class RevisionSpec_last(RevisionSpec):
503
"""Selects the nth revision from the end."""
505
help_txt = """Selects the nth revision from the end.
507
Supply a positive number to get the nth revision from the end.
508
This is the same as supplying negative numbers to the 'revno:' spec.
511
last:1 -> return the last revision
512
last:3 -> return the revision 2 before the end.
217
517
def _match_on(self, branch, revs):
518
revno, revision_id = self._revno_and_revision_id(branch, revs)
519
return RevisionInfo(branch, revno, revision_id)
521
def _revno_and_revision_id(self, context_branch, revs_or_none):
522
last_revno, last_revision_id = context_branch.last_revision_info()
526
raise errors.NoCommits(context_branch)
527
return last_revno, last_revision_id
219
530
offset = int(self.spec)
221
return RevisionInfo(branch, None)
224
raise BzrError('You must supply a positive value for --revision last:XXX')
225
return RevisionInfo(branch, len(revs) - offset + 1)
227
SPEC_TYPES.append(RevisionSpec_last)
531
except ValueError, e:
532
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
535
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
536
'you must supply a positive value')
538
revno = last_revno - offset + 1
540
revision_id = context_branch.get_rev_id(revno, revs_or_none)
541
except errors.NoSuchRevision:
542
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
543
return revno, revision_id
545
def _as_revision_id(self, context_branch):
546
# We compute the revno as part of the process, but we don't really care
548
revno, revision_id = self._revno_and_revision_id(context_branch, None)
230
553
class RevisionSpec_before(RevisionSpec):
554
"""Selects the parent of the revision specified."""
556
help_txt = """Selects the parent of the revision specified.
558
Supply any revision spec to return the parent of that revision. This is
559
mostly useful when inspecting revisions that are not in the revision history
562
It is an error to request the parent of the null revision (before:0).
566
before:1913 -> Return the parent of revno 1913 (revno 1912)
567
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
569
bzr diff -r before:1913..1913
570
-> Find the changes between revision 1913 and its parent (1912).
571
(What changes did revision 1913 introduce).
572
This is equivalent to: bzr diff -c 1913
232
575
prefix = 'before:'
234
577
def _match_on(self, branch, revs):
235
r = RevisionSpec(self.spec)._match_on(branch, revs)
236
if (r.revno is None) or (r.revno == 0):
238
return RevisionInfo(branch, r.revno - 1)
240
SPEC_TYPES.append(RevisionSpec_before)
578
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
580
raise errors.InvalidRevisionSpec(self.user_spec, branch,
581
'cannot go before the null: revision')
583
# We need to use the repository history here
584
rev = branch.repository.get_revision(r.rev_id)
585
if not rev.parent_ids:
587
revision_id = revision.NULL_REVISION
589
revision_id = rev.parent_ids[0]
591
revno = revs.index(revision_id) + 1
597
revision_id = branch.get_rev_id(revno, revs)
598
except errors.NoSuchRevision:
599
raise errors.InvalidRevisionSpec(self.user_spec,
601
return RevisionInfo(branch, revno, revision_id)
603
def _as_revision_id(self, context_branch):
604
base_revspec = RevisionSpec.from_string(self.spec)
605
base_revision_id = base_revspec.as_revision_id(context_branch)
606
if base_revision_id == revision.NULL_REVISION:
607
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
608
'cannot go before the null: revision')
609
context_repo = context_branch.repository
610
context_repo.lock_read()
612
parent_map = context_repo.get_parent_map([base_revision_id])
614
context_repo.unlock()
615
if base_revision_id not in parent_map:
616
# Ghost, or unknown revision id
617
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
618
'cannot find the matching revision')
619
parents = parent_map[base_revision_id]
621
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
622
'No parents for revision.')
243
627
class RevisionSpec_tag(RevisionSpec):
628
"""Select a revision identified by tag name"""
630
help_txt = """Selects a revision identified by a tag name.
632
Tags are stored in the branch and created by the 'tag' command.
636
dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
246
638
def _match_on(self, branch, revs):
247
raise BzrError('tag: namespace registered, but not implemented.')
249
SPEC_TYPES.append(RevisionSpec_tag)
252
class RevisionSpec_revs:
639
# Can raise tags not supported, NoSuchTag, etc
640
return RevisionInfo.from_revision_id(branch,
641
branch.tags.lookup_tag(self.spec),
644
def _as_revision_id(self, context_branch):
645
return context_branch.tags.lookup_tag(self.spec)
649
class _RevListToTimestamps(object):
650
"""This takes a list of revisions, and allows you to bisect by date"""
652
__slots__ = ['revs', 'branch']
253
654
def __init__(self, revs, branch):
255
656
self.branch = branch
256
658
def __getitem__(self, index):
659
"""Get the date of the index'd item"""
257
660
r = self.branch.repository.get_revision(self.revs[index])
258
661
# TODO: Handle timezone.
259
662
return datetime.datetime.fromtimestamp(r.timestamp)
260
664
def __len__(self):
261
665
return len(self.revs)
264
668
class RevisionSpec_date(RevisionSpec):
669
"""Selects a revision on the basis of a datestamp."""
671
help_txt = """Selects a revision on the basis of a datestamp.
673
Supply a datestamp to select the first revision that matches the date.
674
Date can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
675
Matches the first entry after a given date (either at midnight or
676
at a specified time).
678
One way to display all the changes since yesterday would be::
680
bzr log -r date:yesterday..
684
date:yesterday -> select the first revision since yesterday
685
date:2006-08-14,17:10:14 -> select the first revision after
686
August 14th, 2006 at 5:10pm.
266
_date_re = re.compile(
689
_date_regex = lazy_regex.lazy_compile(
267
690
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
269
692
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
272
695
def _match_on(self, branch, revs):
274
Spec for date revisions:
696
"""Spec for date revisions:
276
698
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
277
699
matches the first entry after a given date (either at midnight or
278
700
at a specified time).
280
So the proper way of saying 'give me all entries for today' is:
281
-r date:today..date:tomorrow
702
# XXX: This doesn't actually work
703
# So the proper way of saying 'give me all entries for today' is:
704
# -r date:yesterday..date:today
283
705
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
284
706
if self.spec.lower() == 'yesterday':
285
707
dt = today - datetime.timedelta(days=1)
288
710
elif self.spec.lower() == 'tomorrow':
289
711
dt = today + datetime.timedelta(days=1)
291
m = self._date_re.match(self.spec)
713
m = self._date_regex.match(self.spec)
292
714
if not m or (not m.group('date') and not m.group('time')):
293
raise BzrError('Invalid revision date %r' % self.spec)
296
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
298
year, month, day = today.year, today.month, today.day
300
hour = int(m.group('hour'))
301
minute = int(m.group('minute'))
302
if m.group('second'):
303
second = int(m.group('second'))
307
hour, minute, second = 0,0,0
715
raise errors.InvalidRevisionSpec(self.user_spec,
716
branch, 'invalid date')
720
year = int(m.group('year'))
721
month = int(m.group('month'))
722
day = int(m.group('day'))
729
hour = int(m.group('hour'))
730
minute = int(m.group('minute'))
731
if m.group('second'):
732
second = int(m.group('second'))
736
hour, minute, second = 0,0,0
738
raise errors.InvalidRevisionSpec(self.user_spec,
739
branch, 'invalid date')
309
741
dt = datetime.datetime(year=year, month=month, day=day,
310
742
hour=hour, minute=minute, second=second)
311
743
branch.lock_read()
313
rev = bisect.bisect(RevisionSpec_revs(revs, branch), dt)
745
rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
316
748
if rev == len(revs):
317
return RevisionInfo(branch, None)
749
raise errors.InvalidRevisionSpec(self.user_spec, branch)
319
751
return RevisionInfo(branch, rev + 1)
321
SPEC_TYPES.append(RevisionSpec_date)
324
755
class RevisionSpec_ancestor(RevisionSpec):
756
"""Selects a common ancestor with a second branch."""
758
help_txt = """Selects a common ancestor with a second branch.
760
Supply the path to a branch to select the common ancestor.
762
The common ancestor is the last revision that existed in both
763
branches. Usually this is the branch point, but it could also be
764
a revision that was merged.
766
This is frequently used with 'diff' to return all of the changes
767
that your branch introduces, while excluding the changes that you
768
have not merged from the remote branch.
772
ancestor:/path/to/branch
773
$ bzr diff -r ancestor:../../mainline/branch
325
775
prefix = 'ancestor:'
327
777
def _match_on(self, branch, revs):
328
from branch import Branch
329
from revision import common_ancestor, MultipleRevisionSources
330
other_branch = Branch.open_containing(self.spec)[0]
331
revision_a = branch.last_revision()
332
revision_b = other_branch.last_revision()
333
for r, b in ((revision_a, branch), (revision_b, other_branch)):
336
revision_source = MultipleRevisionSources(branch.repository,
337
other_branch.repository)
338
rev_id = common_ancestor(revision_a, revision_b, revision_source)
778
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
779
return self._find_revision_info(branch, self.spec)
781
def _as_revision_id(self, context_branch):
782
return self._find_revision_id(context_branch, self.spec)
785
def _find_revision_info(branch, other_location):
786
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
340
revno = branch.revision_id_to_revno(rev_id)
341
except NoSuchRevision:
789
revno = branch.revision_id_to_revno(revision_id)
790
except errors.NoSuchRevision:
343
return RevisionInfo(branch, revno, rev_id)
345
SPEC_TYPES.append(RevisionSpec_ancestor)
792
return RevisionInfo(branch, revno, revision_id)
795
def _find_revision_id(branch, other_location):
796
from bzrlib.branch import Branch
800
revision_a = revision.ensure_null(branch.last_revision())
801
if revision_a == revision.NULL_REVISION:
802
raise errors.NoCommits(branch)
803
if other_location == '':
804
other_location = branch.get_parent()
805
other_branch = Branch.open(other_location)
806
other_branch.lock_read()
808
revision_b = revision.ensure_null(other_branch.last_revision())
809
if revision_b == revision.NULL_REVISION:
810
raise errors.NoCommits(other_branch)
811
graph = branch.repository.get_graph(other_branch.repository)
812
rev_id = graph.find_unique_lca(revision_a, revision_b)
814
other_branch.unlock()
815
if rev_id == revision.NULL_REVISION:
816
raise errors.NoCommonAncestor(revision_a, revision_b)
347
824
class RevisionSpec_branch(RevisionSpec):
348
"""A branch: revision specifier.
350
This takes the path to a branch and returns its tip revision id.
825
"""Selects the last revision of a specified branch."""
827
help_txt = """Selects the last revision of a specified branch.
829
Supply the path to a branch to select its last revision.
833
branch:/path/to/branch
352
835
prefix = 'branch:'
836
dwim_catchable_exceptions = (errors.NotBranchError,)
354
838
def _match_on(self, branch, revs):
355
from branch import Branch
356
other_branch = Branch.open_containing(self.spec)[0]
839
from bzrlib.branch import Branch
840
other_branch = Branch.open(self.spec)
357
841
revision_b = other_branch.last_revision()
358
if revision_b is None:
359
raise NoCommits(other_branch)
360
# pull in the remote revisions so we can diff
361
branch.fetch(other_branch, revision_b)
842
if revision_b in (None, revision.NULL_REVISION):
843
raise errors.NoCommits(other_branch)
845
branch = other_branch
848
# pull in the remote revisions so we can diff
849
branch.fetch(other_branch, revision_b)
850
except errors.ReadOnlyError:
851
branch = other_branch
363
853
revno = branch.revision_id_to_revno(revision_b)
364
except NoSuchRevision:
854
except errors.NoSuchRevision:
366
856
return RevisionInfo(branch, revno, revision_b)
368
SPEC_TYPES.append(RevisionSpec_branch)
858
def _as_revision_id(self, context_branch):
859
from bzrlib.branch import Branch
860
other_branch = Branch.open(self.spec)
861
last_revision = other_branch.last_revision()
862
last_revision = revision.ensure_null(last_revision)
863
context_branch.fetch(other_branch, last_revision)
864
if last_revision == revision.NULL_REVISION:
865
raise errors.NoCommits(other_branch)
868
def _as_tree(self, context_branch):
869
from bzrlib.branch import Branch
870
other_branch = Branch.open(self.spec)
871
last_revision = other_branch.last_revision()
872
last_revision = revision.ensure_null(last_revision)
873
if last_revision == revision.NULL_REVISION:
874
raise errors.NoCommits(other_branch)
875
return other_branch.repository.revision_tree(last_revision)
877
def needs_branch(self):
880
def get_branch(self):
885
class RevisionSpec_submit(RevisionSpec_ancestor):
886
"""Selects a common ancestor with a submit branch."""
888
help_txt = """Selects a common ancestor with the submit branch.
890
Diffing against this shows all the changes that were made in this branch,
891
and is a good predictor of what merge will do. The submit branch is
892
used by the bundle and merge directive commands. If no submit branch
893
is specified, the parent branch is used instead.
895
The common ancestor is the last revision that existed in both
896
branches. Usually this is the branch point, but it could also be
897
a revision that was merged.
901
$ bzr diff -r submit:
906
def _get_submit_location(self, branch):
907
submit_location = branch.get_submit_branch()
908
location_type = 'submit branch'
909
if submit_location is None:
910
submit_location = branch.get_parent()
911
location_type = 'parent branch'
912
if submit_location is None:
913
raise errors.NoSubmitBranch(branch)
914
trace.note(gettext('Using {0} {1}').format(location_type,
916
return submit_location
918
def _match_on(self, branch, revs):
919
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
920
return self._find_revision_info(branch,
921
self._get_submit_location(branch))
923
def _as_revision_id(self, context_branch):
924
return self._find_revision_id(context_branch,
925
self._get_submit_location(context_branch))
928
class RevisionSpec_annotate(RevisionIDSpec):
932
help_txt = """Select the revision that last modified the specified line.
934
Select the revision that last modified the specified line. Line is
935
specified as path:number. Path is a relative path to the file. Numbers
936
start at 1, and are relative to the current version, not the last-
937
committed version of the file.
940
def _raise_invalid(self, numstring, context_branch):
941
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
942
'No such line: %s' % numstring)
944
def _as_revision_id(self, context_branch):
945
path, numstring = self.spec.rsplit(':', 1)
947
index = int(numstring) - 1
949
self._raise_invalid(numstring, context_branch)
950
tree, file_path = workingtree.WorkingTree.open_containing(path)
953
file_id = tree.path2id(file_path)
955
raise errors.InvalidRevisionSpec(self.user_spec,
956
context_branch, "File '%s' is not versioned." %
958
revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
962
revision_id = revision_ids[index]
964
self._raise_invalid(numstring, context_branch)
965
if revision_id == revision.CURRENT_REVISION:
966
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
967
'Line %s has not been committed.' % numstring)
971
class RevisionSpec_mainline(RevisionIDSpec):
973
help_txt = """Select mainline revision that merged the specified revision.
975
Select the revision that merged the specified revision into mainline.
980
def _as_revision_id(self, context_branch):
981
revspec = RevisionSpec.from_string(self.spec)
982
if revspec.get_branch() is None:
983
spec_branch = context_branch
985
spec_branch = _mod_branch.Branch.open(revspec.get_branch())
986
revision_id = revspec.as_revision_id(spec_branch)
987
graph = context_branch.repository.get_graph()
988
result = graph.find_lefthand_merger(revision_id,
989
context_branch.last_revision())
991
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
995
# The order in which we want to DWIM a revision spec without any prefix.
996
# revno is always tried first and isn't listed here, this is used by
997
# RevisionSpec_dwim._match_on
998
dwim_revspecs = symbol_versioning.deprecated_list(
999
symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
1001
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
1002
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
1003
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
1004
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
1006
revspec_registry = registry.Registry()
1007
def _register_revspec(revspec):
1008
revspec_registry.register(revspec.prefix, revspec)
1010
_register_revspec(RevisionSpec_revno)
1011
_register_revspec(RevisionSpec_revid)
1012
_register_revspec(RevisionSpec_last)
1013
_register_revspec(RevisionSpec_before)
1014
_register_revspec(RevisionSpec_tag)
1015
_register_revspec(RevisionSpec_date)
1016
_register_revspec(RevisionSpec_ancestor)
1017
_register_revspec(RevisionSpec_branch)
1018
_register_revspec(RevisionSpec_submit)
1019
_register_revspec(RevisionSpec_annotate)
1020
_register_revspec(RevisionSpec_mainline)