~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/testament.py

  • Committer: Andrew Bennetts
  • Date: 2008-05-21 11:58:09 UTC
  • mto: (3452.2.9 inter-remote-pack)
  • mto: This revision was merged to the branch mainline in revision 3511.
  • Revision ID: andrew.bennetts@canonical.com-20080521115809-6cw3t8gn4qm0bpg9
Remove a bit more debugging cruft.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
 
1
# Copyright (C) 2005 Canonical Ltd
2
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
70
70
# revisions can be serialized.
71
71
 
72
72
from copy import copy
73
 
from cStringIO import StringIO
74
 
import string
75
73
from sha import sha
76
74
 
77
75
from bzrlib.osutils import contains_whitespace, contains_linebreaks
78
76
 
 
77
 
79
78
class Testament(object):
80
79
    """Reduced summary of a revision.
81
80
 
87
86
      - compared to a revision
88
87
    """
89
88
 
 
89
    long_header = 'bazaar-ng testament version 1\n'
 
90
    short_header = 'bazaar-ng testament short form 1\n'
 
91
 
90
92
    @classmethod
91
93
    def from_revision(cls, repository, revision_id):
92
94
        """Produce a new testament from a historical revision"""
96
98
 
97
99
    def __init__(self, rev, inventory):
98
100
        """Create a new testament for rev using inventory."""
99
 
        self.revision_id = str(rev.revision_id)
 
101
        self.revision_id = rev.revision_id
100
102
        self.committer = rev.committer
101
103
        self.timezone = rev.timezone or 0
102
104
        self.timestamp = rev.timestamp
104
106
        self.parent_ids = rev.parent_ids[:]
105
107
        self.inventory = inventory
106
108
        self.revprops = copy(rev.properties)
107
 
        assert not contains_whitespace(self.revision_id)
108
 
        assert not contains_linebreaks(self.committer)
 
109
        if contains_whitespace(self.revision_id):
 
110
            raise ValueError(self.revision_id)
 
111
        if contains_linebreaks(self.committer):
 
112
            raise ValueError(self.committer)
109
113
 
110
114
    def as_text_lines(self):
111
115
        """Yield text form as a sequence of lines.
115
119
        """
116
120
        r = []
117
121
        a = r.append
118
 
        a('bazaar-ng testament version 1\n')
 
122
        a(self.long_header)
119
123
        a('revision-id: %s\n' % self.revision_id)
120
124
        a('committer: %s\n' % self.committer)
121
125
        a('timestamp: %d\n' % self.timestamp)
123
127
        # inventory length contains the root, which is not shown here
124
128
        a('parents:\n')
125
129
        for parent_id in sorted(self.parent_ids):
126
 
            assert not contains_whitespace(parent_id)
 
130
            if contains_whitespace(parent_id):
 
131
                raise ValueError(parent_id)
127
132
            a('  %s\n' % parent_id)
128
133
        a('message:\n')
129
134
        for l in self.message.splitlines():
130
135
            a('  %s\n' % l)
131
136
        a('inventory:\n')
132
 
        for path, ie in self.inventory.iter_entries():
 
137
        for path, ie in self._get_entries():
133
138
            a(self._entry_to_line(path, ie))
134
139
        r.extend(self._revprops_to_lines())
135
 
        if __debug__:
136
 
            for l in r:
137
 
                assert isinstance(l, basestring), \
138
 
                    '%r of type %s is not a plain string' % (l, type(l))
139
140
        return [line.encode('utf-8') for line in r]
140
141
 
 
142
    def _get_entries(self):
 
143
        entries = self.inventory.iter_entries()
 
144
        entries.next()
 
145
        return entries
 
146
 
141
147
    def _escape_path(self, path):
142
 
        assert not contains_linebreaks(path)
143
 
        return unicode(path.replace('\\', '/').replace(' ', '\ ')).encode('utf-8')
 
148
        if contains_linebreaks(path):
 
149
            raise ValueError(path)
 
150
        return unicode(path.replace('\\', '/').replace(' ', '\ '))
144
151
 
145
152
    def _entry_to_line(self, path, ie):
146
153
        """Turn an inventory entry into a testament line"""
147
 
        l = '  ' + str(ie.kind)
148
 
        l += ' ' + self._escape_path(path)
149
 
        assert not contains_whitespace(ie.file_id)
150
 
        l += ' ' + unicode(ie.file_id).encode('utf-8')
 
154
        if contains_whitespace(ie.file_id):
 
155
            raise ValueError(ie.file_id)
 
156
        content = ''
 
157
        content_spacer=''
151
158
        if ie.kind == 'file':
152
159
            # TODO: avoid switching on kind
153
 
            assert ie.text_sha1
154
 
            l += ' ' + ie.text_sha1
 
160
            if not ie.text_sha1:
 
161
                raise AssertionError()
 
162
            content = ie.text_sha1
 
163
            content_spacer = ' '
155
164
        elif ie.kind == 'symlink':
156
 
            assert ie.symlink_target
157
 
            l += ' ' + self._escape_path(ie.symlink_target)
158
 
        l += '\n'
159
 
        return l.decode('utf-8')
 
165
            if not ie.symlink_target:
 
166
                raise AssertionError()
 
167
            content = self._escape_path(ie.symlink_target)
 
168
            content_spacer = ' '
 
169
 
 
170
        l = u'  %s %s %s%s%s\n' % (ie.kind, self._escape_path(path),
 
171
                                   ie.file_id.decode('utf8'),
 
172
                                   content_spacer, content)
 
173
        return l
160
174
 
161
175
    def as_text(self):
162
176
        return ''.join(self.as_text_lines())
163
177
 
164
178
    def as_short_text(self):
165
179
        """Return short digest-based testament."""
166
 
        s = sha()
167
 
        map(s.update, self.as_text_lines())
168
 
        return ('bazaar-ng testament short form 1\n'
 
180
        return (self.short_header + 
169
181
                'revision-id: %s\n'
170
182
                'sha1: %s\n'
171
 
                % (self.revision_id, s.hexdigest()))
 
183
                % (self.revision_id, self.as_sha1()))
172
184
 
173
185
    def _revprops_to_lines(self):
174
186
        """Pack up revision properties."""
176
188
            return []
177
189
        r = ['properties:\n']
178
190
        for name, value in sorted(self.revprops.items()):
179
 
            assert isinstance(name, str)
180
 
            assert not contains_whitespace(name)
 
191
            if contains_whitespace(name):
 
192
                raise ValueError(name)
181
193
            r.append('  %s:\n' % name)
182
194
            for line in value.splitlines():
183
 
                if not isinstance(line, str):
184
 
                    line = line.encode('utf-8')
185
 
                r.append('    %s\n' % line)
 
195
                r.append(u'    %s\n' % line)
186
196
        return r
187
197
 
188
198
    def as_sha1(self):
189
 
        return sha(self.as_short_text()).hexdigest()
 
199
        s = sha()
 
200
        map(s.update, self.as_text_lines())
 
201
        return s.hexdigest()
190
202
 
191
203
 
192
204
class StrictTestament(Testament):
193
 
    """This testament format is for use as a checksum in changesets"""
 
205
    """This testament format is for use as a checksum in bundle format 0.8"""
194
206
 
 
207
    long_header = 'bazaar-ng testament version 2.1\n'
 
208
    short_header = 'bazaar-ng testament short form 2.1\n'
195
209
    def _entry_to_line(self, path, ie):
196
 
        l = ie.revision.decode('utf-8') + ' '
197
 
        l += {True: 'yes', False: 'no'}[ie.executable] + ' '
198
 
        l += Testament._entry_to_line(self, path, ie)
 
210
        l = Testament._entry_to_line(self, path, ie)[:-1]
 
211
        l += ' ' + ie.revision
 
212
        l += {True: ' yes\n', False: ' no\n'}[ie.executable]
199
213
        return l
200
214
 
201
 
    def as_text_lines(self):
202
 
        lines = ['bazaar-ng testament version 2']
203
 
        lines.extend(Testament.as_text_lines(self)[1:])
204
 
        return lines
 
215
 
 
216
class StrictTestament3(StrictTestament):
 
217
    """This testament format is for use as a checksum in bundle format 0.9+
 
218
    
 
219
    It differs from StrictTestament by including data about the tree root.
 
220
    """
 
221
 
 
222
    long_header = 'bazaar testament version 3 strict\n'
 
223
    short_header = 'bazaar testament short form 3 strict\n'
 
224
    def _get_entries(self):
 
225
        return self.inventory.iter_entries()
 
226
 
 
227
    def _escape_path(self, path):
 
228
        if contains_linebreaks(path):
 
229
            raise ValueError(path)
 
230
        if path == '':
 
231
            path = '.'
 
232
        return unicode(path.replace('\\', '/').replace(' ', '\ '))