/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/bundle/serializer/__init__.py

  • Committer: Robert Collins
  • Date: 2008-08-20 02:07:36 UTC
  • mfrom: (3640 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3682.
  • Revision ID: robertc@robertcollins.net-20080820020736-g2xe4921zzxtymle
Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# (C) 2005 Canonical Development Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
24
import bzrlib.errors as errors
25
25
from bzrlib.diff import internal_diff
26
26
from bzrlib.revision import NULL_REVISION
 
27
# For backwards-compatibility
 
28
from bzrlib.timestamp import unpack_highres_date, format_highres_date
 
29
 
27
30
 
28
31
# New bundles should try to use this header format
29
32
BUNDLE_HEADER = '# Bazaar revision bundle v'
30
 
BUNDLE_HEADER_RE = re.compile(r'^# Bazaar revision bundle v(?P<version>\d+[\w.]*)\n$')
31
 
CHANGESET_OLD_HEADER_RE = re.compile(r'^# Bazaar-NG changeset v(?P<version>\d+[\w.]*)\n$')
32
 
 
33
 
 
34
 
_serializers = {} 
35
 
 
 
33
BUNDLE_HEADER_RE = re.compile(
 
34
    r'^# Bazaar revision bundle v(?P<version>\d+[\w.]*)(?P<lineending>\r?)\n$')
 
35
CHANGESET_OLD_HEADER_RE = re.compile(
 
36
    r'^# Bazaar-NG changeset v(?P<version>\d+[\w.]*)(?P<lineending>\r?)\n$')
 
37
 
 
38
 
 
39
_serializers = {}
 
40
 
 
41
v4_string = '4'
 
42
 
 
43
def _get_bundle_header(version):
 
44
    return '%s%s\n' % (BUNDLE_HEADER, version)
36
45
 
37
46
def _get_filename(f):
38
 
    if hasattr(f, 'name'):
39
 
        return f.name
40
 
    return '<unknown>'
41
 
 
42
 
 
43
 
def read(f):
 
47
    return getattr(f, 'name', '<unknown>')
 
48
 
 
49
 
 
50
def read_bundle(f):
44
51
    """Read in a bundle from a filelike object.
45
52
 
46
53
    :param f: A file-like object
50
57
    for line in f:
51
58
        m = BUNDLE_HEADER_RE.match(line)
52
59
        if m:
 
60
            if m.group('lineending') != '':
 
61
                raise errors.UnsupportedEOLMarker()
53
62
            version = m.group('version')
54
63
            break
 
64
        elif line.startswith(BUNDLE_HEADER):
 
65
            raise errors.MalformedHeader(
 
66
                'Extra characters after version number')
55
67
        m = CHANGESET_OLD_HEADER_RE.match(line)
56
68
        if m:
57
69
            version = m.group('version')
58
 
            raise errors.BundleNotSupported(version, 'old format bundles not supported')
 
70
            raise errors.BundleNotSupported(version, 
 
71
                'old format bundles not supported')
59
72
 
60
73
    if version is None:
61
 
        raise errors.NoBundleFound(_get_filename(f))
 
74
        raise errors.NotABundle('Did not find an opening header')
62
75
 
63
76
    # Now we have a version, to figure out how to read the bundle 
64
 
    if not _serializers.has_key(version):
65
 
        raise errors.BundleNotSupported(version, 'version not listed in known versions')
 
77
    if version not in _serializers:
 
78
        raise errors.BundleNotSupported(version, 
 
79
            'version not listed in known versions')
66
80
 
67
81
    serializer = _serializers[version](version)
68
82
 
69
83
    return serializer.read(f)
70
84
 
71
85
 
 
86
def get_serializer(version):
 
87
    try:
 
88
        return _serializers[version](version)
 
89
    except KeyError:
 
90
        raise errors.BundleNotSupported(version, 'unknown bundle format')
 
91
 
 
92
 
72
93
def write(source, revision_ids, f, version=None, forced_bases={}):
73
94
    """Serialize a list of bundles to a filelike object.
74
95
 
78
99
    :param version: [optional] target serialization version
79
100
    """
80
101
 
81
 
    if not _serializers.has_key(version):
82
 
        raise errors.BundleNotSupported(version, 'unknown bundle format')
83
 
 
84
 
    serializer = _serializers[version](version)
85
 
    return serializer.write(source, revision_ids, forced_bases, f) 
86
 
 
87
 
 
88
 
def write_bundle(repository, revision_id, base_revision_id, out):
89
 
    """"""
90
 
    if base_revision_id is NULL_REVISION:
91
 
        base_revision_id = None
92
 
    base_ancestry = set(repository.get_ancestry(base_revision_id))
93
 
    revision_ids = [r for r in repository.get_ancestry(revision_id) if r
94
 
                    not in base_ancestry]
95
 
    revision_ids = list(reversed(revision_ids))
96
 
    write(repository, revision_ids, out, 
97
 
          forced_bases = {revision_id:base_revision_id})
98
 
    return revision_ids
99
 
 
100
 
 
101
 
def format_highres_date(t, offset=0):
102
 
    """Format a date, such that it includes higher precision in the
103
 
    seconds field.
104
 
 
105
 
    :param t:   The local time in fractional seconds since the epoch
106
 
    :type t: float
107
 
    :param offset:  The timezone offset in integer seconds
108
 
    :type offset: int
109
 
 
110
 
    Example: format_highres_date(time.time(), -time.timezone)
111
 
    this will return a date stamp for right now,
112
 
    formatted for the local timezone.
113
 
 
114
 
    >>> from bzrlib.osutils import format_date
115
 
    >>> format_date(1120153132.350850105, 0)
116
 
    'Thu 2005-06-30 17:38:52 +0000'
117
 
    >>> format_highres_date(1120153132.350850105, 0)
118
 
    'Thu 2005-06-30 17:38:52.350850105 +0000'
119
 
    >>> format_date(1120153132.350850105, -5*3600)
120
 
    'Thu 2005-06-30 12:38:52 -0500'
121
 
    >>> format_highres_date(1120153132.350850105, -5*3600)
122
 
    'Thu 2005-06-30 12:38:52.350850105 -0500'
123
 
    >>> format_highres_date(1120153132.350850105, 7200)
124
 
    'Thu 2005-06-30 19:38:52.350850105 +0200'
125
 
    """
126
 
    import time
127
 
    assert isinstance(t, float)
128
 
    
129
 
    # This has to be formatted for "original" date, so that the
130
 
    # revision XML entry will be reproduced faithfully.
131
 
    if offset == None:
132
 
        offset = 0
133
 
    tt = time.gmtime(t + offset)
134
 
 
135
 
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
136
 
            + ('%.9f' % (t - int(t)))[1:] # Get the high-res seconds, but ignore the 0
137
 
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
138
 
 
139
 
 
140
 
def unpack_highres_date(date):
141
 
    """This takes the high-resolution date stamp, and
142
 
    converts it back into the tuple (timestamp, timezone)
143
 
    Where timestamp is in real UTC since epoch seconds, and timezone is an integer
144
 
    number of seconds offset.
145
 
 
146
 
    :param date: A date formated by format_highres_date
147
 
    :type date: string
148
 
 
149
 
    >>> import time, random
150
 
    >>> unpack_highres_date('Thu 2005-06-30 12:38:52.350850105 -0500')
151
 
    (1120153132.3508501, -18000)
152
 
    >>> unpack_highres_date('Thu 2005-06-30 17:38:52.350850105 +0000')
153
 
    (1120153132.3508501, 0)
154
 
    >>> unpack_highres_date('Thu 2005-06-30 19:38:52.350850105 +0200')
155
 
    (1120153132.3508501, 7200)
156
 
    >>> from bzrlib.osutils import local_time_offset
157
 
    >>> t = time.time()
158
 
    >>> o = local_time_offset()
159
 
    >>> t2, o2 = unpack_highres_date(format_highres_date(t, o))
160
 
    >>> t == t2
161
 
    True
162
 
    >>> o == o2
163
 
    True
164
 
    >>> t -= 24*3600*365*2 # Start 2 years ago
165
 
    >>> o = -12*3600
166
 
    >>> for count in xrange(500):
167
 
    ...   t += random.random()*24*3600*30
168
 
    ...   o = ((o/3600 + 13) % 25 - 12)*3600 # Add 1 wrap around from [-12, 12]
169
 
    ...   date = format_highres_date(t, o)
170
 
    ...   t2, o2 = unpack_highres_date(date)
171
 
    ...   if t != t2 or o != o2:
172
 
    ...      print 'Failed on date %r, %s,%s diff:%s' % (date, t, o, t2-t)
173
 
    ...      break
174
 
 
175
 
    """
176
 
    import time, calendar
177
 
    # Up until the first period is a datestamp that is generated
178
 
    # as normal from time.strftime, so use time.strptime to
179
 
    # parse it
180
 
    dot_loc = date.find('.')
181
 
    if dot_loc == -1:
182
 
        raise ValueError('Date string does not contain high-precision seconds: %r' % date)
183
 
    base_time = time.strptime(date[:dot_loc], "%a %Y-%m-%d %H:%M:%S")
184
 
    fract_seconds, offset = date[dot_loc:].split()
185
 
    fract_seconds = float(fract_seconds)
186
 
    offset = int(offset)
187
 
    offset = int(offset / 100) * 3600 + offset % 100
188
 
    
189
 
    # time.mktime returns localtime, but calendar.timegm returns UTC time
190
 
    timestamp = calendar.timegm(base_time)
191
 
    timestamp -= offset
192
 
    # Add back in the fractional seconds
193
 
    timestamp += fract_seconds
194
 
    return (timestamp, offset)
 
102
    source.lock_read()
 
103
    try:
 
104
        return get_serializer(version).write(source, revision_ids,
 
105
                                             forced_bases, f)
 
106
    finally:
 
107
        source.unlock()
 
108
 
 
109
 
 
110
def write_bundle(repository, revision_id, base_revision_id, out, format=None):
 
111
    """Write a bundle of revisions.
 
112
 
 
113
    :param repository: Repository containing revisions to serialize.
 
114
    :param revision_id: Head revision_id of the bundle.
 
115
    :param base_revision_id: Revision assumed to be present in repositories
 
116
         applying the bundle.
 
117
    :param out: Output file.
 
118
    """
 
119
    repository.lock_read()
 
120
    try:
 
121
        return get_serializer(format).write_bundle(repository, revision_id,
 
122
                                                   base_revision_id, out)
 
123
    finally:
 
124
        repository.unlock()
195
125
 
196
126
 
197
127
class BundleSerializer(object):
210
140
        """
211
141
        raise NotImplementedError
212
142
 
 
143
    def write_bundle(self, repository, target, base, fileobj):
 
144
        """Write the bundle to the supplied file.
 
145
 
 
146
        :param repository: The repository to retrieve revision data from
 
147
        :param target: The revision to provide data for
 
148
        :param base: The most recent of ancestor of the revision that does not
 
149
            need to be included in the bundle
 
150
        :param fileobj: The file to output to
 
151
        """
 
152
        raise NotImplementedError
 
153
 
213
154
    def write(self, source, revision_ids, forced_bases, f):
214
155
        """Write the bundle to the supplied file.
215
156
 
 
157
        DEPRECATED: see write_bundle
216
158
        :param source: A source for revision information
217
159
        :param revision_ids: The list of revision ids to serialize
218
160
        :param forced_bases: A dict of revision -> base that overrides default
220
162
        """
221
163
        raise NotImplementedError
222
164
 
 
165
    def _write_bundle(self, repository, revision_id, base_revision_id, out):
 
166
        """Helper function for translating write_bundle to write"""
 
167
        forced_bases = {revision_id:base_revision_id}
 
168
        if base_revision_id is NULL_REVISION:
 
169
            base_revision_id = None
 
170
        revision_ids = set(repository.get_ancestry(revision_id,
 
171
                           topo_sorted=False))
 
172
        revision_ids.difference_update(repository.get_ancestry(
 
173
            base_revision_id, topo_sorted=False))
 
174
        revision_ids = list(repository.get_graph().iter_topo_order(
 
175
            revision_ids))
 
176
        revision_ids.reverse()
 
177
        self.write(repository, revision_ids, forced_bases, out)
 
178
        return revision_ids
 
179
 
223
180
 
224
181
def register(version, klass, overwrite=False):
225
182
    """Register a BundleSerializer version.
232
189
        _serializers[version] = klass
233
190
        return
234
191
 
235
 
    if not _serializers.has_key(version):
 
192
    if version not in _serializers:
236
193
        _serializers[version] = klass
237
194
 
238
195
 
259
216
    base64.encode(temp, to_file)
260
217
    to_file.write('\n')
261
218
 
262
 
register_lazy('0.7', 'bzrlib.bundle.serializer.v07', 'BundleSerializerV07')
263
 
register_lazy(None, 'bzrlib.bundle.serializer.v07', 'BundleSerializerV07')
 
219
register_lazy('0.8', 'bzrlib.bundle.serializer.v08', 'BundleSerializerV08')
 
220
register_lazy('0.9', 'bzrlib.bundle.serializer.v09', 'BundleSerializerV09')
 
221
register_lazy(v4_string, 'bzrlib.bundle.serializer.v4',
 
222
              'BundleSerializerV4')
 
223
register_lazy(None, 'bzrlib.bundle.serializer.v4', 'BundleSerializerV4')
264
224