1
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005 Canonical Ltd
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.
72
72
from copy import copy
73
from cStringIO import StringIO
75
73
from sha import sha
77
75
from bzrlib.osutils import contains_whitespace, contains_linebreaks
79
78
class Testament(object):
80
79
"""Reduced summary of a revision.
87
86
- compared to a revision
89
long_header = 'bazaar-ng testament version 1\n'
90
short_header = 'bazaar-ng testament short form 1\n'
91
93
def from_revision(cls, repository, revision_id):
92
94
"""Produce a new testament from a historical revision"""
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
129
131
for l in self.message.splitlines():
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())
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]
143
def _get_entries(self):
144
entries = self.inventory.iter_entries()
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(' ', '\ '))
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')
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
155
163
elif ie.kind == 'symlink':
156
164
assert ie.symlink_target
157
l += ' ' + self._escape_path(ie.symlink_target)
159
return l.decode('utf-8')
165
content = self._escape_path(ie.symlink_target)
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)
161
173
def as_text(self):
162
174
return ''.join(self.as_text_lines())
164
176
def as_short_text(self):
165
177
"""Return short digest-based testament."""
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'
171
% (self.revision_id, s.hexdigest()))
181
% (self.revision_id, self.as_sha1()))
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)
188
196
def as_sha1(self):
189
return sha(self.as_short_text()).hexdigest()
198
map(s.update, self.as_text_lines())
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"""
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]
201
def as_text_lines(self):
202
lines = ['bazaar-ng testament version 2']
203
lines.extend(Testament.as_text_lines(self)[1:])
214
class StrictTestament3(StrictTestament):
215
"""This testament format is for use as a checksum in bundle format 0.9+
217
It differs from StrictTestament by including data about the tree root.
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()
225
def _escape_path(self, path):
226
assert not contains_linebreaks(path)
229
return unicode(path.replace('\\', '/').replace(' ', '\ '))