144
# wants_revision_history has been deprecated in 2.5.
145
wants_revision_history = False
146
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
147
"""Exceptions that RevisionSpec_dwim._match_on will catch.
149
If the revspec is part of ``dwim_revspecs``, it may be tried with an
150
invalid revspec and raises some exception. The exceptions mentioned here
151
will not be reported to the user but simply ignored without stopping the
156
def from_string(spec):
157
"""Parse a revision spec string into a RevisionSpec object.
159
:param spec: A string specified by the user
160
:return: A RevisionSpec object that understands how to parse the
107
def __new__(cls, spec, foo=_marker):
108
"""Parse a revision specifier.
163
if not isinstance(spec, (type(None), basestring)):
164
raise TypeError('error')
167
return RevisionSpec(None, _internal=True)
168
match = revspec_registry.get_prefix(spec)
169
if match is not None:
170
spectype, specsuffix = match
171
trace.mutter('Returning RevisionSpec %s for %s',
172
spectype.__name__, spec)
173
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' %
175
# Otherwise treat it as a DWIM, build the RevisionSpec object and
176
# wait for _match_on to be called.
177
return RevisionSpec_dwim(spec, _internal=True)
179
def __init__(self, spec, _internal=False):
180
"""Create a RevisionSpec referring to the Null revision.
182
:param spec: The original spec supplied by the user
183
:param _internal: Used to ensure that RevisionSpec is not being
184
called directly. Only from RevisionSpec.from_string()
187
symbol_versioning.warn('Creating a RevisionSpec directly has'
188
' been deprecated in version 0.11. Use'
189
' RevisionSpec.from_string()'
191
DeprecationWarning, stacklevel=2)
192
self.user_spec = spec
128
raise TypeError('Unhandled revision type %s' % spec)
130
def __init__(self, spec):
193
131
if self.prefix and spec.startswith(self.prefix):
194
132
spec = spec[len(self.prefix):]
197
135
def _match_on(self, branch, revs):
198
trace.mutter('Returning RevisionSpec._match_on: None')
199
return RevisionInfo(branch, None, None)
136
return RevisionInfo(branch, 0, None)
201
138
def _match_on_and_check(self, branch, revs):
202
139
info = self._match_on(branch, revs)
205
elif info == (None, None):
206
# special case - nothing supplied
142
elif info == (0, None):
143
# special case - the empty tree
208
145
elif self.prefix:
209
raise errors.InvalidRevisionSpec(self.user_spec, branch)
146
raise NoSuchRevision(branch, self.prefix + str(self.spec))
211
raise errors.InvalidRevisionSpec(self.spec, branch)
148
raise NoSuchRevision(branch, str(self.spec))
213
150
def in_history(self, branch):
215
if self.wants_revision_history:
216
symbol_versioning.warn(
217
"RevisionSpec.wants_revision_history was "
218
"deprecated in 2.5 (%s)." % self.__class__.__name__,
222
graph = branch.repository.get_graph()
223
revs = list(graph.iter_lefthand_ancestry(
224
branch.last_revision(), [revision.NULL_REVISION]))
231
# this should never trigger.
232
# TODO: make it a deprecated code path. RBC 20060928
151
revs = branch.revision_history()
234
152
return self._match_on_and_check(branch, revs)
236
154
# FIXME: in_history is somewhat broken,
242
160
# will do what you expect.
243
161
in_store = in_history
244
162
in_branch = in_store
246
def as_revision_id(self, context_branch):
247
"""Return just the revision_id for this revisions spec.
249
Some revision specs require a context_branch to be able to determine
250
their value. Not all specs will make use of it.
252
return self._as_revision_id(context_branch)
254
def _as_revision_id(self, context_branch):
255
"""Implementation of as_revision_id()
257
Classes should override this function to provide appropriate
258
functionality. The default is to just call '.in_history().rev_id'
260
return self.in_history(context_branch).rev_id
262
def as_tree(self, context_branch):
263
"""Return the tree object for this revisions spec.
265
Some revision specs require a context_branch to be able to determine
266
the revision id and access the repository. Not all specs will make
269
return self._as_tree(context_branch)
271
def _as_tree(self, context_branch):
272
"""Implementation of as_tree().
274
Classes should override this function to provide appropriate
275
functionality. The default is to just call '.as_revision_id()'
276
and get the revision tree from context_branch's repository.
278
revision_id = self.as_revision_id(context_branch)
279
return context_branch.repository.revision_tree(revision_id)
281
164
def __repr__(self):
282
165
# this is mostly for helping with testing
283
return '<%s %s>' % (self.__class__.__name__,
286
def needs_branch(self):
287
"""Whether this revision spec needs a branch.
289
Set this to False the branch argument of _match_on is not used.
293
def get_branch(self):
294
"""When the revision specifier contains a branch location, return it.
296
Otherwise, return None.
166
return '<%s %s%s>' % (self.__class__.__name__,
303
class RevisionSpec_dwim(RevisionSpec):
304
"""Provides a DWIMish revision specifier lookup.
306
Note that this does not go in the revspec_registry because by definition
307
there is no prefix to identify it. It's solely called from
308
RevisionSpec.from_string() because the DWIMification happen when _match_on
309
is called so the string describing the revision is kept here until needed.
314
_revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
316
# The revspecs to try
317
_possible_revspecs = []
319
def _try_spectype(self, rstype, branch):
320
rs = rstype(self.spec, _internal=True)
321
# Hit in_history to find out if it exists, or we need to try the
323
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)
325
178
def _match_on(self, branch, revs):
326
"""Run the lookup and see what we can get."""
328
# First, see if it's a revno
329
if self._revno_regex.match(self.spec) is not None:
331
return self._try_spectype(RevisionSpec_revno, branch)
332
except RevisionSpec_revno.dwim_catchable_exceptions:
335
# Next see what has been registered
336
for objgetter in self._possible_revspecs:
337
rs_class = objgetter.get_obj()
339
return self._try_spectype(rs_class, branch)
340
except rs_class.dwim_catchable_exceptions:
343
# Try the old (deprecated) dwim list:
344
for rs_class in dwim_revspecs:
346
return self._try_spectype(rs_class, branch)
347
except rs_class.dwim_catchable_exceptions:
350
# Well, I dunno what it is. Note that we don't try to keep track of the
351
# first of last exception raised during the DWIM tries as none seems
353
raise errors.InvalidRevisionSpec(self.spec, branch)
356
def append_possible_revspec(cls, revspec):
357
"""Append a possible DWIM revspec.
359
:param revspec: Revision spec to try.
361
cls._possible_revspecs.append(registry._ObjectGetter(revspec))
364
def append_possible_lazy_revspec(cls, module_name, member_name):
365
"""Append a possible lazily loaded DWIM revspec.
367
:param module_name: Name of the module with the revspec
368
:param member_name: Name of the revspec within the module
370
cls._possible_revspecs.append(
371
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)
374
187
class RevisionSpec_revno(RevisionSpec):
375
"""Selects a revision using a number."""
377
help_txt = """Selects a revision using a number.
379
Use an integer to specify a revision in the history of the branch.
380
Optionally a branch can be specified. A negative number will count
381
from the end of the branch (-1 is the last revision, -2 the previous
382
one). If the negative number is larger than the branch's history, the
383
first revision is returned.
386
revno:1 -> return the first revision of this branch
387
revno:3:/path/to/branch -> return the 3rd revision of
388
the branch '/path/to/branch'
389
revno:-1 -> The last revision in a branch.
390
-2:http://other/branch -> The second to last revision in the
392
-1000000 -> Most likely the first revision, unless
393
your history is very long.
395
188
prefix = 'revno:'
397
190
def _match_on(self, branch, revs):
398
191
"""Lookup a revision by revision number"""
399
branch, revno, revision_id = self._lookup(branch)
400
return RevisionInfo(branch, revno, revision_id)
402
def _lookup(self, branch):
403
loc = self.spec.find(':')
405
revno_spec = self.spec
408
revno_spec = self.spec[:loc]
409
branch_spec = self.spec[loc+1:]
413
raise errors.InvalidRevisionSpec(self.user_spec,
414
branch, 'cannot have an empty revno and no branch')
418
revno = int(revno_spec)
421
# dotted decimal. This arguably should not be here
422
# but the from_string method is a little primitive
423
# right now - RBC 20060928
425
match_revno = tuple((int(number) for number in revno_spec.split('.')))
426
except ValueError, e:
427
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
432
# the user has overriden the branch to look in.
433
branch = _mod_branch.Branch.open(branch_spec)
437
revision_id = branch.dotted_revno_to_revision_id(match_revno,
439
except errors.NoSuchRevision:
440
raise errors.InvalidRevisionSpec(self.user_spec, branch)
442
# there is no traditional 'revno' for dotted-decimal revnos.
443
# so for API compatibility we return None.
444
return branch, None, revision_id
446
last_revno, last_revision_id = branch.last_revision_info()
448
# if get_rev_id supported negative revnos, there would not be a
449
# need for this special case.
450
if (-revno) >= last_revno:
453
revno = last_revno + revno + 1
455
revision_id = branch.get_rev_id(revno)
456
except errors.NoSuchRevision:
457
raise errors.InvalidRevisionSpec(self.user_spec, branch)
458
return branch, revno, revision_id
460
def _as_revision_id(self, context_branch):
461
# We would have the revno here, but we don't really care
462
branch, revno, revision_id = self._lookup(context_branch)
465
def needs_branch(self):
466
return self.spec.find(':') == -1
468
def get_branch(self):
469
if self.spec.find(':') == -1:
472
return self.spec[self.spec.find(':')+1:]
475
RevisionSpec_int = RevisionSpec_revno
478
class RevisionIDSpec(RevisionSpec):
480
def _match_on(self, branch, revs):
481
revision_id = self.as_revision_id(branch)
482
return RevisionInfo.from_revision_id(branch, revision_id)
485
class RevisionSpec_revid(RevisionIDSpec):
486
"""Selects a revision using the revision id."""
488
help_txt = """Selects a revision using the revision id.
490
Supply a specific revision id, that can be used to specify any
491
revision id in the ancestry of the branch.
492
Including merges, and pending merges.
495
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):
498
201
prefix = 'revid:'
500
def _as_revision_id(self, context_branch):
501
# self.spec comes straight from parsing the command line arguments,
502
# so we expect it to be a Unicode string. Switch it to the internal
504
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)
209
SPEC_TYPES.append(RevisionSpec_revid)
508
212
class RevisionSpec_last(RevisionSpec):
509
"""Selects the nth revision from the end."""
511
help_txt = """Selects the nth revision from the end.
513
Supply a positive number to get the nth revision from the end.
514
This is the same as supplying negative numbers to the 'revno:' spec.
517
last:1 -> return the last revision
518
last:3 -> return the revision 2 before the end.
523
216
def _match_on(self, branch, revs):
524
revno, revision_id = self._revno_and_revision_id(branch)
525
return RevisionInfo(branch, revno, revision_id)
527
def _revno_and_revision_id(self, context_branch):
528
last_revno, last_revision_id = context_branch.last_revision_info()
532
raise errors.NoCommits(context_branch)
533
return last_revno, last_revision_id
536
218
offset = int(self.spec)
537
except ValueError, e:
538
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
541
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
542
'you must supply a positive value')
544
revno = last_revno - offset + 1
546
revision_id = context_branch.get_rev_id(revno)
547
except errors.NoSuchRevision:
548
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
549
return revno, revision_id
551
def _as_revision_id(self, context_branch):
552
# We compute the revno as part of the process, but we don't really care
554
revno, revision_id = self._revno_and_revision_id(context_branch)
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)
559
229
class RevisionSpec_before(RevisionSpec):
560
"""Selects the parent of the revision specified."""
562
help_txt = """Selects the parent of the revision specified.
564
Supply any revision spec to return the parent of that revision. This is
565
mostly useful when inspecting revisions that are not in the revision history
568
It is an error to request the parent of the null revision (before:0).
572
before:1913 -> Return the parent of revno 1913 (revno 1912)
573
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
575
bzr diff -r before:1913..1913
576
-> Find the changes between revision 1913 and its parent (1912).
577
(What changes did revision 1913 introduce).
578
This is equivalent to: bzr diff -c 1913
581
231
prefix = 'before:'
583
233
def _match_on(self, branch, revs):
584
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
586
raise errors.InvalidRevisionSpec(self.user_spec, branch,
587
'cannot go before the null: revision')
589
# We need to use the repository history here
590
rev = branch.repository.get_revision(r.rev_id)
591
if not rev.parent_ids:
592
revision_id = revision.NULL_REVISION
594
revision_id = rev.parent_ids[0]
599
revision_id = branch.get_rev_id(revno, revs)
600
except errors.NoSuchRevision:
601
raise errors.InvalidRevisionSpec(self.user_spec,
603
return RevisionInfo(branch, revno, revision_id)
605
def _as_revision_id(self, context_branch):
606
base_revision_id = RevisionSpec.from_string(self.spec)._as_revision_id(context_branch)
607
if base_revision_id == revision.NULL_REVISION:
608
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
609
'cannot go before the null: revision')
610
context_repo = context_branch.repository
611
context_repo.lock_read()
613
parent_map = context_repo.get_parent_map([base_revision_id])
615
context_repo.unlock()
616
if base_revision_id not in parent_map:
617
# Ghost, or unknown revision id
618
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
619
'cannot find the matching revision')
620
parents = parent_map[base_revision_id]
622
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
623
'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)
628
242
class RevisionSpec_tag(RevisionSpec):
629
"""Select a revision identified by tag name"""
631
help_txt = """Selects a revision identified by a tag name.
633
Tags are stored in the branch and created by the 'tag' command.
637
dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
639
245
def _match_on(self, branch, revs):
640
# Can raise tags not supported, NoSuchTag, etc
641
return RevisionInfo.from_revision_id(branch,
642
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__ = ['branch']
654
def __init__(self, branch):
657
def __getitem__(self, index):
658
"""Get the date of the index'd item"""
659
r = self.branch.repository.get_revision(self.branch.get_rev_id(index))
660
# TODO: Handle timezone.
661
return datetime.datetime.fromtimestamp(r.timestamp)
664
return self.branch.revno()
246
raise BzrError('tag: namespace registered, but not implemented.')
248
SPEC_TYPES.append(RevisionSpec_tag)
667
251
class RevisionSpec_date(RevisionSpec):
668
"""Selects a revision on the basis of a datestamp."""
670
help_txt = """Selects a revision on the basis of a datestamp.
672
Supply a datestamp to select the first revision that matches the date.
673
Date can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
674
Matches the first entry after a given date (either at midnight or
675
at a specified time).
677
One way to display all the changes since yesterday would be::
679
bzr log -r date:yesterday..
683
date:yesterday -> select the first revision since yesterday
684
date:2006-08-14,17:10:14 -> select the first revision after
685
August 14th, 2006 at 5:10pm.
688
_date_regex = lazy_regex.lazy_compile(
253
_date_re = re.compile(
689
254
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
691
256
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
694
259
def _match_on(self, branch, revs):
695
"""Spec for date revisions:
261
Spec for date revisions:
697
263
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
698
264
matches the first entry after a given date (either at midnight or
699
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
701
# XXX: This doesn't actually work
702
# So the proper way of saying 'give me all entries for today' is:
703
# -r date:yesterday..date:today
704
270
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
705
271
if self.spec.lower() == 'yesterday':
706
272
dt = today - datetime.timedelta(days=1)
709
275
elif self.spec.lower() == 'tomorrow':
710
276
dt = today + datetime.timedelta(days=1)
712
m = self._date_regex.match(self.spec)
278
m = self._date_re.match(self.spec)
713
279
if not m or (not m.group('date') and not m.group('time')):
714
raise errors.InvalidRevisionSpec(self.user_spec,
715
branch, 'invalid date')
719
year = int(m.group('year'))
720
month = int(m.group('month'))
721
day = int(m.group('day'))
728
hour = int(m.group('hour'))
729
minute = int(m.group('minute'))
730
if m.group('second'):
731
second = int(m.group('second'))
735
hour, minute, second = 0,0,0
737
raise errors.InvalidRevisionSpec(self.user_spec,
738
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
740
296
dt = datetime.datetime(year=year, month=month, day=day,
741
297
hour=hour, minute=minute, second=second)
744
rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
747
if rev == branch.revno():
748
raise errors.InvalidRevisionSpec(self.user_spec, branch)
749
return RevisionInfo(branch, rev)
299
for i in range(len(revs)):
300
r = branch.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)
753
310
class RevisionSpec_ancestor(RevisionSpec):
754
"""Selects a common ancestor with a second branch."""
756
help_txt = """Selects a common ancestor with a second branch.
758
Supply the path to a branch to select the common ancestor.
760
The common ancestor is the last revision that existed in both
761
branches. Usually this is the branch point, but it could also be
762
a revision that was merged.
764
This is frequently used with 'diff' to return all of the changes
765
that your branch introduces, while excluding the changes that you
766
have not merged from the remote branch.
770
ancestor:/path/to/branch
771
$ bzr diff -r ancestor:../../mainline/branch
773
311
prefix = 'ancestor:'
775
313
def _match_on(self, branch, revs):
776
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
777
return self._find_revision_info(branch, self.spec)
779
def _as_revision_id(self, context_branch):
780
return self._find_revision_id(context_branch, self.spec)
783
def _find_revision_info(branch, other_location):
784
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
786
return RevisionInfo(branch, None, revision_id)
789
def _find_revision_id(branch, other_location):
790
from bzrlib.branch import 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, other_branch)
323
rev_id = common_ancestor(revision_a, revision_b, revision_source)
794
revision_a = revision.ensure_null(branch.last_revision())
795
if revision_a == revision.NULL_REVISION:
796
raise errors.NoCommits(branch)
797
if other_location == '':
798
other_location = branch.get_parent()
799
other_branch = Branch.open(other_location)
800
other_branch.lock_read()
802
revision_b = revision.ensure_null(other_branch.last_revision())
803
if revision_b == revision.NULL_REVISION:
804
raise errors.NoCommits(other_branch)
805
graph = branch.repository.get_graph(other_branch.repository)
806
rev_id = graph.find_unique_lca(revision_a, revision_b)
808
other_branch.unlock()
809
if rev_id == revision.NULL_REVISION:
810
raise errors.NoCommonAncestor(revision_a, revision_b)
325
revno = branch.revision_id_to_revno(rev_id)
326
except NoSuchRevision:
328
return RevisionInfo(branch, revno, rev_id)
330
SPEC_TYPES.append(RevisionSpec_ancestor)
818
332
class RevisionSpec_branch(RevisionSpec):
819
"""Selects the last revision of a specified branch."""
821
help_txt = """Selects the last revision of a specified branch.
823
Supply the path to a branch to select its last revision.
827
branch:/path/to/branch
333
"""A branch: revision specifier.
335
This takes the path to a branch and returns its tip revision id.
829
337
prefix = 'branch:'
830
dwim_catchable_exceptions = (errors.NotBranchError,)
832
339
def _match_on(self, branch, revs):
833
from bzrlib.branch import Branch
834
other_branch = Branch.open(self.spec)
340
from branch import Branch
341
from fetch import greedy_fetch
342
other_branch = Branch.open_containing(self.spec)[0]
835
343
revision_b = other_branch.last_revision()
836
if revision_b in (None, revision.NULL_REVISION):
837
raise errors.NoCommits(other_branch)
839
branch = other_branch
842
# pull in the remote revisions so we can diff
843
branch.fetch(other_branch, revision_b)
844
except errors.ReadOnlyError:
845
branch = other_branch
846
return RevisionInfo(branch, None, revision_b)
848
def _as_revision_id(self, context_branch):
849
from bzrlib.branch import Branch
850
other_branch = Branch.open(self.spec)
851
last_revision = other_branch.last_revision()
852
last_revision = revision.ensure_null(last_revision)
853
context_branch.fetch(other_branch, last_revision)
854
if last_revision == revision.NULL_REVISION:
855
raise errors.NoCommits(other_branch)
858
def _as_tree(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
if last_revision == revision.NULL_REVISION:
864
raise errors.NoCommits(other_branch)
865
return other_branch.repository.revision_tree(last_revision)
867
def needs_branch(self):
870
def get_branch(self):
875
class RevisionSpec_submit(RevisionSpec_ancestor):
876
"""Selects a common ancestor with a submit branch."""
878
help_txt = """Selects a common ancestor with the submit branch.
880
Diffing against this shows all the changes that were made in this branch,
881
and is a good predictor of what merge will do. The submit branch is
882
used by the bundle and merge directive commands. If no submit branch
883
is specified, the parent branch is used instead.
885
The common ancestor is the last revision that existed in both
886
branches. Usually this is the branch point, but it could also be
887
a revision that was merged.
891
$ bzr diff -r submit:
896
def _get_submit_location(self, branch):
897
submit_location = branch.get_submit_branch()
898
location_type = 'submit branch'
899
if submit_location is None:
900
submit_location = branch.get_parent()
901
location_type = 'parent branch'
902
if submit_location is None:
903
raise errors.NoSubmitBranch(branch)
904
trace.note(gettext('Using {0} {1}').format(location_type,
906
return submit_location
908
def _match_on(self, branch, revs):
909
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
910
return self._find_revision_info(branch,
911
self._get_submit_location(branch))
913
def _as_revision_id(self, context_branch):
914
return self._find_revision_id(context_branch,
915
self._get_submit_location(context_branch))
918
class RevisionSpec_annotate(RevisionIDSpec):
922
help_txt = """Select the revision that last modified the specified line.
924
Select the revision that last modified the specified line. Line is
925
specified as path:number. Path is a relative path to the file. Numbers
926
start at 1, and are relative to the current version, not the last-
927
committed version of the file.
930
def _raise_invalid(self, numstring, context_branch):
931
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
932
'No such line: %s' % numstring)
934
def _as_revision_id(self, context_branch):
935
path, numstring = self.spec.rsplit(':', 1)
937
index = int(numstring) - 1
939
self._raise_invalid(numstring, context_branch)
940
tree, file_path = workingtree.WorkingTree.open_containing(path)
943
file_id = tree.path2id(file_path)
945
raise errors.InvalidRevisionSpec(self.user_spec,
946
context_branch, "File '%s' is not versioned." %
948
revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
952
revision_id = revision_ids[index]
954
self._raise_invalid(numstring, context_branch)
955
if revision_id == revision.CURRENT_REVISION:
956
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
957
'Line %s has not been committed.' % numstring)
961
class RevisionSpec_mainline(RevisionIDSpec):
963
help_txt = """Select mainline revision that merged the specified revision.
965
Select the revision that merged the specified revision into mainline.
970
def _as_revision_id(self, context_branch):
971
revspec = RevisionSpec.from_string(self.spec)
972
if revspec.get_branch() is None:
973
spec_branch = context_branch
975
spec_branch = _mod_branch.Branch.open(revspec.get_branch())
976
revision_id = revspec.as_revision_id(spec_branch)
977
graph = context_branch.repository.get_graph()
978
result = graph.find_lefthand_merger(revision_id,
979
context_branch.last_revision())
981
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
985
# The order in which we want to DWIM a revision spec without any prefix.
986
# revno is always tried first and isn't listed here, this is used by
987
# RevisionSpec_dwim._match_on
988
dwim_revspecs = symbol_versioning.deprecated_list(
989
symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
991
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
992
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
993
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
994
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
996
revspec_registry = registry.Registry()
997
def _register_revspec(revspec):
998
revspec_registry.register(revspec.prefix, revspec)
1000
_register_revspec(RevisionSpec_revno)
1001
_register_revspec(RevisionSpec_revid)
1002
_register_revspec(RevisionSpec_last)
1003
_register_revspec(RevisionSpec_before)
1004
_register_revspec(RevisionSpec_tag)
1005
_register_revspec(RevisionSpec_date)
1006
_register_revspec(RevisionSpec_ancestor)
1007
_register_revspec(RevisionSpec_branch)
1008
_register_revspec(RevisionSpec_submit)
1009
_register_revspec(RevisionSpec_annotate)
1010
_register_revspec(RevisionSpec_mainline)
344
if revision_b is None:
345
raise NoCommits(other_branch)
346
# pull in the remote revisions so we can diff
347
greedy_fetch(branch, other_branch, revision=revision_b)
349
revno = branch.revision_id_to_revno(revision_b)
350
except NoSuchRevision:
352
return RevisionInfo(branch, revno, revision_b)
354
SPEC_TYPES.append(RevisionSpec_branch)