108
def __new__(cls, spec, foo=_marker):
109
"""Parse a revision specifier.
143
# wants_revision_history has been deprecated in 2.5.
144
wants_revision_history = False
145
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
146
"""Exceptions that RevisionSpec_dwim._match_on will catch.
148
If the revspec is part of ``dwim_revspecs``, it may be tried with an
149
invalid revspec and raises some exception. The exceptions mentioned here
150
will not be reported to the user but simply ignored without stopping the
155
def from_string(spec):
156
"""Parse a revision spec string into a RevisionSpec object.
158
:param spec: A string specified by the user
159
:return: A RevisionSpec object that understands how to parse the
162
if not isinstance(spec, (type(None), basestring)):
163
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' %
166
return RevisionSpec(None, _internal=True)
167
match = revspec_registry.get_prefix(spec)
168
if match is not None:
169
spectype, specsuffix = match
170
trace.mutter('Returning RevisionSpec %s for %s',
171
spectype.__name__, spec)
172
return spectype(spec, _internal=True)
129
raise TypeError('Unhandled revision type %s' % spec)
131
def __init__(self, spec):
174
# Otherwise treat it as a DWIM, build the RevisionSpec object and
175
# wait for _match_on to be called.
176
return RevisionSpec_dwim(spec, _internal=True)
178
def __init__(self, spec, _internal=False):
179
"""Create a RevisionSpec referring to the Null revision.
181
:param spec: The original spec supplied by the user
182
:param _internal: Used to ensure that RevisionSpec is not being
183
called directly. Only from RevisionSpec.from_string()
186
symbol_versioning.warn('Creating a RevisionSpec directly has'
187
' been deprecated in version 0.11. Use'
188
' RevisionSpec.from_string()'
190
DeprecationWarning, stacklevel=2)
191
self.user_spec = spec
132
192
if self.prefix and spec.startswith(self.prefix):
133
193
spec = spec[len(self.prefix):]
136
196
def _match_on(self, branch, revs):
137
return RevisionInfo(branch, 0, None)
197
trace.mutter('Returning RevisionSpec._match_on: None')
198
return RevisionInfo(branch, None, None)
139
200
def _match_on_and_check(self, branch, revs):
140
201
info = self._match_on(branch, revs)
143
elif info == (0, None):
144
# special case - the empty tree
204
elif info == (None, None):
205
# special case - nothing supplied
146
207
elif self.prefix:
147
raise NoSuchRevision(branch, self.prefix + str(self.spec))
208
raise errors.InvalidRevisionSpec(self.user_spec, branch)
149
raise NoSuchRevision(branch, str(self.spec))
210
raise errors.InvalidRevisionSpec(self.spec, branch)
151
212
def in_history(self, branch):
153
revs = branch.revision_history()
214
if self.wants_revision_history:
215
symbol_versioning.warn(
216
"RevisionSpec.wants_revision_history was "
217
"deprecated in 2.5 (%s)." % self.__class__.__name__,
221
graph = branch.repository.get_graph()
222
revs = list(graph.iter_lefthand_ancestry(
223
branch.last_revision(), [revision.NULL_REVISION]))
230
# this should never trigger.
231
# TODO: make it a deprecated code path. RBC 20060928
156
233
return self._match_on_and_check(branch, revs)
164
241
# will do what you expect.
165
242
in_store = in_history
166
243
in_branch = in_store
245
def as_revision_id(self, context_branch):
246
"""Return just the revision_id for this revisions spec.
248
Some revision specs require a context_branch to be able to determine
249
their value. Not all specs will make use of it.
251
return self._as_revision_id(context_branch)
253
def _as_revision_id(self, context_branch):
254
"""Implementation of as_revision_id()
256
Classes should override this function to provide appropriate
257
functionality. The default is to just call '.in_history().rev_id'
259
return self.in_history(context_branch).rev_id
261
def as_tree(self, context_branch):
262
"""Return the tree object for this revisions spec.
264
Some revision specs require a context_branch to be able to determine
265
the revision id and access the repository. Not all specs will make
268
return self._as_tree(context_branch)
270
def _as_tree(self, context_branch):
271
"""Implementation of as_tree().
273
Classes should override this function to provide appropriate
274
functionality. The default is to just call '.as_revision_id()'
275
and get the revision tree from context_branch's repository.
277
revision_id = self.as_revision_id(context_branch)
278
return context_branch.repository.revision_tree(revision_id)
168
280
def __repr__(self):
169
281
# this is mostly for helping with testing
170
return '<%s %s%s>' % (self.__class__.__name__,
282
return '<%s %s>' % (self.__class__.__name__,
285
def needs_branch(self):
286
"""Whether this revision spec needs a branch.
288
Set this to False the branch argument of _match_on is not used.
292
def get_branch(self):
293
"""When the revision specifier contains a branch location, return it.
295
Otherwise, return None.
177
class RevisionSpec_int(RevisionSpec):
178
"""Spec is a number. Special case."""
179
def __init__(self, spec):
180
self.spec = int(spec)
302
class RevisionSpec_dwim(RevisionSpec):
303
"""Provides a DWIMish revision specifier lookup.
305
Note that this does not go in the revspec_registry because by definition
306
there is no prefix to identify it. It's solely called from
307
RevisionSpec.from_string() because the DWIMification happen when _match_on
308
is called so the string describing the revision is kept here until needed.
313
_revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
315
# The revspecs to try
316
_possible_revspecs = []
318
def _try_spectype(self, rstype, branch):
319
rs = rstype(self.spec, _internal=True)
320
# Hit in_history to find out if it exists, or we need to try the
322
return rs.in_history(branch)
182
324
def _match_on(self, branch, revs):
184
revno = len(revs) + self.spec + 1
187
rev_id = branch.get_rev_id(revno, revs)
188
return RevisionInfo(branch, revno, rev_id)
325
"""Run the lookup and see what we can get."""
327
# First, see if it's a revno
328
if self._revno_regex.match(self.spec) is not None:
330
return self._try_spectype(RevisionSpec_revno, branch)
331
except RevisionSpec_revno.dwim_catchable_exceptions:
334
# Next see what has been registered
335
for objgetter in self._possible_revspecs:
336
rs_class = objgetter.get_obj()
338
return self._try_spectype(rs_class, branch)
339
except rs_class.dwim_catchable_exceptions:
342
# Try the old (deprecated) dwim list:
343
for rs_class in dwim_revspecs:
345
return self._try_spectype(rs_class, branch)
346
except rs_class.dwim_catchable_exceptions:
349
# Well, I dunno what it is. Note that we don't try to keep track of the
350
# first of last exception raised during the DWIM tries as none seems
352
raise errors.InvalidRevisionSpec(self.spec, branch)
355
def append_possible_revspec(cls, revspec):
356
"""Append a possible DWIM revspec.
358
:param revspec: Revision spec to try.
360
cls._possible_revspecs.append(registry._ObjectGetter(revspec))
363
def append_possible_lazy_revspec(cls, module_name, member_name):
364
"""Append a possible lazily loaded DWIM revspec.
366
:param module_name: Name of the module with the revspec
367
:param member_name: Name of the revspec within the module
369
cls._possible_revspecs.append(
370
registry._LazyObjectGetter(module_name, member_name))
191
373
class RevisionSpec_revno(RevisionSpec):
374
"""Selects a revision using a number."""
376
help_txt = """Selects a revision using a number.
378
Use an integer to specify a revision in the history of the branch.
379
Optionally a branch can be specified. A negative number will count
380
from the end of the branch (-1 is the last revision, -2 the previous
381
one). If the negative number is larger than the branch's history, the
382
first revision is returned.
385
revno:1 -> return the first revision of this branch
386
revno:3:/path/to/branch -> return the 3rd revision of
387
the branch '/path/to/branch'
388
revno:-1 -> The last revision in a branch.
389
-2:http://other/branch -> The second to last revision in the
391
-1000000 -> Most likely the first revision, unless
392
your history is very long.
192
394
prefix = 'revno:'
194
396
def _match_on(self, branch, revs):
195
397
"""Lookup a revision by revision number"""
398
branch, revno, revision_id = self._lookup(branch)
399
return RevisionInfo(branch, revno, revision_id)
401
def _lookup(self, branch):
402
loc = self.spec.find(':')
404
revno_spec = self.spec
407
revno_spec = self.spec[:loc]
408
branch_spec = self.spec[loc+1:]
412
raise errors.InvalidRevisionSpec(self.user_spec,
413
branch, 'cannot have an empty revno and no branch')
417
revno = int(revno_spec)
420
# dotted decimal. This arguably should not be here
421
# but the from_string method is a little primitive
422
# right now - RBC 20060928
424
match_revno = tuple((int(number) for number in revno_spec.split('.')))
425
except ValueError, e:
426
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
431
# the user has overriden the branch to look in.
432
branch = _mod_branch.Branch.open(branch_spec)
436
revision_id = branch.dotted_revno_to_revision_id(match_revno,
438
except errors.NoSuchRevision:
439
raise errors.InvalidRevisionSpec(self.user_spec, branch)
441
# there is no traditional 'revno' for dotted-decimal revnos.
442
# so for API compatibility we return None.
443
return branch, None, revision_id
445
last_revno, last_revision_id = branch.last_revision_info()
447
# if get_rev_id supported negative revnos, there would not be a
448
# need for this special case.
449
if (-revno) >= last_revno:
452
revno = last_revno + revno + 1
454
revision_id = branch.get_rev_id(revno)
455
except errors.NoSuchRevision:
456
raise errors.InvalidRevisionSpec(self.user_spec, branch)
457
return branch, revno, revision_id
459
def _as_revision_id(self, context_branch):
460
# We would have the revno here, but we don't really care
461
branch, revno, revision_id = self._lookup(context_branch)
464
def needs_branch(self):
465
return self.spec.find(':') == -1
467
def get_branch(self):
196
468
if self.spec.find(':') == -1:
198
return RevisionInfo(branch, int(self.spec))
200
return RevisionInfo(branch, None)
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):
471
return self.spec[self.spec.find(':')+1:]
474
RevisionSpec_int = RevisionSpec_revno
477
class RevisionIDSpec(RevisionSpec):
479
def _match_on(self, branch, revs):
480
revision_id = self.as_revision_id(branch)
481
return RevisionInfo.from_revision_id(branch, revision_id)
484
class RevisionSpec_revid(RevisionIDSpec):
485
"""Selects a revision using the revision id."""
487
help_txt = """Selects a revision using the revision id.
489
Supply a specific revision id, that can be used to specify any
490
revision id in the ancestry of the branch.
491
Including merges, and pending merges.
494
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
216
497
prefix = 'revid:'
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)
499
def _as_revision_id(self, context_branch):
500
# self.spec comes straight from parsing the command line arguments,
501
# so we expect it to be a Unicode string. Switch it to the internal
503
return osutils.safe_revision_id(self.spec, warn=False)
224
SPEC_TYPES.append(RevisionSpec_revid)
227
507
class RevisionSpec_last(RevisionSpec):
508
"""Selects the nth revision from the end."""
510
help_txt = """Selects the nth revision from the end.
512
Supply a positive number to get the nth revision from the end.
513
This is the same as supplying negative numbers to the 'revno:' spec.
516
last:1 -> return the last revision
517
last:3 -> return the revision 2 before the end.
231
522
def _match_on(self, branch, revs):
523
revno, revision_id = self._revno_and_revision_id(branch)
524
return RevisionInfo(branch, revno, revision_id)
526
def _revno_and_revision_id(self, context_branch):
527
last_revno, last_revision_id = context_branch.last_revision_info()
531
raise errors.NoCommits(context_branch)
532
return last_revno, last_revision_id
233
535
offset = int(self.spec)
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)
536
except ValueError, e:
537
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
540
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
541
'you must supply a positive value')
543
revno = last_revno - offset + 1
545
revision_id = context_branch.get_rev_id(revno)
546
except errors.NoSuchRevision:
547
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
548
return revno, revision_id
550
def _as_revision_id(self, context_branch):
551
# We compute the revno as part of the process, but we don't really care
553
revno, revision_id = self._revno_and_revision_id(context_branch)
244
558
class RevisionSpec_before(RevisionSpec):
559
"""Selects the parent of the revision specified."""
561
help_txt = """Selects the parent of the revision specified.
563
Supply any revision spec to return the parent of that revision. This is
564
mostly useful when inspecting revisions that are not in the revision history
567
It is an error to request the parent of the null revision (before:0).
571
before:1913 -> Return the parent of revno 1913 (revno 1912)
572
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
574
bzr diff -r before:1913..1913
575
-> Find the changes between revision 1913 and its parent (1912).
576
(What changes did revision 1913 introduce).
577
This is equivalent to: bzr diff -c 1913
246
580
prefix = 'before:'
248
582
def _match_on(self, branch, revs):
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)
583
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
585
raise errors.InvalidRevisionSpec(self.user_spec, branch,
586
'cannot go before the null: revision')
588
# We need to use the repository history here
589
rev = branch.repository.get_revision(r.rev_id)
590
if not rev.parent_ids:
591
revision_id = revision.NULL_REVISION
593
revision_id = rev.parent_ids[0]
598
revision_id = branch.get_rev_id(revno, revs)
599
except errors.NoSuchRevision:
600
raise errors.InvalidRevisionSpec(self.user_spec,
602
return RevisionInfo(branch, revno, revision_id)
604
def _as_revision_id(self, context_branch):
605
base_revision_id = RevisionSpec.from_string(self.spec)._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.')
257
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)
260
638
def _match_on(self, branch, revs):
261
raise BzrError('tag: namespace registered, but not implemented.')
263
SPEC_TYPES.append(RevisionSpec_tag)
266
class RevisionSpec_revs:
267
def __init__(self, revs, branch):
639
# Can raise tags not supported, NoSuchTag, etc
640
return RevisionInfo.from_revision_id(branch,
641
branch.tags.lookup_tag(self.spec))
643
def _as_revision_id(self, context_branch):
644
return context_branch.tags.lookup_tag(self.spec)
648
class _RevListToTimestamps(object):
649
"""This takes a list of revisions, and allows you to bisect by date"""
651
__slots__ = ['branch']
653
def __init__(self, branch):
269
654
self.branch = branch
270
656
def __getitem__(self, index):
271
r = self.branch.repository.get_revision(self.revs[index])
657
"""Get the date of the index'd item"""
658
r = self.branch.repository.get_revision(self.branch.get_rev_id(index))
272
659
# TODO: Handle timezone.
273
660
return datetime.datetime.fromtimestamp(r.timestamp)
274
662
def __len__(self):
275
return len(self.revs)
663
return self.branch.revno()
278
666
class RevisionSpec_date(RevisionSpec):
667
"""Selects a revision on the basis of a datestamp."""
669
help_txt = """Selects a revision on the basis of a datestamp.
671
Supply a datestamp to select the first revision that matches the date.
672
Date can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
673
Matches the first entry after a given date (either at midnight or
674
at a specified time).
676
One way to display all the changes since yesterday would be::
678
bzr log -r date:yesterday..
682
date:yesterday -> select the first revision since yesterday
683
date:2006-08-14,17:10:14 -> select the first revision after
684
August 14th, 2006 at 5:10pm.
280
_date_re = re.compile(
687
_date_regex = lazy_regex.lazy_compile(
281
688
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
283
690
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
286
693
def _match_on(self, branch, revs):
288
Spec for date revisions:
694
"""Spec for date revisions:
290
696
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
291
697
matches the first entry after a given date (either at midnight or
292
698
at a specified time).
294
So the proper way of saying 'give me all entries for today' is:
295
-r date:today..date:tomorrow
700
# XXX: This doesn't actually work
701
# So the proper way of saying 'give me all entries for today' is:
702
# -r date:yesterday..date:today
297
703
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
298
704
if self.spec.lower() == 'yesterday':
299
705
dt = today - datetime.timedelta(days=1)
302
708
elif self.spec.lower() == 'tomorrow':
303
709
dt = today + datetime.timedelta(days=1)
305
m = self._date_re.match(self.spec)
711
m = self._date_regex.match(self.spec)
306
712
if not m or (not m.group('date') and not m.group('time')):
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
713
raise errors.InvalidRevisionSpec(self.user_spec,
714
branch, 'invalid date')
718
year = int(m.group('year'))
719
month = int(m.group('month'))
720
day = int(m.group('day'))
727
hour = int(m.group('hour'))
728
minute = int(m.group('minute'))
729
if m.group('second'):
730
second = int(m.group('second'))
734
hour, minute, second = 0,0,0
736
raise errors.InvalidRevisionSpec(self.user_spec,
737
branch, 'invalid date')
323
739
dt = datetime.datetime(year=year, month=month, day=day,
324
740
hour=hour, minute=minute, second=second)
325
741
branch.lock_read()
327
rev = bisect.bisect(RevisionSpec_revs(revs, branch), dt)
743
rev = bisect.bisect(_RevListToTimestamps(branch), dt, 1)
331
return RevisionInfo(branch, None)
333
return RevisionInfo(branch, rev + 1)
746
if rev == branch.revno():
747
raise errors.InvalidRevisionSpec(self.user_spec, branch)
748
return RevisionInfo(branch, rev)
335
SPEC_TYPES.append(RevisionSpec_date)
338
752
class RevisionSpec_ancestor(RevisionSpec):
753
"""Selects a common ancestor with a second branch."""
755
help_txt = """Selects a common ancestor with a second branch.
757
Supply the path to a branch to select the common ancestor.
759
The common ancestor is the last revision that existed in both
760
branches. Usually this is the branch point, but it could also be
761
a revision that was merged.
763
This is frequently used with 'diff' to return all of the changes
764
that your branch introduces, while excluding the changes that you
765
have not merged from the remote branch.
769
ancestor:/path/to/branch
770
$ bzr diff -r ancestor:../../mainline/branch
339
772
prefix = 'ancestor:'
341
774
def _match_on(self, branch, revs):
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)
775
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
776
return self._find_revision_info(branch, self.spec)
778
def _as_revision_id(self, context_branch):
779
return self._find_revision_id(context_branch, self.spec)
782
def _find_revision_info(branch, other_location):
783
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
785
return RevisionInfo(branch, None, revision_id)
788
def _find_revision_id(branch, other_location):
789
from bzrlib.branch import Branch
354
revno = branch.revision_id_to_revno(rev_id)
355
except NoSuchRevision:
357
return RevisionInfo(branch, revno, rev_id)
359
SPEC_TYPES.append(RevisionSpec_ancestor)
793
revision_a = revision.ensure_null(branch.last_revision())
794
if revision_a == revision.NULL_REVISION:
795
raise errors.NoCommits(branch)
796
if other_location == '':
797
other_location = branch.get_parent()
798
other_branch = Branch.open(other_location)
799
other_branch.lock_read()
801
revision_b = revision.ensure_null(other_branch.last_revision())
802
if revision_b == revision.NULL_REVISION:
803
raise errors.NoCommits(other_branch)
804
graph = branch.repository.get_graph(other_branch.repository)
805
rev_id = graph.find_unique_lca(revision_a, revision_b)
807
other_branch.unlock()
808
if rev_id == revision.NULL_REVISION:
809
raise errors.NoCommonAncestor(revision_a, revision_b)
361
817
class RevisionSpec_branch(RevisionSpec):
362
"""A branch: revision specifier.
364
This takes the path to a branch and returns its tip revision id.
818
"""Selects the last revision of a specified branch."""
820
help_txt = """Selects the last revision of a specified branch.
822
Supply the path to a branch to select its last revision.
826
branch:/path/to/branch
366
828
prefix = 'branch:'
829
dwim_catchable_exceptions = (errors.NotBranchError,)
368
831
def _match_on(self, branch, revs):
369
from branch import Branch
370
other_branch = Branch.open_containing(self.spec)[0]
832
from bzrlib.branch import Branch
833
other_branch = Branch.open(self.spec)
371
834
revision_b = other_branch.last_revision()
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)
377
revno = branch.revision_id_to_revno(revision_b)
378
except NoSuchRevision:
380
return RevisionInfo(branch, revno, revision_b)
382
SPEC_TYPES.append(RevisionSpec_branch)
835
if revision_b in (None, revision.NULL_REVISION):
836
raise errors.NoCommits(other_branch)
838
branch = other_branch
841
# pull in the remote revisions so we can diff
842
branch.fetch(other_branch, revision_b)
843
except errors.ReadOnlyError:
844
branch = other_branch
845
return RevisionInfo(branch, None, revision_b)
847
def _as_revision_id(self, context_branch):
848
from bzrlib.branch import Branch
849
other_branch = Branch.open(self.spec)
850
last_revision = other_branch.last_revision()
851
last_revision = revision.ensure_null(last_revision)
852
context_branch.fetch(other_branch, last_revision)
853
if last_revision == revision.NULL_REVISION:
854
raise errors.NoCommits(other_branch)
857
def _as_tree(self, context_branch):
858
from bzrlib.branch import Branch
859
other_branch = Branch.open(self.spec)
860
last_revision = other_branch.last_revision()
861
last_revision = revision.ensure_null(last_revision)
862
if last_revision == revision.NULL_REVISION:
863
raise errors.NoCommits(other_branch)
864
return other_branch.repository.revision_tree(last_revision)
866
def needs_branch(self):
869
def get_branch(self):
874
class RevisionSpec_submit(RevisionSpec_ancestor):
875
"""Selects a common ancestor with a submit branch."""
877
help_txt = """Selects a common ancestor with the submit branch.
879
Diffing against this shows all the changes that were made in this branch,
880
and is a good predictor of what merge will do. The submit branch is
881
used by the bundle and merge directive commands. If no submit branch
882
is specified, the parent branch is used instead.
884
The common ancestor is the last revision that existed in both
885
branches. Usually this is the branch point, but it could also be
886
a revision that was merged.
890
$ bzr diff -r submit:
895
def _get_submit_location(self, branch):
896
submit_location = branch.get_submit_branch()
897
location_type = 'submit branch'
898
if submit_location is None:
899
submit_location = branch.get_parent()
900
location_type = 'parent branch'
901
if submit_location is None:
902
raise errors.NoSubmitBranch(branch)
903
trace.note(gettext('Using {0} {1}').format(location_type,
905
return submit_location
907
def _match_on(self, branch, revs):
908
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
909
return self._find_revision_info(branch,
910
self._get_submit_location(branch))
912
def _as_revision_id(self, context_branch):
913
return self._find_revision_id(context_branch,
914
self._get_submit_location(context_branch))
917
class RevisionSpec_annotate(RevisionIDSpec):
921
help_txt = """Select the revision that last modified the specified line.
923
Select the revision that last modified the specified line. Line is
924
specified as path:number. Path is a relative path to the file. Numbers
925
start at 1, and are relative to the current version, not the last-
926
committed version of the file.
929
def _raise_invalid(self, numstring, context_branch):
930
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
931
'No such line: %s' % numstring)
933
def _as_revision_id(self, context_branch):
934
path, numstring = self.spec.rsplit(':', 1)
936
index = int(numstring) - 1
938
self._raise_invalid(numstring, context_branch)
939
tree, file_path = workingtree.WorkingTree.open_containing(path)
942
file_id = tree.path2id(file_path)
944
raise errors.InvalidRevisionSpec(self.user_spec,
945
context_branch, "File '%s' is not versioned." %
947
revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
951
revision_id = revision_ids[index]
953
self._raise_invalid(numstring, context_branch)
954
if revision_id == revision.CURRENT_REVISION:
955
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
956
'Line %s has not been committed.' % numstring)
960
class RevisionSpec_mainline(RevisionIDSpec):
962
help_txt = """Select mainline revision that merged the specified revision.
964
Select the revision that merged the specified revision into mainline.
969
def _as_revision_id(self, context_branch):
970
revspec = RevisionSpec.from_string(self.spec)
971
if revspec.get_branch() is None:
972
spec_branch = context_branch
974
spec_branch = _mod_branch.Branch.open(revspec.get_branch())
975
revision_id = revspec.as_revision_id(spec_branch)
976
graph = context_branch.repository.get_graph()
977
result = graph.find_lefthand_merger(revision_id,
978
context_branch.last_revision())
980
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
984
# The order in which we want to DWIM a revision spec without any prefix.
985
# revno is always tried first and isn't listed here, this is used by
986
# RevisionSpec_dwim._match_on
987
dwim_revspecs = symbol_versioning.deprecated_list(
988
symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
990
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
991
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
992
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
993
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
995
revspec_registry = registry.Registry()
996
def _register_revspec(revspec):
997
revspec_registry.register(revspec.prefix, revspec)
999
_register_revspec(RevisionSpec_revno)
1000
_register_revspec(RevisionSpec_revid)
1001
_register_revspec(RevisionSpec_last)
1002
_register_revspec(RevisionSpec_before)
1003
_register_revspec(RevisionSpec_tag)
1004
_register_revspec(RevisionSpec_date)
1005
_register_revspec(RevisionSpec_ancestor)
1006
_register_revspec(RevisionSpec_branch)
1007
_register_revspec(RevisionSpec_submit)
1008
_register_revspec(RevisionSpec_annotate)
1009
_register_revspec(RevisionSpec_mainline)