~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/testament.py

Fix BzrDir.create_workingtree for NULL_REVISION

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
    def from_revision(cls, branch, revision_id):
 
93
    def from_revision(cls, repository, revision_id):
92
94
        """Produce a new testament from a historical revision"""
93
 
        rev = branch.get_revision(revision_id)
94
 
        inventory = branch.get_inventory(revision_id)
 
95
        rev = repository.get_revision(revision_id)
 
96
        inventory = repository.get_inventory(revision_id)
95
97
        return cls(rev, inventory)
96
98
 
97
99
    def __init__(self, rev, inventory):
114
116
        hashed in that encoding.
115
117
        """
116
118
        r = []
117
 
        def a(s):
118
 
            r.append(s)
119
 
        a('bazaar-ng testament version 1\n')
 
119
        a = r.append
 
120
        a(self.long_header)
120
121
        a('revision-id: %s\n' % self.revision_id)
121
122
        a('committer: %s\n' % self.committer)
122
123
        a('timestamp: %d\n' % self.timestamp)
130
131
        for l in self.message.splitlines():
131
132
            a('  %s\n' % l)
132
133
        a('inventory:\n')
133
 
        for path, ie in self.inventory.iter_entries():
 
134
        entries = self.inventory.iter_entries()
 
135
        entries.next()
 
136
        for path, ie in entries:
134
137
            a(self._entry_to_line(path, ie))
135
138
        r.extend(self._revprops_to_lines())
136
139
        if __debug__:
137
140
            for l in r:
138
141
                assert isinstance(l, basestring), \
139
142
                    '%r of type %s is not a plain string' % (l, type(l))
140
 
        return r
 
143
        return [line.encode('utf-8') for line in r]
141
144
 
142
145
    def _escape_path(self, path):
143
146
        assert not contains_linebreaks(path)
144
 
        return unicode(path.replace('\\', '/').replace(' ', '\ ')).encode('utf-8')
 
147
        return unicode(path.replace('\\', '/').replace(' ', '\ '))
145
148
 
146
149
    def _entry_to_line(self, path, ie):
147
150
        """Turn an inventory entry into a testament line"""
148
 
        l = '  ' + str(ie.kind)
149
 
        l += ' ' + self._escape_path(path)
150
151
        assert not contains_whitespace(ie.file_id)
151
 
        l += ' ' + unicode(ie.file_id).encode('utf-8')
 
152
 
 
153
        content = ''
 
154
        content_spacer=''
152
155
        if ie.kind == 'file':
153
156
            # TODO: avoid switching on kind
154
157
            assert ie.text_sha1
155
 
            l += ' ' + ie.text_sha1
 
158
            content = ie.text_sha1
 
159
            content_spacer = ' '
156
160
        elif ie.kind == 'symlink':
157
161
            assert ie.symlink_target
158
 
            l += ' ' + self._escape_path(ie.symlink_target)
159
 
        l += '\n'
 
162
            content = self._escape_path(ie.symlink_target)
 
163
            content_spacer = ' '
 
164
 
 
165
        l = u'  %s %s %s%s%s\n' % (ie.kind, self._escape_path(path),
 
166
                                   unicode(ie.file_id),
 
167
                                   content_spacer, content)
160
168
        return l
161
169
 
162
170
    def as_text(self):
164
172
 
165
173
    def as_short_text(self):
166
174
        """Return short digest-based testament."""
167
 
        s = sha()
168
 
        map(s.update, self.as_text_lines())
169
 
        return ('bazaar-ng testament short form 1\n'
 
175
        return (self.short_header + 
170
176
                'revision-id: %s\n'
171
177
                'sha1: %s\n'
172
 
                % (self.revision_id, s.hexdigest()))
 
178
                % (self.revision_id, self.as_sha1()))
173
179
 
174
180
    def _revprops_to_lines(self):
175
181
        """Pack up revision properties."""
181
187
            assert not contains_whitespace(name)
182
188
            r.append('  %s:\n' % name)
183
189
            for line in value.splitlines():
184
 
                if not isinstance(line, str):
185
 
                    line = line.encode('utf-8')
186
 
                r.append('    %s\n' % line)
 
190
                r.append(u'    %s\n' % line)
187
191
        return r
 
192
 
 
193
    def as_sha1(self):
 
194
        s = sha()
 
195
        map(s.update, self.as_text_lines())
 
196
        return s.hexdigest()
 
197
 
 
198
 
 
199
class StrictTestament(Testament):
 
200
    """This testament format is for use as a checksum in changesets"""
 
201
 
 
202
    long_header = 'bazaar-ng testament version 2.1\n'
 
203
    short_header = 'bazaar-ng testament short form 2.1\n'
 
204
    def _entry_to_line(self, path, ie):
 
205
        l = Testament._entry_to_line(self, path, ie)[:-1]
 
206
        l += ' ' + ie.revision
 
207
        l += {True: ' yes\n', False: ' no\n'}[ie.executable]
 
208
        return l