[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