59
59
* the testament uses unix line-endings (\n)
62
from __future__ import absolute_import
62
64
# XXX: At the moment, clients trust that the graph described in a weave
63
65
# is accurate, but that's not covered by the testament. Perhaps the best
64
66
# fix is when verifying a revision to make sure that every file mentioned
92
96
long_header = 'bazaar-ng testament version 1\n'
93
97
short_header = 'bazaar-ng testament short form 1\n'
96
101
def from_revision(cls, repository, revision_id):
97
"""Produce a new testament from a historical revision"""
102
"""Produce a new testament from a historical revision."""
98
103
rev = repository.get_revision(revision_id)
99
inventory = repository.get_inventory(revision_id)
100
return cls(rev, inventory)
102
def __init__(self, rev, inventory):
103
"""Create a new testament for rev using inventory."""
104
tree = repository.revision_tree(revision_id)
105
return cls(rev, tree)
108
def from_revision_tree(cls, tree):
109
"""Produce a new testament from a revision tree."""
110
rev = tree._repository.get_revision(tree.get_revision_id())
111
return cls(rev, tree)
113
def __init__(self, rev, tree):
114
"""Create a new testament for rev using tree."""
104
115
self.revision_id = rev.revision_id
105
116
self.committer = rev.committer
106
117
self.timezone = rev.timezone or 0
107
118
self.timestamp = rev.timestamp
108
119
self.message = rev.message
109
120
self.parent_ids = rev.parent_ids[:]
110
self.inventory = inventory
121
if not isinstance(tree, Tree):
122
raise TypeError("As of bzr 2.4 Testament.__init__() takes a "
123
"Revision and a Tree.")
111
125
self.revprops = copy(rev.properties)
112
126
if contains_whitespace(self.revision_id):
113
127
raise ValueError(self.revision_id)
125
139
a(self.long_header)
126
a('revision-id: %s\n' % self.revision_id)
140
a('revision-id: %s\n' % self.revision_id.decode('utf-8'))
127
141
a('committer: %s\n' % self.committer)
128
142
a('timestamp: %d\n' % self.timestamp)
129
143
a('timezone: %d\n' % self.timezone)
132
146
for parent_id in sorted(self.parent_ids):
133
147
if contains_whitespace(parent_id):
134
148
raise ValueError(parent_id)
135
a(' %s\n' % parent_id)
149
a(' %s\n' % parent_id.decode('utf-8'))
137
151
for l in self.message.splitlines():
143
157
return [line.encode('utf-8') for line in r]
145
159
def _get_entries(self):
146
entries = self.inventory.iter_entries()
160
return ((path, ie) for (path, file_class, kind, ie) in
161
self.tree.list_files(include_root=self.include_root))
150
163
def _escape_path(self, path):
151
164
if contains_linebreaks(path):
152
165
raise ValueError(path)
153
return unicode(path.replace('\\', '/').replace(' ', '\ '))
166
if not isinstance(path, text_type):
167
# TODO(jelmer): Clean this up for pad.lv/1696545
168
path = path.decode('ascii')
169
return path.replace(u'\\', u'/').replace(u' ', u'\\ ')
155
171
def _entry_to_line(self, path, ie):
156
172
"""Turn an inventory entry into a testament line"""
157
173
if contains_whitespace(ie.file_id):
158
174
raise ValueError(ie.file_id)
161
177
if ie.kind == 'file':
162
178
# TODO: avoid switching on kind
163
179
if not ie.text_sha1:
164
180
raise AssertionError()
165
content = ie.text_sha1
181
content = ie.text_sha1.decode('ascii')
166
182
content_spacer = ' '
167
183
elif ie.kind == 'symlink':
168
184
if not ie.symlink_target:
178
194
def as_text(self):
179
return ''.join(self.as_text_lines())
195
return b''.join(self.as_text_lines())
181
197
def as_short_text(self):
182
198
"""Return short digest-based testament."""
183
return (self.short_header +
199
return (self.short_header.encode('ascii') +
186
202
% (self.revision_id, self.as_sha1()))
188
204
def _revprops_to_lines(self):
210
224
long_header = 'bazaar-ng testament version 2.1\n'
211
225
short_header = 'bazaar-ng testament short form 2.1\n'
212
228
def _entry_to_line(self, path, ie):
213
229
l = Testament._entry_to_line(self, path, ie)[:-1]
214
l += ' ' + ie.revision
230
l += ' ' + ie.revision.decode('utf-8')
215
231
l += {True: ' yes\n', False: ' no\n'}[ie.executable]
225
241
long_header = 'bazaar testament version 3 strict\n'
226
242
short_header = 'bazaar testament short form 3 strict\n'
227
def _get_entries(self):
228
return self.inventory.iter_entries()
230
245
def _escape_path(self, path):
231
246
if contains_linebreaks(path):
232
247
raise ValueError(path)
235
return unicode(path.replace('\\', '/').replace(' ', '\ '))
248
if not isinstance(path, text_type):
249
# TODO(jelmer): Clean this up for pad.lv/1696545
250
path = path.decode('ascii')
253
return path.replace(u'\\', u'/').replace(u' ', u'\\ ')