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]
192
194
raise KeyError(revision_id)
194
196
def revision_tree(self, repository, revision_id, base=None):
197
revision_id = osutils.safe_revision_id(revision_id)
195
198
revision = self.get_revision(revision_id)
196
199
base = self.get_base(revision)
197
if base == revision_id:
198
raise AssertionError()
200
assert base != revision_id
199
201
if not self._validated_revisions_against_repo:
200
202
self._validate_references_from_repository(repository)
201
203
revision_info = self.get_revision_info(revision_id)
202
204
inventory_revision_id = revision_id
203
bundle_tree = BundleTree(repository.revision_tree(base),
205
bundle_tree = BundleTree(repository.revision_tree(base),
204
206
inventory_revision_id)
205
207
self._update_tree(bundle_tree, revision_id)
239
241
for rev_info in self.revisions:
240
242
checked[rev_info.revision_id] = True
241
243
add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
243
245
for (rev, rev_info) in zip(self.real_revisions, self.revisions):
244
246
add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
248
250
for revision_id, sha1 in rev_to_sha.iteritems():
249
251
if repository.has_revision(revision_id):
250
testament = StrictTestament.from_revision(repository,
252
testament = StrictTestament.from_revision(repository,
252
254
local_sha1 = self._testament_sha1_from_revision(repository,
254
256
if sha1 != local_sha1:
255
raise BzrError('sha1 mismatch. For revision id {%s}'
257
raise BzrError('sha1 mismatch. For revision id {%s}'
256
258
'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
259
261
elif revision_id not in checked:
260
262
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))
262
279
if len(missing) > 0:
263
280
# I don't know if this is an error yet
264
281
warning('Not all revision hashes could be validated.'
270
287
"""At this point we should have generated the BundleTree,
271
288
so build up an inventory, and make sure the hashes match.
291
assert inv is not None
273
293
# Now we should have a complete inventory entry.
274
294
s = serializer_v5.write_inventory_to_string(inv)
275
295
sha1 = sha_string(s)
276
296
# Target revision is the last entry in the real_revisions list
277
297
rev = self.get_revision(revision_id)
278
if rev.revision_id != revision_id:
279
raise AssertionError()
298
assert rev.revision_id == revision_id
280
299
if sha1 != rev.inventory_sha1:
281
300
open(',,bogus-inv', 'wb').write(s)
282
301
warning('Inventory sha hash mismatch for revision %s. %s'
288
307
# This is a mapping from each revision id to it's sha hash
291
310
rev = self.get_revision(revision_id)
292
311
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()
312
assert rev.revision_id == rev_info.revision_id
313
assert rev.revision_id == revision_id
297
314
sha1 = self._testament_sha1(rev, inventory)
298
315
if sha1 != rev_info.sha1:
299
316
raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
331
348
if name == 'last-changed':
332
349
last_changed = value
333
350
elif name == 'executable':
351
assert value in ('yes', 'no'), value
334
352
val = (value == 'yes')
335
353
bundle_tree.note_executable(new_path, val)
336
354
elif name == 'target':
340
358
return last_changed, encoding
342
360
def do_patch(path, lines, encoding):
343
if encoding == 'base64':
361
if encoding is not None:
362
assert encoding == 'base64'
344
363
patch = base64.decodestring(''.join(lines))
345
elif encoding is None:
346
365
patch = ''.join(lines)
348
raise ValueError(encoding)
349
366
bundle_tree.note_patch(path, patch)
351
368
def renamed(kind, extra, lines):
440
457
' (unrecognized action): %r' % action_line)
441
458
valid_actions[action](kind, extra, lines)
443
def install_revisions(self, target_repo, stream_input=True):
444
"""Install revisions and return the target revision
446
:param target_repo: The repository to install into
447
:param stream_input: Ignored by this implementation.
460
def install_revisions(self, target_repo):
461
"""Install revisions and return the target revision"""
449
462
apply_bundle.install_bundle(target_repo, self)
450
463
return self.target
480
493
def note_rename(self, old_path, new_path):
481
494
"""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)
495
assert new_path not in self._renamed
496
assert old_path not in self._renamed_r
486
497
self._renamed[new_path] = old_path
487
498
self._renamed_r[old_path] = new_path
519
530
def old_path(self, new_path):
520
531
"""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)
532
assert new_path[:1] not in ('\\', '/')
523
533
old_path = self._renamed.get(new_path)
524
534
if old_path is not None:
540
550
if old_path in self._renamed_r:
544
554
def new_path(self, old_path):
545
555
"""Get the new_path (path in the target_tree) for the file at old_path
546
556
in the base tree.
548
if old_path[:1] in ('\\', '/'):
549
raise ValueError(old_path)
558
assert old_path[:1] not in ('\\', '/')
550
559
new_path = self._renamed_r.get(old_path)
551
560
if new_path is not None:
606
615
new_path = self.id2path(file_id)
607
616
return self.base_tree.path2id(new_path)
609
618
def get_file(self, file_id):
610
619
"""Return a file-like object containing the new contents of the
611
620
file given by file_id.
622
631
patch_original = None
623
632
file_patch = self.patches.get(self.id2path(file_id))
624
633
if file_patch is None:
625
if (patch_original is None and
634
if (patch_original is None and
626
635
self.get_kind(file_id) == 'directory'):
627
636
return StringIO()
628
if patch_original is None:
629
raise AssertionError("None: %s" % file_id)
637
assert patch_original is not None, "None: %s" % file_id
630
638
return patch_original
632
if file_patch.startswith('\\'):
634
'Malformed patch for %s, %r' % (file_id, file_patch))
640
assert not file_patch.startswith('\\'), \
641
'Malformed patch for %s, %r' % (file_id, file_patch)
635
642
return patched_file(file_patch, patch_original)
637
644
def get_symlink_target(self, file_id):
684
691
This need to be called before ever accessing self.inventory
686
693
from os.path import dirname, basename
695
assert self.base_tree is not None
687
696
base_inv = self.base_tree.inventory
688
697
inv = Inventory(None, self.revision_id)