~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/testament.py

  • Committer: Robert Collins
  • Date: 2007-03-08 04:06:06 UTC
  • mfrom: (2323.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 2442.
  • Revision ID: robertc@robertcollins.net-20070308040606-84gsniv56huiyjt4
Merge bzr.dev.

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
115
117
        """
116
118
        r = []
117
119
        a = r.append
118
 
        a('bazaar-ng testament version 1\n')
 
120
        a(self.long_header)
119
121
        a('revision-id: %s\n' % self.revision_id)
120
122
        a('committer: %s\n' % self.committer)
121
123
        a('timestamp: %d\n' % self.timestamp)
129
131
        for l in self.message.splitlines():
130
132
            a('  %s\n' % l)
131
133
        a('inventory:\n')
132
 
        for path, ie in self.inventory.iter_entries():
 
134
        for path, ie in self._get_entries():
133
135
            a(self._entry_to_line(path, ie))
134
136
        r.extend(self._revprops_to_lines())
135
137
        if __debug__:
138
140
                    '%r of type %s is not a plain string' % (l, type(l))
139
141
        return [line.encode('utf-8') for line in r]
140
142
 
 
143
    def _get_entries(self):
 
144
        entries = self.inventory.iter_entries()
 
145
        entries.next()
 
146
        return entries
 
147
 
141
148
    def _escape_path(self, path):
142
149
        assert not contains_linebreaks(path)
143
 
        return unicode(path.replace('\\', '/').replace(' ', '\ ')).encode('utf-8')
 
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
154
        assert not contains_whitespace(ie.file_id)
150
 
        l += ' ' + unicode(ie.file_id).encode('utf-8')
 
155
 
 
156
        content = ''
 
157
        content_spacer=''
151
158
        if ie.kind == 'file':
152
159
            # TODO: avoid switching on kind
153
160
            assert ie.text_sha1
154
 
            l += ' ' + ie.text_sha1
 
161
            content = ie.text_sha1
 
162
            content_spacer = ' '
155
163
        elif ie.kind == 'symlink':
156
164
            assert ie.symlink_target
157
 
            l += ' ' + self._escape_path(ie.symlink_target)
158
 
        l += '\n'
159
 
        return l.decode('utf-8')
 
165
            content = self._escape_path(ie.symlink_target)
 
166
            content_spacer = ' '
 
167
 
 
168
        l = u'  %s %s %s%s%s\n' % (ie.kind, self._escape_path(path),
 
169
                                   ie.file_id.decode('utf8'),
 
170
                                   content_spacer, content)
 
171
        return l
160
172
 
161
173
    def as_text(self):
162
174
        return ''.join(self.as_text_lines())
163
175
 
164
176
    def as_short_text(self):
165
177
        """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'
 
178
        return (self.short_header + 
169
179
                'revision-id: %s\n'
170
180
                'sha1: %s\n'
171
 
                % (self.revision_id, s.hexdigest()))
 
181
                % (self.revision_id, self.as_sha1()))
172
182
 
173
183
    def _revprops_to_lines(self):
174
184
        """Pack up revision properties."""
180
190
            assert not contains_whitespace(name)
181
191
            r.append('  %s:\n' % name)
182
192
            for line in value.splitlines():
183
 
                if not isinstance(line, str):
184
 
                    line = line.encode('utf-8')
185
 
                r.append('    %s\n' % line)
 
193
                r.append(u'    %s\n' % line)
186
194
        return r
187
195
 
188
196
    def as_sha1(self):
189
 
        return sha(self.as_short_text()).hexdigest()
 
197
        s = sha()
 
198
        map(s.update, self.as_text_lines())
 
199
        return s.hexdigest()
190
200
 
191
201
 
192
202
class StrictTestament(Testament):
193
 
    """This testament format is for use as a checksum in changesets"""
 
203
    """This testament format is for use as a checksum in bundle format 0.8"""
194
204
 
 
205
    long_header = 'bazaar-ng testament version 2.1\n'
 
206
    short_header = 'bazaar-ng testament short form 2.1\n'
195
207
    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)
 
208
        l = Testament._entry_to_line(self, path, ie)[:-1]
 
209
        l += ' ' + ie.revision
 
210
        l += {True: ' yes\n', False: ' no\n'}[ie.executable]
199
211
        return l
200
212
 
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
 
213
 
 
214
class StrictTestament3(StrictTestament):
 
215
    """This testament format is for use as a checksum in bundle format 0.9+
 
216
    
 
217
    It differs from StrictTestament by including data about the tree root.
 
218
    """
 
219
 
 
220
    long_header = 'bazaar testament version 3 strict\n'
 
221
    short_header = 'bazaar testament short form 3 strict\n'
 
222
    def _get_entries(self):
 
223
        return self.inventory.iter_entries()
 
224
 
 
225
    def _escape_path(self, path):
 
226
        assert not contains_linebreaks(path)
 
227
        if path == '':
 
228
            path = '.'
 
229
        return unicode(path.replace('\\', '/').replace(' ', '\ '))