1
# Copyright (C) 2005-2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
from bzrlib.lazy_import import lazy_import
19
lazy_import(globals(), """
24
branch as _mod_branch,
30
from bzrlib.i18n import gettext
41
class RevisionInfo(object):
42
"""The results of applying a revision specification to a branch."""
44
help_txt = """The results of applying a revision specification to a branch.
46
An instance has two useful attributes: revno, and rev_id.
48
They can also be accessed as spec[0] and spec[1] respectively,
49
so that you can write code like:
50
revno, rev_id = RevisionSpec(branch, spec)
51
although this is probably going to be deprecated later.
53
This class exists mostly to be the return value of a RevisionSpec,
54
so that you can access the member you're interested in (number or id)
55
or treat the result as a tuple.
58
def __init__(self, branch, revno=None, rev_id=None):
60
self._has_revno = (revno is not None)
63
if self.rev_id is None and self._revno is not None:
64
# allow caller to be lazy
65
self.rev_id = branch.get_rev_id(self._revno)
69
if not self._has_revno and self.rev_id is not None:
71
self._revno = self.branch.revision_id_to_revno(self.rev_id)
72
except errors.NoSuchRevision:
74
self._has_revno = True
77
def __nonzero__(self):
78
# first the easy ones...
79
if self.rev_id is None:
81
if self.revno is not None:
83
# TODO: otherwise, it should depend on how I was built -
84
# if it's in_history(branch), then check revision_history(),
85
# if it's in_store(branch), do the check below
86
return self.branch.repository.has_revision(self.rev_id)
91
def __getitem__(self, index):
92
if index == 0: return self.revno
93
if index == 1: return self.rev_id
94
raise IndexError(index)
97
return self.branch.repository.get_revision(self.rev_id)
99
def __eq__(self, other):
100
if type(other) not in (tuple, list, type(self)):
102
if type(other) is type(self) and self.branch is not other.branch:
104
return tuple(self) == tuple(other)
107
return '<bzrlib.revisionspec.RevisionInfo object %s, %s for %r>' % (
108
self.revno, self.rev_id, self.branch)
111
def from_revision_id(branch, revision_id, revs=symbol_versioning.DEPRECATED_PARAMETER):
112
"""Construct a RevisionInfo given just the id.
114
Use this if you don't know or care what the revno is.
116
if symbol_versioning.deprecated_passed(revs):
117
symbol_versioning.warn(
118
'RevisionInfo.from_revision_id(revs) was deprecated in 2.5.',
121
return RevisionInfo(branch, revno=None, rev_id=revision_id)
124
class RevisionSpec(object):
125
"""A parsed revision specification."""
127
help_txt = """A parsed revision specification.
129
A revision specification is a string, which may be unambiguous about
130
what it represents by giving a prefix like 'date:' or 'revid:' etc,
131
or it may have no prefix, in which case it's tried against several
132
specifier types in sequence to determine what the user meant.
134
Revision specs are an UI element, and they have been moved out
135
of the branch class to leave "back-end" classes unaware of such
136
details. Code that gets a revno or rev_id from other code should
137
not be using revision specs - revnos and revision ids are the
138
accepted ways to refer to revisions internally.
140
(Equivalent to the old Branch method get_revision_info())
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
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)
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
193
if self.prefix and spec.startswith(self.prefix):
194
spec = spec[len(self.prefix):]
197
def _match_on(self, branch, revs):
198
trace.mutter('Returning RevisionSpec._match_on: None')
199
return RevisionInfo(branch, None, None)
201
def _match_on_and_check(self, branch, revs):
202
info = self._match_on(branch, revs)
205
elif info == (None, None):
206
# special case - nothing supplied
209
raise errors.InvalidRevisionSpec(self.user_spec, branch)
211
raise errors.InvalidRevisionSpec(self.spec, branch)
213
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
234
return self._match_on_and_check(branch, revs)
236
# FIXME: in_history is somewhat broken,
237
# it will return non-history revisions in many
238
# circumstances. The expected facility is that
239
# in_history only returns revision-history revs,
240
# in_store returns any rev. RBC 20051010
241
# aliases for now, when we fix the core logic, then they
242
# will do what you expect.
243
in_store = in_history
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)
282
# 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.
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)
325
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))
374
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.
397
def _match_on(self, branch, revs):
398
"""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'
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)
508
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
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
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)
559
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
583
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.')
628
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
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()
667
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(
689
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
691
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
694
def _match_on(self, branch, revs):
695
"""Spec for date revisions:
697
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
698
matches the first entry after a given date (either at midnight or
699
at a specified time).
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
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
705
if self.spec.lower() == 'yesterday':
706
dt = today - datetime.timedelta(days=1)
707
elif self.spec.lower() == 'today':
709
elif self.spec.lower() == 'tomorrow':
710
dt = today + datetime.timedelta(days=1)
712
m = self._date_regex.match(self.spec)
713
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')
740
dt = datetime.datetime(year=year, month=month, day=day,
741
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)
753
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
775
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
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)
818
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
830
dwim_catchable_exceptions = (errors.NotBranchError,)
832
def _match_on(self, branch, revs):
833
from bzrlib.branch import Branch
834
other_branch = Branch.open(self.spec)
835
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)