~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/testament.py

  • Committer: Wouter van Heyst
  • Date: 2006-06-06 22:37:30 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: larstiq@larstiq.dyndns.org-20060606223730-a308c5429fc6c617
change branch.{get,set}_parent to store a relative path but return full urls

Show diffs side-by-side

added added

removed removed

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