13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
28
28
import bzrlib.errors
29
29
from bzrlib.bundle import apply_bundle
30
from bzrlib.errors import (TestamentMismatch, BzrError,
30
from bzrlib.errors import (TestamentMismatch, BzrError,
31
31
MalformedHeader, MalformedPatches, NotABundle)
32
32
from bzrlib.inventory import (Inventory, InventoryEntry,
33
33
InventoryDirectory, InventoryFile,
78
78
for property in self.properties:
79
79
key_end = property.find(': ')
81
if not property.endswith(':'):
82
raise ValueError(property)
81
assert property.endswith(':')
83
82
key = str(property[:-1])
159
158
def get_base(self, revision):
160
159
revision_info = self.get_revision_info(revision.revision_id)
161
160
if revision_info.base_id is not None:
162
return revision_info.base_id
161
if revision_info.base_id == NULL_REVISION:
164
return revision_info.base_id
163
165
if len(revision.parent_ids) == 0:
164
166
# There is no base listed, and
165
167
# the lowest revision doesn't have a parent
166
168
# so this is probably against the empty tree
167
# and thus base truly is NULL_REVISION
169
# and thus base truly is None
170
172
return revision.parent_ids[-1]
194
196
def revision_tree(self, repository, revision_id, base=None):
195
197
revision = self.get_revision(revision_id)
196
198
base = self.get_base(revision)
197
if base == revision_id:
198
raise AssertionError()
199
assert base != revision_id
199
200
if not self._validated_revisions_against_repo:
200
201
self._validate_references_from_repository(repository)
201
202
revision_info = self.get_revision_info(revision_id)
202
203
inventory_revision_id = revision_id
203
bundle_tree = BundleTree(repository.revision_tree(base),
204
bundle_tree = BundleTree(repository.revision_tree(base),
204
205
inventory_revision_id)
205
206
self._update_tree(bundle_tree, revision_id)
239
240
for rev_info in self.revisions:
240
241
checked[rev_info.revision_id] = True
241
242
add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
243
244
for (rev, rev_info) in zip(self.real_revisions, self.revisions):
244
245
add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
248
249
for revision_id, sha1 in rev_to_sha.iteritems():
249
250
if repository.has_revision(revision_id):
250
testament = StrictTestament.from_revision(repository,
251
testament = StrictTestament.from_revision(repository,
252
253
local_sha1 = self._testament_sha1_from_revision(repository,
254
255
if sha1 != local_sha1:
255
raise BzrError('sha1 mismatch. For revision id {%s}'
256
raise BzrError('sha1 mismatch. For revision id {%s}'
256
257
'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
259
260
elif revision_id not in checked:
260
261
missing[revision_id] = sha1
263
for inv_id, sha1 in inv_to_sha.iteritems():
264
if repository.has_revision(inv_id):
265
# Note: branch.get_inventory_sha1() just returns the value that
266
# is stored in the revision text, and that value may be out
267
# of date. This is bogus, because that means we aren't
268
# validating the actual text, just that we wrote and read the
269
# string. But for now, what the hell.
270
local_sha1 = repository.get_inventory_sha1(inv_id)
271
if sha1 != local_sha1:
272
raise BzrError('sha1 mismatch. For inventory id {%s}'
273
'local: %s, bundle: %s' %
274
(inv_id, local_sha1, sha1))
262
278
if len(missing) > 0:
263
279
# I don't know if this is an error yet
264
280
warning('Not all revision hashes could be validated.'
270
286
"""At this point we should have generated the BundleTree,
271
287
so build up an inventory, and make sure the hashes match.
290
assert inv is not None
273
292
# Now we should have a complete inventory entry.
274
293
s = serializer_v5.write_inventory_to_string(inv)
275
294
sha1 = sha_string(s)
276
295
# Target revision is the last entry in the real_revisions list
277
296
rev = self.get_revision(revision_id)
278
if rev.revision_id != revision_id:
279
raise AssertionError()
297
assert rev.revision_id == revision_id
280
298
if sha1 != rev.inventory_sha1:
281
299
open(',,bogus-inv', 'wb').write(s)
282
300
warning('Inventory sha hash mismatch for revision %s. %s'
288
306
# This is a mapping from each revision id to it's sha hash
291
309
rev = self.get_revision(revision_id)
292
310
rev_info = self.get_revision_info(revision_id)
293
if not (rev.revision_id == rev_info.revision_id):
294
raise AssertionError()
295
if not (rev.revision_id == revision_id):
296
raise AssertionError()
311
assert rev.revision_id == rev_info.revision_id
312
assert rev.revision_id == revision_id
297
313
sha1 = self._testament_sha1(rev, inventory)
298
314
if sha1 != rev_info.sha1:
299
315
raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
331
347
if name == 'last-changed':
332
348
last_changed = value
333
349
elif name == 'executable':
350
assert value in ('yes', 'no'), value
334
351
val = (value == 'yes')
335
352
bundle_tree.note_executable(new_path, val)
336
353
elif name == 'target':
340
357
return last_changed, encoding
342
359
def do_patch(path, lines, encoding):
343
if encoding == 'base64':
360
if encoding is not None:
361
assert encoding == 'base64'
344
362
patch = base64.decodestring(''.join(lines))
345
elif encoding is None:
346
364
patch = ''.join(lines)
348
raise ValueError(encoding)
349
365
bundle_tree.note_patch(path, patch)
351
367
def renamed(kind, extra, lines):
480
496
def note_rename(self, old_path, new_path):
481
497
"""A file/directory has been renamed from old_path => new_path"""
482
if new_path in self._renamed:
483
raise AssertionError(new_path)
484
if old_path in self._renamed_r:
485
raise AssertionError(old_path)
498
assert new_path not in self._renamed
499
assert old_path not in self._renamed_r
486
500
self._renamed[new_path] = old_path
487
501
self._renamed_r[old_path] = new_path
519
533
def old_path(self, new_path):
520
534
"""Get the old_path (path in the base_tree) for the file at new_path"""
521
if new_path[:1] in ('\\', '/'):
522
raise ValueError(new_path)
535
assert new_path[:1] not in ('\\', '/')
523
536
old_path = self._renamed.get(new_path)
524
537
if old_path is not None:
540
553
if old_path in self._renamed_r:
544
557
def new_path(self, old_path):
545
558
"""Get the new_path (path in the target_tree) for the file at old_path
546
559
in the base tree.
548
if old_path[:1] in ('\\', '/'):
549
raise ValueError(old_path)
561
assert old_path[:1] not in ('\\', '/')
550
562
new_path = self._renamed_r.get(old_path)
551
563
if new_path is not None:
606
618
new_path = self.id2path(file_id)
607
619
return self.base_tree.path2id(new_path)
609
621
def get_file(self, file_id):
610
622
"""Return a file-like object containing the new contents of the
611
623
file given by file_id.
622
634
patch_original = None
623
635
file_patch = self.patches.get(self.id2path(file_id))
624
636
if file_patch is None:
625
if (patch_original is None and
637
if (patch_original is None and
626
638
self.get_kind(file_id) == 'directory'):
627
639
return StringIO()
628
if patch_original is None:
629
raise AssertionError("None: %s" % file_id)
640
assert patch_original is not None, "None: %s" % file_id
630
641
return patch_original
632
if file_patch.startswith('\\'):
634
'Malformed patch for %s, %r' % (file_id, file_patch))
643
assert not file_patch.startswith('\\'), \
644
'Malformed patch for %s, %r' % (file_id, file_patch)
635
645
return patched_file(file_patch, patch_original)
637
647
def get_symlink_target(self, file_id):
684
694
This need to be called before ever accessing self.inventory
686
696
from os.path import dirname, basename
698
assert self.base_tree is not None
687
699
base_inv = self.base_tree.inventory
688
700
inv = Inventory(None, self.revision_id)