~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/testament.py

  • Committer: John Arbash Meinel
  • Date: 2007-11-13 20:37:09 UTC
  • mto: This revision was merged to the branch mainline in revision 3001.
  • Revision ID: john@arbash-meinel.com-20071113203709-kysdte0emqv84pnj
Fix bug #162486, by having RemoteBranch properly initialize self._revision_id_to_revno_map.

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
 
    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):
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
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
        for path, ie in self._get_entries():
134
135
            a(self._entry_to_line(path, ie))
135
136
        r.extend(self._revprops_to_lines())
136
137
        if __debug__:
137
138
            for l in r:
138
139
                assert isinstance(l, basestring), \
139
140
                    '%r of type %s is not a plain string' % (l, type(l))
140
 
        return r
 
141
        return [line.encode('utf-8') for line in r]
 
142
 
 
143
    def _get_entries(self):
 
144
        entries = self.inventory.iter_entries()
 
145
        entries.next()
 
146
        return entries
141
147
 
142
148
    def _escape_path(self, path):
143
149
        assert not contains_linebreaks(path)
144
 
        return unicode(path.replace('\\', '/').replace(' ', '\ ')).encode('utf-8')
 
150
        return unicode(path.replace('\\', '/').replace(' ', '\ '))
145
151
 
146
152
    def _entry_to_line(self, path, ie):
147
153
        """Turn an inventory entry into a testament line"""
148
 
        l = '  ' + str(ie.kind)
149
 
        l += ' ' + self._escape_path(path)
150
154
        assert not contains_whitespace(ie.file_id)
151
 
        l += ' ' + unicode(ie.file_id).encode('utf-8')
 
155
 
 
156
        content = ''
 
157
        content_spacer=''
152
158
        if ie.kind == 'file':
153
159
            # TODO: avoid switching on kind
154
160
            assert ie.text_sha1
155
 
            l += ' ' + ie.text_sha1
 
161
            content = ie.text_sha1
 
162
            content_spacer = ' '
156
163
        elif ie.kind == 'symlink':
157
164
            assert ie.symlink_target
158
 
            l += ' ' + self._escape_path(ie.symlink_target)
159
 
        l += '\n'
 
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)
160
171
        return l
161
172
 
162
173
    def as_text(self):
164
175
 
165
176
    def as_short_text(self):
166
177
        """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'
 
178
        return (self.short_header + 
170
179
                'revision-id: %s\n'
171
180
                'sha1: %s\n'
172
 
                % (self.revision_id, s.hexdigest()))
 
181
                % (self.revision_id, self.as_sha1()))
173
182
 
174
183
    def _revprops_to_lines(self):
175
184
        """Pack up revision properties."""
181
190
            assert not contains_whitespace(name)
182
191
            r.append('  %s:\n' % name)
183
192
            for line in value.splitlines():
184
 
                if not isinstance(line, str):
185
 
                    line = line.encode('utf-8')
186
 
                r.append('    %s\n' % line)
 
193
                r.append(u'    %s\n' % line)
187
194
        return r
 
195
 
 
196
    def as_sha1(self):
 
197
        s = sha()
 
198
        map(s.update, self.as_text_lines())
 
199
        return s.hexdigest()
 
200
 
 
201
 
 
202
class StrictTestament(Testament):
 
203
    """This testament format is for use as a checksum in bundle format 0.8"""
 
204
 
 
205
    long_header = 'bazaar-ng testament version 2.1\n'
 
206
    short_header = 'bazaar-ng testament short form 2.1\n'
 
207
    def _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]
 
211
        return l
 
212
 
 
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(' ', '\ '))