[Pkg-bazaar-commits] ./bzr/unstable r897: - merge john's revision-naming code
Martin Pool
mbp at sourcefrog.net
Fri Apr 10 08:13:38 UTC 2009
------------------------------------------------------------
revno: 897
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Mon 2005-07-11 16:13:18 +1000
message:
- merge john's revision-naming code
added:
bzrlib/selftest/testrevisionnamespaces.py
modified:
bzrlib/branch.py
bzrlib/commands.py
bzrlib/selftest/__init__.py
-------------- next part --------------
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py 2005-07-04 12:28:33 +0000
+++ b/bzrlib/branch.py 2005-07-11 06:13:18 +0000
@@ -150,6 +150,11 @@
_lock_count = None
_lock = None
+ # Map some sort of prefix into a namespace
+ # stuff like "revno:10", "revid:", etc.
+ # This should match a prefix with a function which accepts
+ REVISION_NAMESPACES = {}
+
def __init__(self, base, init=False, find_root=True):
"""Create new branch object at a particular location.
@@ -461,7 +466,7 @@
# use inventory as it was in that revision
file_id = tree.inventory.path2id(file)
if not file_id:
- raise BzrError("%r is not present in revision %d" % (file, revno))
+ raise BzrError("%r is not present in revision %s" % (file, revno))
tree.print_file(file_id)
finally:
self.unlock()
@@ -836,17 +841,162 @@
commit(self, *args, **kw)
- def lookup_revision(self, revno):
- """Return revision hash for revision number."""
- if revno == 0:
- return None
-
- try:
- # list is 0-based; revisions are 1-based
- return self.revision_history()[revno-1]
- except IndexError:
- raise BzrError("no such revision %s" % revno)
-
+ def lookup_revision(self, revision):
+ """Return the revision identifier for a given revision information."""
+ revno, info = self.get_revision_info(revision)
+ return info
+
+ def get_revision_info(self, revision):
+ """Return (revno, revision id) for revision identifier.
+
+ revision can be an integer, in which case it is assumed to be revno (though
+ this will translate negative values into positive ones)
+ revision can also be a string, in which case it is parsed for something like
+ 'date:' or 'revid:' etc.
+ """
+ if revision is None:
+ return 0, None
+ revno = None
+ try:# Convert to int if possible
+ revision = int(revision)
+ except ValueError:
+ pass
+ revs = self.revision_history()
+ if isinstance(revision, int):
+ if revision == 0:
+ return 0, None
+ # Mabye we should do this first, but we don't need it if revision == 0
+ if revision < 0:
+ revno = len(revs) + revision + 1
+ else:
+ revno = revision
+ elif isinstance(revision, basestring):
+ for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
+ if revision.startswith(prefix):
+ revno = func(self, revs, revision)
+ break
+ else:
+ raise BzrError('No namespace registered for string: %r' % revision)
+
+ if revno is None or revno <= 0 or revno > len(revs):
+ raise BzrError("no such revision %s" % revision)
+ return revno, revs[revno-1]
+
+ def _namespace_revno(self, revs, revision):
+ """Lookup a revision by revision number"""
+ assert revision.startswith('revno:')
+ try:
+ return int(revision[6:])
+ except ValueError:
+ return None
+ REVISION_NAMESPACES['revno:'] = _namespace_revno
+
+ def _namespace_revid(self, revs, revision):
+ assert revision.startswith('revid:')
+ try:
+ return revs.index(revision[6:]) + 1
+ except ValueError:
+ return None
+ REVISION_NAMESPACES['revid:'] = _namespace_revid
+
+ def _namespace_last(self, revs, revision):
+ assert revision.startswith('last:')
+ try:
+ offset = int(revision[5:])
+ except ValueError:
+ return None
+ else:
+ if offset <= 0:
+ raise BzrError('You must supply a positive value for --revision last:XXX')
+ return len(revs) - offset + 1
+ REVISION_NAMESPACES['last:'] = _namespace_last
+
+ def _namespace_tag(self, revs, revision):
+ assert revision.startswith('tag:')
+ raise BzrError('tag: namespace registered, but not implemented.')
+ REVISION_NAMESPACES['tag:'] = _namespace_tag
+
+ def _namespace_date(self, revs, revision):
+ assert revision.startswith('date:')
+ import datetime
+ # Spec for date revisions:
+ # date:value
+ # value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
+ # it can also start with a '+/-/='. '+' says match the first
+ # entry after the given date. '-' is match the first entry before the date
+ # '=' is match the first entry after, but still on the given date.
+ #
+ # +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
+ # -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
+ # =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
+ # May 13th, 2005 at 0:00
+ #
+ # So the proper way of saying 'give me all entries for today' is:
+ # -r {date:+today}:{date:-tomorrow}
+ # The default is '=' when not supplied
+ val = revision[5:]
+ match_style = '='
+ if val[:1] in ('+', '-', '='):
+ match_style = val[:1]
+ val = val[1:]
+
+ today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
+ if val.lower() == 'yesterday':
+ dt = today - datetime.timedelta(days=1)
+ elif val.lower() == 'today':
+ dt = today
+ elif val.lower() == 'tomorrow':
+ dt = today + datetime.timedelta(days=1)
+ else:
+ # This should be done outside the function to avoid recompiling it.
+ _date_re = re.compile(
+ r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
+ r'(,|T)?\s*'
+ r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
+ )
+ m = _date_re.match(val)
+ if not m or (not m.group('date') and not m.group('time')):
+ raise BzrError('Invalid revision date %r' % revision)
+
+ if m.group('date'):
+ year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
+ else:
+ year, month, day = today.year, today.month, today.day
+ if m.group('time'):
+ hour = int(m.group('hour'))
+ minute = int(m.group('minute'))
+ if m.group('second'):
+ second = int(m.group('second'))
+ else:
+ second = 0
+ else:
+ hour, minute, second = 0,0,0
+
+ dt = datetime.datetime(year=year, month=month, day=day,
+ hour=hour, minute=minute, second=second)
+ first = dt
+ last = None
+ reversed = False
+ if match_style == '-':
+ reversed = True
+ elif match_style == '=':
+ last = dt + datetime.timedelta(days=1)
+
+ if reversed:
+ for i in range(len(revs)-1, -1, -1):
+ r = self.get_revision(revs[i])
+ # TODO: Handle timezone.
+ dt = datetime.datetime.fromtimestamp(r.timestamp)
+ if first >= dt and (last is None or dt >= last):
+ return i+1
+ else:
+ for i in range(len(revs)):
+ r = self.get_revision(revs[i])
+ # TODO: Handle timezone.
+ dt = datetime.datetime.fromtimestamp(r.timestamp)
+ if first <= dt and (last is None or dt <= last):
+ return i+1
+ REVISION_NAMESPACES['date:'] = _namespace_date
def revision_tree(self, revision_id):
"""Return Tree for a revision on this branch.
=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py 2005-07-11 03:42:53 +0000
+++ b/bzrlib/commands.py 2005-07-11 06:13:18 +0000
@@ -19,7 +19,7 @@
import sys, os
import bzrlib
-from bzrlib.trace import mutter, note, log_error
+from bzrlib.trace import mutter, note, log_error, warning
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
from bzrlib.branch import find_branch
from bzrlib import BZRDIR
@@ -54,35 +54,68 @@
def _parse_revision_str(revstr):
"""This handles a revision string -> revno.
- There are several possibilities:
-
- '234' -> 234
- '234:345' -> [234, 345]
- ':234' -> [None, 234]
- '234:' -> [234, None]
-
- In the future we will also support:
- 'uuid:blah-blah-blah' -> ?
- 'hash:blahblahblah' -> ?
- potentially:
- 'tag:mytag' -> ?
+ It supports integers directly, but everything else it
+ defers for passing to Branch.get_revision_info()
+
+ >>> _parse_revision_str('234')
+ [234]
+ >>> _parse_revision_str('234..567')
+ [234, 567]
+ >>> _parse_revision_str('..')
+ [None, None]
+ >>> _parse_revision_str('..234')
+ [None, 234]
+ >>> _parse_revision_str('234..')
+ [234, None]
+ >>> _parse_revision_str('234..456..789') # Maybe this should be an error
+ [234, 456, 789]
+ >>> _parse_revision_str('234....789') # Error?
+ [234, None, 789]
+ >>> _parse_revision_str('revid:test at other.com-234234')
+ ['revid:test at other.com-234234']
+ >>> _parse_revision_str('revid:test at other.com-234234..revid:test at other.com-234235')
+ ['revid:test at other.com-234234', 'revid:test at other.com-234235']
+ >>> _parse_revision_str('revid:test at other.com-234234..23')
+ ['revid:test at other.com-234234', 23]
+ >>> _parse_revision_str('date:2005-04-12')
+ ['date:2005-04-12']
+ >>> _parse_revision_str('date:2005-04-12 12:24:33')
+ ['date:2005-04-12 12:24:33']
+ >>> _parse_revision_str('date:2005-04-12T12:24:33')
+ ['date:2005-04-12T12:24:33']
+ >>> _parse_revision_str('date:2005-04-12,12:24:33')
+ ['date:2005-04-12,12:24:33']
+ >>> _parse_revision_str('-5..23')
+ [-5, 23]
+ >>> _parse_revision_str('-5')
+ [-5]
+ >>> _parse_revision_str('123a')
+ ['123a']
+ >>> _parse_revision_str('abc')
+ ['abc']
"""
- if revstr.find(':') != -1:
- revs = revstr.split(':')
- if len(revs) > 2:
- raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
-
- if not revs[0]:
- revs[0] = None
- else:
- revs[0] = int(revs[0])
-
- if not revs[1]:
- revs[1] = None
- else:
- revs[1] = int(revs[1])
- else:
- revs = int(revstr)
+ import re
+ old_format_re = re.compile('\d*:\d*')
+ m = old_format_re.match(revstr)
+ if m:
+ warning('Colon separator for revision numbers is deprecated.'
+ ' Use .. instead')
+ revs = []
+ for rev in revstr.split(':'):
+ if rev:
+ revs.append(int(rev))
+ else:
+ revs.append(None)
+ return revs
+ revs = []
+ for x in revstr.split('..'):
+ if not x:
+ revs.append(None)
+ else:
+ try:
+ revs.append(int(x))
+ except ValueError:
+ revs.append(x)
return revs
@@ -334,6 +367,28 @@
def run(self):
print find_branch('.').revno()
+class cmd_revision_info(Command):
+ """Show revision number and revision id for a given revision identifier.
+ """
+ hidden = True
+ takes_args = ['revision_info*']
+ takes_options = ['revision']
+ def run(self, revision=None, revision_info_list=None):
+ from bzrlib.branch import find_branch
+
+ revs = []
+ if revision is not None:
+ revs.extend(revision)
+ if revision_info_list is not None:
+ revs.extend(revision_info_list)
+ if len(revs) == 0:
+ raise BzrCommandError('You must supply a revision identifier')
+
+ b = find_branch('.')
+
+ for rev in revs:
+ print '%4d %s' % b.get_revision_info(rev)
+
class cmd_add(Command):
"""Add specified files or directories.
@@ -401,7 +456,10 @@
if revision == None:
inv = b.read_working_inventory()
else:
- inv = b.get_revision_inventory(b.lookup_revision(revision))
+ if len(revision) > 1:
+ raise BzrCommandError('bzr inventory --revision takes'
+ ' exactly one revision identifier')
+ inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
for path, entry in inv.entries():
if show_ids:
@@ -529,6 +587,10 @@
from meta_store import CachedStore
import tempfile
cache_root = tempfile.mkdtemp()
+
+ if revision is not None:
+ if len(revision) > 1:
+ raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
try:
try:
br_from = find_cached_branch(from_location, cache_root)
@@ -555,12 +617,13 @@
raise
br_to = Branch(to_location, init=True)
+ revno = br_to.lookup_revision(revision[0])
try:
- br_to.update_revisions(br_from, stop_revision=revision)
+ br_to.update_revisions(br_from, stop_revision=revno)
except NoSuchRevision:
rmtree(to_location)
msg = "The branch %s has no revision %d." % (from_location,
- revision)
+ revno)
raise BzrCommandError(msg)
merge((to_location, -1), (to_location, 0), this_dir=to_location,
check_clean=False, ignore_zero=True)
@@ -735,8 +798,15 @@
file_list = None
else:
b = find_branch('.')
+
+ # TODO: Make show_diff support taking 2 arguments
+ base_rev = None
+ if revision is not None:
+ if len(revision) != 1:
+ raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
+ base_rev = revision[0]
- show_diff(b, revision, specific_files=file_list,
+ show_diff(b, base_rev, specific_files=file_list,
external_diff_options=diff_options)
@@ -847,15 +917,21 @@
b = find_branch('.')
file_id = None
- if revision == None:
- revision = [None, None]
- elif isinstance(revision, int):
- revision = [revision, revision]
+ if revision is None:
+ rev1 = None
+ rev2 = None
+ elif len(revision) == 1:
+ rev1 = rev2 = b.get_revision_info(revision[0])[0]
+ elif len(revision) == 2:
+ rev1 = b.get_revision_info(revision[0])[0]
+ rev2 = b.get_revision_info(revision[1])[0]
else:
- # pair of revisions?
- pass
-
- assert len(revision) == 2
+ raise BzrCommandError('bzr log --revision takes one or two values.')
+
+ if rev1 == 0:
+ rev1 = None
+ if rev2 == 0:
+ rev2 = None
mutter('encoding log as %r' % bzrlib.user_encoding)
@@ -877,8 +953,8 @@
file_id,
verbose=verbose,
direction=direction,
- start_revision=revision[0],
- end_revision=revision[1])
+ start_revision=rev1,
+ end_revision=rev2)
@@ -1040,11 +1116,13 @@
def run(self, dest, revision=None, format=None, root=None):
import os.path
b = find_branch('.')
- if revision == None:
- rh = b.revision_history()[-1]
+ if revision is None:
+ rev_id = b.last_patch()
else:
- rh = b.lookup_revision(int(revision))
- t = b.revision_tree(rh)
+ if len(revision) != 1:
+ raise BzrError('bzr export --revision takes exactly 1 argument')
+ revno, rev_id = b.get_revision_info(revision[0])
+ t = b.revision_tree(rev_id)
root, ext = os.path.splitext(dest)
if not format:
if ext in (".tar",):
@@ -1067,8 +1145,10 @@
def run(self, filename, revision=None):
if revision == None:
raise BzrCommandError("bzr cat requires a revision number")
+ elif len(revision) != 1:
+ raise BzrCommandError("bzr cat --revision takes exactly one number")
b = find_branch('.')
- b.print_file(b.relpath(filename), int(revision))
+ b.print_file(b.relpath(filename), revision[0])
class cmd_local_time_offset(Command):
@@ -1222,6 +1302,8 @@
['..', -1]
>>> parse_spec("../f/@35")
['../f', 35]
+ >>> parse_spec('./@revid:john at arbash-meinel.com-20050711044610-3ca0327c6a222f67')
+ ['.', 'revid:john at arbash-meinel.com-20050711044610-3ca0327c6a222f67']
"""
if spec is None:
return [None, None]
@@ -1231,8 +1313,12 @@
if parsed[1] == "":
parsed[1] = -1
else:
- parsed[1] = int(parsed[1])
- assert parsed[1] >=0
+ try:
+ parsed[1] = int(parsed[1])
+ except ValueError:
+ pass # We can allow stuff like ./@revid:blahblahblah
+ else:
+ assert parsed[1] >=0
else:
parsed = [spec, None]
return parsed
@@ -1296,9 +1382,13 @@
"""
takes_options = ['revision']
- def run(self, revision=-1):
+ def run(self, revision=None):
from bzrlib.merge import merge
- merge(('.', revision), parse_spec('.'),
+ if revision is None:
+ revision = -1
+ elif len(revision) != 1:
+ raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
+ merge(('.', revision[0]), parse_spec('.'),
check_clean=False,
ignore_zero=True)
@@ -1387,15 +1477,13 @@
>>> parse_args('commit --message=biter'.split())
(['commit'], {'message': u'biter'})
>>> parse_args('log -r 500'.split())
- (['log'], {'revision': 500})
- >>> parse_args('log -r500:600'.split())
+ (['log'], {'revision': [500]})
+ >>> parse_args('log -r500..600'.split())
(['log'], {'revision': [500, 600]})
- >>> parse_args('log -vr500:600'.split())
+ >>> parse_args('log -vr500..600'.split())
(['log'], {'verbose': True, 'revision': [500, 600]})
- >>> parse_args('log -rv500:600'.split()) #the r takes an argument
- Traceback (most recent call last):
- ...
- ValueError: invalid literal for int(): v500
+ >>> parse_args('log -rv500..600'.split()) #the r takes an argument
+ (['log'], {'revision': ['v500', 600]})
"""
args = []
opts = {}
=== modified file 'bzrlib/selftest/__init__.py'
--- a/bzrlib/selftest/__init__.py 2005-07-11 03:32:25 +0000
+++ b/bzrlib/selftest/__init__.py 2005-07-11 06:13:18 +0000
@@ -31,6 +31,7 @@
import bzrlib.selftest.versioning
import bzrlib.selftest.testmerge3
import bzrlib.selftest.testhashcache
+ import bzrlib.selftest.testrevisionnamespaces
import bzrlib.merge_core
from doctest import DocTestSuite
import os
@@ -48,7 +49,10 @@
bzrlib.selftest.versioning,
bzrlib.selftest.testmerge3,
bzrlib.selftest.testhashcache,
- bzrlib.selftest.blackbox):
+ bzrlib.selftest.blackbox,
+ bzrlib.selftest.testhashcache,
+ bzrlib.selftest.testrevisionnamespaces,
+ ):
if m not in MODULES_TO_TEST:
MODULES_TO_TEST.append(m)
=== added file 'bzrlib/selftest/testrevisionnamespaces.py'
--- a/bzrlib/selftest/testrevisionnamespaces.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/selftest/testrevisionnamespaces.py 2005-07-11 06:13:18 +0000
@@ -0,0 +1,46 @@
+# Copyright (C) 2004, 2005 by Canonical Ltd
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+from bzrlib.selftest import InTempDir, TestBase
+
+class TestRevisionNamespaces(InTempDir):
+ """Functional tests for hashcache"""
+ def runTest(self):
+ from bzrlib.errors import BzrError
+ from bzrlib.branch import Branch
+ import os
+ import time
+
+ b = Branch('.', init=True)
+
+ b.commit('Commit one', rev_id='a at r-0-1')
+ b.commit('Commit two', rev_id='a at r-0-2')
+ b.commit('Commit three', rev_id='a at r-0-3')
+
+ self.assertEquals(b.get_revision_info(1), (1, 'a at r-0-1'))
+ self.assertEquals(b.get_revision_info('revno:1'), (1, 'a at r-0-1'))
+ self.assertEquals(b.get_revision_info('revid:a at r-0-1'), (1, 'a at r-0-1'))
+ self.assertRaises(BzrError, b.get_revision_info, 'revid:a at r-0-0')
+
+ self.assertEquals(b.get_revision_info('date:-tomorrow'), (3, 'a at r-0-3'))
+ self.assertEquals(b.get_revision_info('date:+today'), (1, 'a at r-0-1'))
+
+ self.assertEquals(b.get_revision_info('last:1'), (3, 'a at r-0-3'))
+
+TEST_CLASSES = [
+ TestRevisionNamespaces
+ ]
More information about the Pkg-bazaar-commits
mailing list