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
44
class RevisionInfo(object):
45
"""The results of applying a revision specification to a branch."""
47
help_txt = """The results of applying a revision specification to a branch.
49
An instance has two useful attributes: revno, and rev_id.
51
They can also be accessed as spec[0] and spec[1] respectively,
52
so that you can write code like:
53
revno, rev_id = RevisionSpec(branch, spec)
54
although this is probably going to be deprecated later.
56
This class exists mostly to be the return value of a RevisionSpec,
57
so that you can access the member you're interested in (number or id)
58
or treat the result as a tuple.
61
def __init__(self, branch, revno, rev_id=_marker):
65
# allow caller to be lazy
66
if self.revno is None:
69
self.rev_id = branch.get_rev_id(self.revno)
73
def __nonzero__(self):
74
# first the easy ones...
75
if self.rev_id is None:
77
if self.revno is not None:
79
# TODO: otherwise, it should depend on how I was built -
80
# if it's in_history(branch), then check revision_history(),
81
# if it's in_store(branch), do the check below
82
return self.branch.repository.has_revision(self.rev_id)
87
def __getitem__(self, index):
88
if index == 0: return self.revno
89
if index == 1: return self.rev_id
90
raise IndexError(index)
93
return self.branch.repository.get_revision(self.rev_id)
95
def __eq__(self, other):
96
if type(other) not in (tuple, list, type(self)):
98
if type(other) is type(self) and self.branch is not other.branch:
100
return tuple(self) == tuple(other)
103
return '<bzrlib.revisionspec.RevisionInfo object %s, %s for %r>' % (
104
self.revno, self.rev_id, self.branch)
107
def from_revision_id(branch, revision_id, revs):
108
"""Construct a RevisionInfo given just the id.
110
Use this if you don't know or care what the revno is.
112
if revision_id == revision.NULL_REVISION:
113
return RevisionInfo(branch, 0, revision_id)
115
revno = revs.index(revision_id) + 1
118
return RevisionInfo(branch, revno, revision_id)
121
class RevisionSpec(object):
122
"""A parsed revision specification."""
124
help_txt = """A parsed revision specification.
126
A revision specification is a string, which may be unambiguous about
127
what it represents by giving a prefix like 'date:' or 'revid:' etc,
128
or it may have no prefix, in which case it's tried against several
129
specifier types in sequence to determine what the user meant.
131
Revision specs are an UI element, and they have been moved out
132
of the branch class to leave "back-end" classes unaware of such
133
details. Code that gets a revno or rev_id from other code should
134
not be using revision specs - revnos and revision ids are the
135
accepted ways to refer to revisions internally.
137
(Equivalent to the old Branch method get_revision_info())
141
wants_revision_history = True
142
dwim_catchable_exceptions = (errors.InvalidRevisionSpec,)
143
"""Exceptions that RevisionSpec_dwim._match_on will catch.
145
If the revspec is part of ``dwim_revspecs``, it may be tried with an
146
invalid revspec and raises some exception. The exceptions mentioned here
147
will not be reported to the user but simply ignored without stopping the
152
def from_string(spec):
153
"""Parse a revision spec string into a RevisionSpec object.
155
:param spec: A string specified by the user
156
:return: A RevisionSpec object that understands how to parse the
159
if not isinstance(spec, (type(None), basestring)):
160
raise TypeError('error')
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)
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
189
if self.prefix and spec.startswith(self.prefix):
190
spec = spec[len(self.prefix):]
193
def _match_on(self, branch, revs):
194
trace.mutter('Returning RevisionSpec._match_on: None')
195
return RevisionInfo(branch, None, None)
197
def _match_on_and_check(self, branch, revs):
198
info = self._match_on(branch, revs)
201
elif info == (None, None):
202
# special case - nothing supplied
205
raise errors.InvalidRevisionSpec(self.user_spec, branch)
207
raise errors.InvalidRevisionSpec(self.spec, branch)
209
def in_history(self, branch):
211
if self.wants_revision_history:
212
# TODO: avoid looking at all of history
215
graph = branch.repository.get_graph()
216
revs = list(graph.iter_lefthand_ancestry(
217
branch.last_revision(), [revision.NULL_REVISION]))
224
# this should never trigger.
225
# TODO: make it a deprecated code path. RBC 20060928
227
return self._match_on_and_check(branch, revs)
229
# FIXME: in_history is somewhat broken,
230
# it will return non-history revisions in many
231
# circumstances. The expected facility is that
232
# in_history only returns revision-history revs,
233
# in_store returns any rev. RBC 20051010
234
# aliases for now, when we fix the core logic, then they
235
# will do what you expect.
236
in_store = in_history
239
def as_revision_id(self, context_branch):
240
"""Return just the revision_id for this revisions spec.
242
Some revision specs require a context_branch to be able to determine
243
their value. Not all specs will make use of it.
245
return self._as_revision_id(context_branch)
247
def _as_revision_id(self, context_branch):
248
"""Implementation of as_revision_id()
250
Classes should override this function to provide appropriate
251
functionality. The default is to just call '.in_history().rev_id'
253
return self.in_history(context_branch).rev_id
255
def as_tree(self, context_branch):
256
"""Return the tree object for this revisions spec.
258
Some revision specs require a context_branch to be able to determine
259
the revision id and access the repository. Not all specs will make
262
return self._as_tree(context_branch)
264
def _as_tree(self, context_branch):
265
"""Implementation of as_tree().
267
Classes should override this function to provide appropriate
268
functionality. The default is to just call '.as_revision_id()'
269
and get the revision tree from context_branch's repository.
271
revision_id = self.as_revision_id(context_branch)
272
return context_branch.repository.revision_tree(revision_id)
275
# this is mostly for helping with testing
276
return '<%s %s>' % (self.__class__.__name__,
279
def needs_branch(self):
280
"""Whether this revision spec needs a branch.
282
Set this to False the branch argument of _match_on is not used.
286
def get_branch(self):
287
"""When the revision specifier contains a branch location, return it.
289
Otherwise, return None.
296
class RevisionSpec_dwim(RevisionSpec):
297
"""Provides a DWIMish revision specifier lookup.
299
Note that this does not go in the revspec_registry because by definition
300
there is no prefix to identify it. It's solely called from
301
RevisionSpec.from_string() because the DWIMification happen when _match_on
302
is called so the string describing the revision is kept here until needed.
306
# We don't need to build the revision history ourself, that's delegated to
307
# each revspec we try.
308
wants_revision_history = False
310
_revno_regex = lazy_regex.lazy_compile(r'^(?:(\d+(\.\d+)*)|-\d+)(:.*)?$')
312
# The revspecs to try
313
_possible_revspecs = []
315
def _try_spectype(self, rstype, branch):
316
rs = rstype(self.spec, _internal=True)
317
# Hit in_history to find out if it exists, or we need to try the
319
return rs.in_history(branch)
321
def _match_on(self, branch, revs):
322
"""Run the lookup and see what we can get."""
324
# First, see if it's a revno
325
if self._revno_regex.match(self.spec) is not None:
327
return self._try_spectype(RevisionSpec_revno, branch)
328
except RevisionSpec_revno.dwim_catchable_exceptions:
331
# Next see what has been registered
332
for objgetter in self._possible_revspecs:
333
rs_class = objgetter.get_obj()
335
return self._try_spectype(rs_class, branch)
336
except rs_class.dwim_catchable_exceptions:
339
# Try the old (deprecated) dwim list:
340
for rs_class in dwim_revspecs:
342
return self._try_spectype(rs_class, branch)
343
except rs_class.dwim_catchable_exceptions:
346
# Well, I dunno what it is. Note that we don't try to keep track of the
347
# first of last exception raised during the DWIM tries as none seems
349
raise errors.InvalidRevisionSpec(self.spec, branch)
352
def append_possible_revspec(cls, revspec):
353
"""Append a possible DWIM revspec.
355
:param revspec: Revision spec to try.
357
cls._possible_revspecs.append(registry._ObjectGetter(revspec))
360
def append_possible_lazy_revspec(cls, module_name, member_name):
361
"""Append a possible lazily loaded DWIM revspec.
363
:param module_name: Name of the module with the revspec
364
:param member_name: Name of the revspec within the module
366
cls._possible_revspecs.append(
367
registry._LazyObjectGetter(module_name, member_name))
370
class RevisionSpec_revno(RevisionSpec):
371
"""Selects a revision using a number."""
373
help_txt = """Selects a revision using a number.
375
Use an integer to specify a revision in the history of the branch.
376
Optionally a branch can be specified. A negative number will count
377
from the end of the branch (-1 is the last revision, -2 the previous
378
one). If the negative number is larger than the branch's history, the
379
first revision is returned.
382
revno:1 -> return the first revision of this branch
383
revno:3:/path/to/branch -> return the 3rd revision of
384
the branch '/path/to/branch'
385
revno:-1 -> The last revision in a branch.
386
-2:http://other/branch -> The second to last revision in the
388
-1000000 -> Most likely the first revision, unless
389
your history is very long.
392
wants_revision_history = False
394
def _match_on(self, branch, revs):
395
"""Lookup a revision by revision number"""
396
branch, revno, revision_id = self._lookup(branch, revs)
397
return RevisionInfo(branch, revno, revision_id)
399
def _lookup(self, branch, revs_or_none):
400
loc = self.spec.find(':')
402
revno_spec = self.spec
405
revno_spec = self.spec[:loc]
406
branch_spec = self.spec[loc+1:]
410
raise errors.InvalidRevisionSpec(self.user_spec,
411
branch, 'cannot have an empty revno and no branch')
415
revno = int(revno_spec)
418
# dotted decimal. This arguably should not be here
419
# but the from_string method is a little primitive
420
# right now - RBC 20060928
422
match_revno = tuple((int(number) for number in revno_spec.split('.')))
423
except ValueError, e:
424
raise errors.InvalidRevisionSpec(self.user_spec, branch, e)
429
# the user has override the branch to look in.
430
# we need to refresh the revision_history map and
432
from bzrlib.branch import Branch
433
branch = Branch.open(branch_spec)
438
revision_id = branch.dotted_revno_to_revision_id(match_revno,
440
except errors.NoSuchRevision:
441
raise errors.InvalidRevisionSpec(self.user_spec, branch)
443
# there is no traditional 'revno' for dotted-decimal revnos.
444
# so for API compatability we return None.
445
return branch, None, revision_id
447
last_revno, last_revision_id = branch.last_revision_info()
449
# if get_rev_id supported negative revnos, there would not be a
450
# need for this special case.
451
if (-revno) >= last_revno:
454
revno = last_revno + revno + 1
456
revision_id = branch.get_rev_id(revno, revs_or_none)
457
except errors.NoSuchRevision:
458
raise errors.InvalidRevisionSpec(self.user_spec, branch)
459
return branch, revno, revision_id
461
def _as_revision_id(self, context_branch):
462
# We would have the revno here, but we don't really care
463
branch, revno, revision_id = self._lookup(context_branch, None)
466
def needs_branch(self):
467
return self.spec.find(':') == -1
469
def get_branch(self):
470
if self.spec.find(':') == -1:
473
return self.spec[self.spec.find(':')+1:]
476
RevisionSpec_int = RevisionSpec_revno
480
class RevisionIDSpec(RevisionSpec):
482
def _match_on(self, branch, revs):
483
revision_id = self.as_revision_id(branch)
484
return RevisionInfo.from_revision_id(branch, revision_id, revs)
487
class RevisionSpec_revid(RevisionIDSpec):
488
"""Selects a revision using the revision id."""
490
help_txt = """Selects a revision using the revision id.
492
Supply a specific revision id, that can be used to specify any
493
revision id in the ancestry of the branch.
494
Including merges, and pending merges.
497
revid:aaaa@bbbb-123456789 -> Select revision 'aaaa@bbbb-123456789'
502
def _as_revision_id(self, context_branch):
503
# self.spec comes straight from parsing the command line arguments,
504
# so we expect it to be a Unicode string. Switch it to the internal
506
return osutils.safe_revision_id(self.spec, warn=False)
510
class RevisionSpec_last(RevisionSpec):
511
"""Selects the nth revision from the end."""
513
help_txt = """Selects the nth revision from the end.
515
Supply a positive number to get the nth revision from the end.
516
This is the same as supplying negative numbers to the 'revno:' spec.
519
last:1 -> return the last revision
520
last:3 -> return the revision 2 before the end.
525
def _match_on(self, branch, revs):
526
revno, revision_id = self._revno_and_revision_id(branch, revs)
527
return RevisionInfo(branch, revno, revision_id)
529
def _revno_and_revision_id(self, context_branch, revs_or_none):
530
last_revno, last_revision_id = context_branch.last_revision_info()
534
raise errors.NoCommits(context_branch)
535
return last_revno, last_revision_id
538
offset = int(self.spec)
539
except ValueError, e:
540
raise errors.InvalidRevisionSpec(self.user_spec, context_branch, e)
543
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
544
'you must supply a positive value')
546
revno = last_revno - offset + 1
548
revision_id = context_branch.get_rev_id(revno, revs_or_none)
549
except errors.NoSuchRevision:
550
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
551
return revno, revision_id
553
def _as_revision_id(self, context_branch):
554
# We compute the revno as part of the process, but we don't really care
556
revno, revision_id = self._revno_and_revision_id(context_branch, None)
561
class RevisionSpec_before(RevisionSpec):
562
"""Selects the parent of the revision specified."""
564
help_txt = """Selects the parent of the revision specified.
566
Supply any revision spec to return the parent of that revision. This is
567
mostly useful when inspecting revisions that are not in the revision history
570
It is an error to request the parent of the null revision (before:0).
574
before:1913 -> Return the parent of revno 1913 (revno 1912)
575
before:revid:aaaa@bbbb-1234567890 -> return the parent of revision
577
bzr diff -r before:1913..1913
578
-> Find the changes between revision 1913 and its parent (1912).
579
(What changes did revision 1913 introduce).
580
This is equivalent to: bzr diff -c 1913
585
def _match_on(self, branch, revs):
586
r = RevisionSpec.from_string(self.spec)._match_on(branch, revs)
588
raise errors.InvalidRevisionSpec(self.user_spec, branch,
589
'cannot go before the null: revision')
591
# We need to use the repository history here
592
rev = branch.repository.get_revision(r.rev_id)
593
if not rev.parent_ids:
595
revision_id = revision.NULL_REVISION
597
revision_id = rev.parent_ids[0]
599
revno = revs.index(revision_id) + 1
605
revision_id = branch.get_rev_id(revno, revs)
606
except errors.NoSuchRevision:
607
raise errors.InvalidRevisionSpec(self.user_spec,
609
return RevisionInfo(branch, revno, revision_id)
611
def _as_revision_id(self, context_branch):
612
base_revspec = RevisionSpec.from_string(self.spec)
613
base_revision_id = base_revspec.as_revision_id(context_branch)
614
if base_revision_id == revision.NULL_REVISION:
615
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
616
'cannot go before the null: revision')
617
context_repo = context_branch.repository
618
context_repo.lock_read()
620
parent_map = context_repo.get_parent_map([base_revision_id])
622
context_repo.unlock()
623
if base_revision_id not in parent_map:
624
# Ghost, or unknown revision id
625
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
626
'cannot find the matching revision')
627
parents = parent_map[base_revision_id]
629
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
630
'No parents for revision.')
635
class RevisionSpec_tag(RevisionSpec):
636
"""Select a revision identified by tag name"""
638
help_txt = """Selects a revision identified by a tag name.
640
Tags are stored in the branch and created by the 'tag' command.
644
dwim_catchable_exceptions = (errors.NoSuchTag, errors.TagsNotSupported)
646
def _match_on(self, branch, revs):
647
# Can raise tags not supported, NoSuchTag, etc
648
return RevisionInfo.from_revision_id(branch,
649
branch.tags.lookup_tag(self.spec),
652
def _as_revision_id(self, context_branch):
653
return context_branch.tags.lookup_tag(self.spec)
657
class _RevListToTimestamps(object):
658
"""This takes a list of revisions, and allows you to bisect by date"""
660
__slots__ = ['revs', 'branch']
662
def __init__(self, revs, branch):
666
def __getitem__(self, index):
667
"""Get the date of the index'd item"""
668
r = self.branch.repository.get_revision(self.revs[index])
669
# TODO: Handle timezone.
670
return datetime.datetime.fromtimestamp(r.timestamp)
673
return len(self.revs)
676
class RevisionSpec_date(RevisionSpec):
677
"""Selects a revision on the basis of a datestamp."""
679
help_txt = """Selects a revision on the basis of a datestamp.
681
Supply a datestamp to select the first revision that matches the date.
682
Date can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
683
Matches the first entry after a given date (either at midnight or
684
at a specified time).
686
One way to display all the changes since yesterday would be::
688
bzr log -r date:yesterday..
692
date:yesterday -> select the first revision since yesterday
693
date:2006-08-14,17:10:14 -> select the first revision after
694
August 14th, 2006 at 5:10pm.
697
_date_regex = lazy_regex.lazy_compile(
698
r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
700
r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
703
def _match_on(self, branch, revs):
704
"""Spec for date revisions:
706
value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
707
matches the first entry after a given date (either at midnight or
708
at a specified time).
710
# XXX: This doesn't actually work
711
# So the proper way of saying 'give me all entries for today' is:
712
# -r date:yesterday..date:today
713
today = datetime.datetime.fromordinal(datetime.date.today().toordinal())
714
if self.spec.lower() == 'yesterday':
715
dt = today - datetime.timedelta(days=1)
716
elif self.spec.lower() == 'today':
718
elif self.spec.lower() == 'tomorrow':
719
dt = today + datetime.timedelta(days=1)
721
m = self._date_regex.match(self.spec)
722
if not m or (not m.group('date') and not m.group('time')):
723
raise errors.InvalidRevisionSpec(self.user_spec,
724
branch, 'invalid date')
728
year = int(m.group('year'))
729
month = int(m.group('month'))
730
day = int(m.group('day'))
737
hour = int(m.group('hour'))
738
minute = int(m.group('minute'))
739
if m.group('second'):
740
second = int(m.group('second'))
744
hour, minute, second = 0,0,0
746
raise errors.InvalidRevisionSpec(self.user_spec,
747
branch, 'invalid date')
749
dt = datetime.datetime(year=year, month=month, day=day,
750
hour=hour, minute=minute, second=second)
753
rev = bisect.bisect(_RevListToTimestamps(revs, branch), dt)
757
raise errors.InvalidRevisionSpec(self.user_spec, branch)
759
return RevisionInfo(branch, rev + 1)
763
class RevisionSpec_ancestor(RevisionSpec):
764
"""Selects a common ancestor with a second branch."""
766
help_txt = """Selects a common ancestor with a second branch.
768
Supply the path to a branch to select the common ancestor.
770
The common ancestor is the last revision that existed in both
771
branches. Usually this is the branch point, but it could also be
772
a revision that was merged.
774
This is frequently used with 'diff' to return all of the changes
775
that your branch introduces, while excluding the changes that you
776
have not merged from the remote branch.
780
ancestor:/path/to/branch
781
$ bzr diff -r ancestor:../../mainline/branch
785
def _match_on(self, branch, revs):
786
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
787
return self._find_revision_info(branch, self.spec)
789
def _as_revision_id(self, context_branch):
790
return self._find_revision_id(context_branch, self.spec)
793
def _find_revision_info(branch, other_location):
794
revision_id = RevisionSpec_ancestor._find_revision_id(branch,
797
revno = branch.revision_id_to_revno(revision_id)
798
except errors.NoSuchRevision:
800
return RevisionInfo(branch, revno, revision_id)
803
def _find_revision_id(branch, other_location):
804
from bzrlib.branch import Branch
808
revision_a = revision.ensure_null(branch.last_revision())
809
if revision_a == revision.NULL_REVISION:
810
raise errors.NoCommits(branch)
811
if other_location == '':
812
other_location = branch.get_parent()
813
other_branch = Branch.open(other_location)
814
other_branch.lock_read()
816
revision_b = revision.ensure_null(other_branch.last_revision())
817
if revision_b == revision.NULL_REVISION:
818
raise errors.NoCommits(other_branch)
819
graph = branch.repository.get_graph(other_branch.repository)
820
rev_id = graph.find_unique_lca(revision_a, revision_b)
822
other_branch.unlock()
823
if rev_id == revision.NULL_REVISION:
824
raise errors.NoCommonAncestor(revision_a, revision_b)
832
class RevisionSpec_branch(RevisionSpec):
833
"""Selects the last revision of a specified branch."""
835
help_txt = """Selects the last revision of a specified branch.
837
Supply the path to a branch to select its last revision.
841
branch:/path/to/branch
844
dwim_catchable_exceptions = (errors.NotBranchError,)
846
def _match_on(self, branch, revs):
847
from bzrlib.branch import Branch
848
other_branch = Branch.open(self.spec)
849
revision_b = other_branch.last_revision()
850
if revision_b in (None, revision.NULL_REVISION):
851
raise errors.NoCommits(other_branch)
853
branch = other_branch
856
# pull in the remote revisions so we can diff
857
branch.fetch(other_branch, revision_b)
858
except errors.ReadOnlyError:
859
branch = other_branch
861
revno = branch.revision_id_to_revno(revision_b)
862
except errors.NoSuchRevision:
864
return RevisionInfo(branch, revno, revision_b)
866
def _as_revision_id(self, context_branch):
867
from bzrlib.branch import Branch
868
other_branch = Branch.open(self.spec)
869
last_revision = other_branch.last_revision()
870
last_revision = revision.ensure_null(last_revision)
871
context_branch.fetch(other_branch, last_revision)
872
if last_revision == revision.NULL_REVISION:
873
raise errors.NoCommits(other_branch)
876
def _as_tree(self, context_branch):
877
from bzrlib.branch import Branch
878
other_branch = Branch.open(self.spec)
879
last_revision = other_branch.last_revision()
880
last_revision = revision.ensure_null(last_revision)
881
if last_revision == revision.NULL_REVISION:
882
raise errors.NoCommits(other_branch)
883
return other_branch.repository.revision_tree(last_revision)
885
def needs_branch(self):
888
def get_branch(self):
893
class RevisionSpec_submit(RevisionSpec_ancestor):
894
"""Selects a common ancestor with a submit branch."""
896
help_txt = """Selects a common ancestor with the submit branch.
898
Diffing against this shows all the changes that were made in this branch,
899
and is a good predictor of what merge will do. The submit branch is
900
used by the bundle and merge directive commands. If no submit branch
901
is specified, the parent branch is used instead.
903
The common ancestor is the last revision that existed in both
904
branches. Usually this is the branch point, but it could also be
905
a revision that was merged.
909
$ bzr diff -r submit:
914
def _get_submit_location(self, branch):
915
submit_location = branch.get_submit_branch()
916
location_type = 'submit branch'
917
if submit_location is None:
918
submit_location = branch.get_parent()
919
location_type = 'parent branch'
920
if submit_location is None:
921
raise errors.NoSubmitBranch(branch)
922
trace.note(gettext('Using {0} {1}').format(location_type,
924
return submit_location
926
def _match_on(self, branch, revs):
927
trace.mutter('matching ancestor: on: %s, %s', self.spec, branch)
928
return self._find_revision_info(branch,
929
self._get_submit_location(branch))
931
def _as_revision_id(self, context_branch):
932
return self._find_revision_id(context_branch,
933
self._get_submit_location(context_branch))
936
class RevisionSpec_annotate(RevisionIDSpec):
940
help_txt = """Select the revision that last modified the specified line.
942
Select the revision that last modified the specified line. Line is
943
specified as path:number. Path is a relative path to the file. Numbers
944
start at 1, and are relative to the current version, not the last-
945
committed version of the file.
948
def _raise_invalid(self, numstring, context_branch):
949
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
950
'No such line: %s' % numstring)
952
def _as_revision_id(self, context_branch):
953
path, numstring = self.spec.rsplit(':', 1)
955
index = int(numstring) - 1
957
self._raise_invalid(numstring, context_branch)
958
tree, file_path = workingtree.WorkingTree.open_containing(path)
961
file_id = tree.path2id(file_path)
963
raise errors.InvalidRevisionSpec(self.user_spec,
964
context_branch, "File '%s' is not versioned." %
966
revision_ids = [r for (r, l) in tree.annotate_iter(file_id)]
970
revision_id = revision_ids[index]
972
self._raise_invalid(numstring, context_branch)
973
if revision_id == revision.CURRENT_REVISION:
974
raise errors.InvalidRevisionSpec(self.user_spec, context_branch,
975
'Line %s has not been committed.' % numstring)
979
class RevisionSpec_mainline(RevisionIDSpec):
981
help_txt = """Select mainline revision that merged the specified revision.
983
Select the revision that merged the specified revision into mainline.
988
def _as_revision_id(self, context_branch):
989
revspec = RevisionSpec.from_string(self.spec)
990
if revspec.get_branch() is None:
991
spec_branch = context_branch
993
spec_branch = _mod_branch.Branch.open(revspec.get_branch())
994
revision_id = revspec.as_revision_id(spec_branch)
995
graph = context_branch.repository.get_graph()
996
result = graph.find_lefthand_merger(revision_id,
997
context_branch.last_revision())
999
raise errors.InvalidRevisionSpec(self.user_spec, context_branch)
1003
# The order in which we want to DWIM a revision spec without any prefix.
1004
# revno is always tried first and isn't listed here, this is used by
1005
# RevisionSpec_dwim._match_on
1006
dwim_revspecs = symbol_versioning.deprecated_list(
1007
symbol_versioning.deprecated_in((2, 4, 0)), "dwim_revspecs", [])
1009
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_tag)
1010
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_revid)
1011
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_date)
1012
RevisionSpec_dwim.append_possible_revspec(RevisionSpec_branch)
1014
revspec_registry = registry.Registry()
1015
def _register_revspec(revspec):
1016
revspec_registry.register(revspec.prefix, revspec)
1018
_register_revspec(RevisionSpec_revno)
1019
_register_revspec(RevisionSpec_revid)
1020
_register_revspec(RevisionSpec_last)
1021
_register_revspec(RevisionSpec_before)
1022
_register_revspec(RevisionSpec_tag)
1023
_register_revspec(RevisionSpec_date)
1024
_register_revspec(RevisionSpec_ancestor)
1025
_register_revspec(RevisionSpec_branch)
1026
_register_revspec(RevisionSpec_submit)
1027
_register_revspec(RevisionSpec_annotate)
1028
_register_revspec(RevisionSpec_mainline)