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