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