24
24
from bzrlib import (
28
28
from bzrlib.bundle import apply_bundle
29
from bzrlib.errors import (
33
from bzrlib.inventory import (
39
from bzrlib.osutils import sha_string, pathjoin
29
from bzrlib.errors import (TestamentMismatch, BzrError,
30
MalformedHeader, MalformedPatches, NotABundle)
31
from bzrlib.inventory import (Inventory, InventoryEntry,
32
InventoryDirectory, InventoryFile,
34
from bzrlib.osutils import sha_file, sha_string, pathjoin
40
35
from bzrlib.revision import Revision, NULL_REVISION
41
36
from bzrlib.testament import StrictTestament
42
37
from bzrlib.trace import mutter, warning
38
import bzrlib.transport
43
39
from bzrlib.tree import Tree
40
import bzrlib.urlutils
44
41
from bzrlib.xml5 import serializer_v5
79
76
if self.properties:
80
77
for property in self.properties:
81
78
key_end = property.find(': ')
83
if not property.endswith(':'):
84
raise ValueError(property)
85
key = str(property[:-1])
88
key = str(property[:key_end])
89
value = property[key_end+2:]
79
assert key_end is not None
80
key = property[:key_end].encode('utf-8')
81
value = property[key_end+2:].encode('utf-8')
90
82
rev.properties[key] = value
95
def from_revision(revision):
96
revision_info = RevisionInfo(revision.revision_id)
97
date = timestamp.format_highres_date(revision.timestamp,
99
revision_info.date = date
100
revision_info.timezone = revision.timezone
101
revision_info.timestamp = revision.timestamp
102
revision_info.message = revision.message.split('\n')
103
revision_info.properties = [': '.join(p) for p in
104
revision.properties.iteritems()]
108
87
class BundleInfo(object):
109
88
"""This contains the meta information. Stuff that allows you to
110
89
recreate the revision or inventory XML.
112
def __init__(self, bundle_format=None):
113
self.bundle_format = None
114
92
self.committer = None
116
94
self.message = None
161
136
def get_base(self, revision):
162
137
revision_info = self.get_revision_info(revision.revision_id)
163
138
if revision_info.base_id is not None:
164
return revision_info.base_id
139
if revision_info.base_id == NULL_REVISION:
142
return revision_info.base_id
165
143
if len(revision.parent_ids) == 0:
166
144
# There is no base listed, and
167
145
# the lowest revision doesn't have a parent
168
146
# so this is probably against the empty tree
169
# and thus base truly is NULL_REVISION
147
# and thus base truly is None
172
150
return revision.parent_ids[-1]
194
172
raise KeyError(revision_id)
196
174
def revision_tree(self, repository, revision_id, base=None):
175
revision_id = osutils.safe_revision_id(revision_id)
197
176
revision = self.get_revision(revision_id)
198
177
base = self.get_base(revision)
199
if base == revision_id:
200
raise AssertionError()
201
if not self._validated_revisions_against_repo:
202
self._validate_references_from_repository(repository)
178
assert base != revision_id
179
self._validate_references_from_repository(repository)
203
180
revision_info = self.get_revision_info(revision_id)
204
181
inventory_revision_id = revision_id
205
bundle_tree = BundleTree(repository.revision_tree(base),
182
bundle_tree = BundleTree(repository.revision_tree(base),
206
183
inventory_revision_id)
207
184
self._update_tree(bundle_tree, revision_id)
209
186
inv = bundle_tree.inventory
210
187
self._validate_inventory(inv, revision_id)
211
self._validate_revision(bundle_tree, revision_id)
188
self._validate_revision(inv, revision_id)
213
190
return bundle_tree
250
227
for revision_id, sha1 in rev_to_sha.iteritems():
251
228
if repository.has_revision(revision_id):
252
testament = StrictTestament.from_revision(repository,
229
testament = StrictTestament.from_revision(repository,
254
231
local_sha1 = self._testament_sha1_from_revision(repository,
256
233
if sha1 != local_sha1:
257
raise BzrError('sha1 mismatch. For revision id {%s}'
234
raise BzrError('sha1 mismatch. For revision id {%s}'
258
235
'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
261
238
elif revision_id not in checked:
262
239
missing[revision_id] = sha1
241
for inv_id, sha1 in inv_to_sha.iteritems():
242
if repository.has_revision(inv_id):
243
# Note: branch.get_inventory_sha1() just returns the value that
244
# is stored in the revision text, and that value may be out
245
# of date. This is bogus, because that means we aren't
246
# validating the actual text, just that we wrote and read the
247
# string. But for now, what the hell.
248
local_sha1 = repository.get_inventory_sha1(inv_id)
249
if sha1 != local_sha1:
250
raise BzrError('sha1 mismatch. For inventory id {%s}'
251
'local: %s, bundle: %s' %
252
(inv_id, local_sha1, sha1))
264
256
if len(missing) > 0:
265
257
# I don't know if this is an error yet
266
258
warning('Not all revision hashes could be validated.'
267
259
' Unable validate %d hashes' % len(missing))
268
260
mutter('Verified %d sha hashes for the bundle.' % count)
269
self._validated_revisions_against_repo = True
271
262
def _validate_inventory(self, inv, revision_id):
272
263
"""At this point we should have generated the BundleTree,
273
264
so build up an inventory, and make sure the hashes match.
267
assert inv is not None
275
269
# Now we should have a complete inventory entry.
276
270
s = serializer_v5.write_inventory_to_string(inv)
277
271
sha1 = sha_string(s)
278
272
# Target revision is the last entry in the real_revisions list
279
273
rev = self.get_revision(revision_id)
280
if rev.revision_id != revision_id:
281
raise AssertionError()
274
assert rev.revision_id == revision_id
282
275
if sha1 != rev.inventory_sha1:
283
f = open(',,bogus-inv', 'wb')
276
open(',,bogus-inv', 'wb').write(s)
288
277
warning('Inventory sha hash mismatch for revision %s. %s'
289
278
' != %s' % (revision_id, sha1, rev.inventory_sha1))
291
def _validate_revision(self, tree, revision_id):
280
def _validate_revision(self, inventory, revision_id):
292
281
"""Make sure all revision entries match their checksum."""
294
# This is a mapping from each revision id to its sha hash
283
# This is a mapping from each revision id to it's sha hash
297
286
rev = self.get_revision(revision_id)
298
287
rev_info = self.get_revision_info(revision_id)
299
if not (rev.revision_id == rev_info.revision_id):
300
raise AssertionError()
301
if not (rev.revision_id == revision_id):
302
raise AssertionError()
303
sha1 = self._testament_sha1(rev, tree)
288
assert rev.revision_id == rev_info.revision_id
289
assert rev.revision_id == revision_id
290
sha1 = self._testament_sha1(rev, inventory)
304
291
if sha1 != rev_info.sha1:
305
292
raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
306
293
if rev.revision_id in rev_to_sha1:
446
433
' (unrecognized action): %r' % action_line)
447
434
valid_actions[action](kind, extra, lines)
449
def install_revisions(self, target_repo, stream_input=True):
450
"""Install revisions and return the target revision
452
:param target_repo: The repository to install into
453
:param stream_input: Ignored by this implementation.
436
def install_revisions(self, target_repo):
437
"""Install revisions and return the target revision"""
455
438
apply_bundle.install_bundle(target_repo, self)
456
439
return self.target
458
def get_merge_request(self, target_repo):
459
"""Provide data for performing a merge
461
Returns suggested base, suggested target, and patch verification status
463
return None, self.target, 'inapplicable'
466
442
class BundleTree(Tree):
468
443
def __init__(self, base_tree, revision_id):
469
444
self.base_tree = base_tree
470
445
self._renamed = {} # Mapping from old_path => new_path
629
600
patch_original = None
630
601
file_patch = self.patches.get(self.id2path(file_id))
631
602
if file_patch is None:
632
if (patch_original is None and
603
if (patch_original is None and
633
604
self.get_kind(file_id) == 'directory'):
634
605
return StringIO()
635
if patch_original is None:
636
raise AssertionError("None: %s" % file_id)
606
assert patch_original is not None, "None: %s" % file_id
637
607
return patch_original
639
if file_patch.startswith('\\'):
641
'Malformed patch for %s, %r' % (file_id, file_patch))
609
assert not file_patch.startswith('\\'), \
610
'Malformed patch for %s, %r' % (file_id, file_patch)
642
611
return patched_file(file_patch, patch_original)
644
613
def get_symlink_target(self, file_id):
742
714
for path, entry in self.inventory.iter_entries():
743
715
yield entry.file_id
745
def list_files(self, include_root=False, from_dir=None, recursive=True):
746
# The only files returned by this are those from the version
751
from_dir_id = inv.path2id(from_dir)
752
if from_dir_id is None:
753
# Directory not versioned
755
entries = inv.iter_entries(from_dir=from_dir_id, recursive=recursive)
756
if inv.root is not None and not include_root and from_dir is None:
757
# skip the root for compatability with the current apis.
759
for path, entry in entries:
760
yield path, 'V', entry.kind, entry.file_id, entry
762
717
def sorted_path_id(self):
764
719
for result in self._new_id.iteritems():