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
107
def __new__(cls, spec, foo=_marker):
108
"""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)
111
return object.__new__(RevisionSpec, spec)
118
if isinstance(spec, int):
119
return object.__new__(RevisionSpec_int, spec)
120
elif isinstance(spec, basestring):
121
for spectype in SPEC_TYPES:
122
if spec.startswith(spectype.prefix):
123
return object.__new__(spectype, spec)
125
raise BzrError('No namespace registered for string: %r' %
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
128
raise TypeError('Unhandled revision type %s' % spec)
130
def __init__(self, spec):
189
131
if self.prefix and spec.startswith(self.prefix):
190
132
spec = spec[len(self.prefix):]
193
135
def _match_on(self, branch, revs):
194
trace.mutter('Returning RevisionSpec._match_on: None')
195
return RevisionInfo(branch, None, None)
136
return RevisionInfo(branch, 0, None)
197
138
def _match_on_and_check(self, branch, revs):
198
139
info = self._match_on(branch, revs)
201
elif info == (None, None):
202
# special case - nothing supplied
142
elif info == (0, None):
143
# special case - the empty tree
204
145
elif self.prefix:
205
raise errors.InvalidRevisionSpec(self.user_spec, branch)
146
raise NoSuchRevision(branch, self.prefix + str(self.spec))
207
raise errors.InvalidRevisionSpec(self.spec, branch)
148
raise NoSuchRevision(branch, str(self.spec))
209
150
def in_history(self, branch):
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
151
revs = branch.revision_history()
219
152
return self._match_on_and_check(branch, revs)
221
154
# FIXME: in_history is somewhat broken,
227
160
# will do what you expect.
228
161
in_store = in_history
229
162
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)
266
164
def __repr__(self):
267
165
# this is mostly for helping with testing
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.
166
return '<%s %s%s>' % (self.__class__.__name__,
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)
173
class RevisionSpec_int(RevisionSpec):
174
"""Spec is a number. Special case."""
175
def __init__(self, spec):
176
self.spec = int(spec)
313
178
def _match_on(self, branch, revs):
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))
180
revno = len(revs) + self.spec + 1
183
rev_id = branch.get_rev_id(revno, revs)
184
return RevisionInfo(branch, revno, rev_id)
362
187
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.
383
188
prefix = 'revno:'
384
wants_revision_history = False
386
190
def _match_on(self, branch, revs):
387
191
"""Lookup a revision by revision number"""
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'
193
return RevisionInfo(branch, int(self.spec))
195
return RevisionInfo(branch, None)
197
SPEC_TYPES.append(RevisionSpec_revno)
200
class RevisionSpec_revid(RevisionSpec):
492
201
prefix = 'revid:'
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)
203
def _match_on(self, branch, revs):
205
return RevisionInfo(branch, revs.index(self.spec) + 1, self.spec)
207
return RevisionInfo(branch, None, self.spec)
209
SPEC_TYPES.append(RevisionSpec_revid)
502
212
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.
517
216
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
530
218
offset = int(self.spec)
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)
220
return RevisionInfo(branch, None)
223
raise BzrError('You must supply a positive value for --revision last:XXX')
224
return RevisionInfo(branch, len(revs) - offset + 1)
226
SPEC_TYPES.append(RevisionSpec_last)
553
229
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
575
231
prefix = 'before:'
577
233
def _match_on(self, branch, revs):
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.')
234
r = RevisionSpec(self.spec)._match_on(branch, revs)
235
if (r.revno is None) or (r.revno == 0):
237
return RevisionInfo(branch, r.revno - 1)
239
SPEC_TYPES.append(RevisionSpec_before)
627
242
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)
638
245
def _match_on(self, branch, 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']
654
def __init__(self, revs, branch):
658
def __getitem__(self, index):
659
"""Get the date of the index'd item"""
660
r = self.branch.repository.get_revision(self.revs[index])
661
# TODO: Handle timezone.
662
return datetime.datetime.fromtimestamp(r.timestamp)
665
return len(self.revs)
246
raise BzrError('tag: namespace registered, but not implemented.')
248
SPEC_TYPES.append(RevisionSpec_tag)
668
251
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.
689
_date_regex = lazy_regex.lazy_compile(
253
_date_re = re.compile(
690
254
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
692
256
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
695
259
def _match_on(self, branch, revs):
696
"""Spec for date revisions:
261
Spec for date revisions:
698
263
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
699
264
matches the first entry after a given date (either at midnight or
700
265
at a specified time).
267
So the proper way of saying 'give me all entries for today' is:
268
-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
705
270
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
706
271
if self.spec.lower() == 'yesterday':
707
272
dt = today - datetime.timedelta(days=1)
710
275
elif self.spec.lower() == 'tomorrow':
711
276
dt = today + datetime.timedelta(days=1)
713
m = self._date_regex.match(self.spec)
278
m = self._date_re.match(self.spec)
714
279
if not m or (not m.group('date') and not m.group('time')):
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')
280
raise BzrError('Invalid revision date %r' % self.spec)
283
year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
285
year, month, day = today.year, today.month, today.day
287
hour = int(m.group('hour'))
288
minute = int(m.group('minute'))
289
if m.group('second'):
290
second = int(m.group('second'))
294
hour, minute, second = 0,0,0
741
296
dt = datetime.datetime(year=year, month=month, day=day,
742
297
hour=hour, minute=minute, second=second)
745
rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
749
raise errors.InvalidRevisionSpec(self.user_spec, branch)
751
return RevisionInfo(branch, rev + 1)
299
for i in range(len(revs)):
300
r = branch.repository.get_revision(revs[i])
301
# TODO: Handle timezone.
302
dt = datetime.datetime.fromtimestamp(r.timestamp)
304
return RevisionInfo(branch, i+1)
305
return RevisionInfo(branch, None)
307
SPEC_TYPES.append(RevisionSpec_date)
755
310
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
775
311
prefix = 'ancestor:'
777
313
def _match_on(self, branch, revs):
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,
314
from branch import Branch
315
from revision import common_ancestor, MultipleRevisionSources
316
other_branch = Branch.open_containing(self.spec)[0]
317
revision_a = branch.last_revision()
318
revision_b = other_branch.last_revision()
319
for r, b in ((revision_a, branch), (revision_b, other_branch)):
322
revision_source = MultipleRevisionSources(branch.repository,
323
other_branch.repository)
324
rev_id = common_ancestor(revision_a, revision_b, revision_source)
789
revno = branch.revision_id_to_revno(revision_id)
790
except errors.NoSuchRevision:
326
revno = branch.revision_id_to_revno(rev_id)
327
except NoSuchRevision:
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)
329
return RevisionInfo(branch, revno, rev_id)
331
SPEC_TYPES.append(RevisionSpec_ancestor)
824
333
class RevisionSpec_branch(RevisionSpec):
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
334
"""A branch: revision specifier.
336
This takes the path to a branch and returns its tip revision id.
835
338
prefix = 'branch:'
836
dwim_catchable_exceptions = (errors.NotBranchError,)
838
340
def _match_on(self, branch, revs):
839
from bzrlib.branch import Branch
840
other_branch = Branch.open(self.spec)
341
from branch import Branch
342
other_branch = Branch.open_containing(self.spec)[0]
841
343
revision_b = other_branch.last_revision()
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
344
if revision_b is None:
345
raise NoCommits(other_branch)
346
# pull in the remote revisions so we can diff
347
branch.fetch(other_branch, revision_b)
853
349
revno = branch.revision_id_to_revno(revision_b)
854
except errors.NoSuchRevision:
350
except NoSuchRevision:
856
352
return RevisionInfo(branch, revno, revision_b)
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)
354
SPEC_TYPES.append(RevisionSpec_branch)