/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/bundle/bundle_data.py

  • Committer: Robert Collins
  • Date: 2007-03-05 03:10:21 UTC
  • mto: (2255.11.4 dirstate)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: robertc@robertcollins.net-20070305031021-ypbakvagbivyw5mu
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
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
16
16
 
17
17
"""Read in a bundle stream, and process it into a BundleReader object."""
18
18
 
23
23
 
24
24
from bzrlib import (
25
25
    osutils,
26
 
    timestamp,
27
26
    )
28
27
import bzrlib.errors
29
 
from bzrlib.bundle import apply_bundle
30
 
from bzrlib.errors import (TestamentMismatch, BzrError,
 
28
from bzrlib.errors import (TestamentMismatch, BzrError, 
31
29
                           MalformedHeader, MalformedPatches, NotABundle)
32
30
from bzrlib.inventory import (Inventory, InventoryEntry,
33
31
                              InventoryDirectory, InventoryFile,
77
75
        if self.properties:
78
76
            for property in self.properties:
79
77
                key_end = property.find(': ')
80
 
                if key_end == -1:
81
 
                    if not property.endswith(':'):
82
 
                        raise ValueError(property)
83
 
                    key = str(property[:-1])
84
 
                    value = ''
85
 
                else:
86
 
                    key = str(property[:key_end])
87
 
                    value = property[key_end+2:]
 
78
                assert key_end is not None
 
79
                key = property[:key_end].encode('utf-8')
 
80
                value = property[key_end+2:].encode('utf-8')
88
81
                rev.properties[key] = value
89
82
 
90
83
        return rev
91
84
 
92
 
    @staticmethod
93
 
    def from_revision(revision):
94
 
        revision_info = RevisionInfo(revision.revision_id)
95
 
        date = timestamp.format_highres_date(revision.timestamp,
96
 
                                             revision.timezone)
97
 
        revision_info.date = date
98
 
        revision_info.timezone = revision.timezone
99
 
        revision_info.timestamp = revision.timestamp
100
 
        revision_info.message = revision.message.split('\n')
101
 
        revision_info.properties = [': '.join(p) for p in
102
 
                                    revision.properties.iteritems()]
103
 
        return revision_info
104
 
 
105
85
 
106
86
class BundleInfo(object):
107
87
    """This contains the meta information. Stuff that allows you to
108
88
    recreate the revision or inventory XML.
109
89
    """
110
 
    def __init__(self, bundle_format=None):
111
 
        self.bundle_format = None
 
90
    def __init__(self):
112
91
        self.committer = None
113
92
        self.date = None
114
93
        self.message = None
125
104
        self.timestamp = None
126
105
        self.timezone = None
127
106
 
128
 
        # Have we checked the repository yet?
129
 
        self._validated_revisions_against_repo = False
130
 
 
131
107
    def __str__(self):
132
108
        return pprint.pformat(self.__dict__)
133
109
 
136
112
        split up, based on the assumptions that can be made
137
113
        when information is missing.
138
114
        """
139
 
        from bzrlib.timestamp import unpack_highres_date
 
115
        from bzrlib.bundle.serializer import unpack_highres_date
140
116
        # Put in all of the guessable information.
141
117
        if not self.timestamp and self.date:
142
118
            self.timestamp, self.timezone = unpack_highres_date(self.date)
159
135
    def get_base(self, revision):
160
136
        revision_info = self.get_revision_info(revision.revision_id)
161
137
        if revision_info.base_id is not None:
162
 
            return revision_info.base_id
 
138
            if revision_info.base_id == NULL_REVISION:
 
139
                return None
 
140
            else:
 
141
                return revision_info.base_id
163
142
        if len(revision.parent_ids) == 0:
164
143
            # There is no base listed, and
165
144
            # the lowest revision doesn't have a parent
166
145
            # so this is probably against the empty tree
167
 
            # and thus base truly is NULL_REVISION
168
 
            return NULL_REVISION
 
146
            # and thus base truly is None
 
147
            return None
169
148
        else:
170
149
            return revision.parent_ids[-1]
171
150
 
194
173
    def revision_tree(self, repository, revision_id, base=None):
195
174
        revision = self.get_revision(revision_id)
196
175
        base = self.get_base(revision)
197
 
        if base == revision_id:
198
 
            raise AssertionError()
199
 
        if not self._validated_revisions_against_repo:
200
 
            self._validate_references_from_repository(repository)
 
176
        assert base != revision_id
 
177
        self._validate_references_from_repository(repository)
201
178
        revision_info = self.get_revision_info(revision_id)
202
179
        inventory_revision_id = revision_id
203
 
        bundle_tree = BundleTree(repository.revision_tree(base),
 
180
        bundle_tree = BundleTree(repository.revision_tree(base), 
204
181
                                  inventory_revision_id)
205
182
        self._update_tree(bundle_tree, revision_id)
206
183
 
239
216
        for rev_info in self.revisions:
240
217
            checked[rev_info.revision_id] = True
241
218
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
242
 
 
 
219
                
243
220
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
244
221
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
245
222
 
247
224
        missing = {}
248
225
        for revision_id, sha1 in rev_to_sha.iteritems():
249
226
            if repository.has_revision(revision_id):
250
 
                testament = StrictTestament.from_revision(repository,
 
227
                testament = StrictTestament.from_revision(repository, 
251
228
                                                          revision_id)
252
229
                local_sha1 = self._testament_sha1_from_revision(repository,
253
230
                                                                revision_id)
254
231
                if sha1 != local_sha1:
255
 
                    raise BzrError('sha1 mismatch. For revision id {%s}'
 
232
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
256
233
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
257
234
                else:
258
235
                    count += 1
259
236
            elif revision_id not in checked:
260
237
                missing[revision_id] = sha1
261
238
 
 
239
        for inv_id, sha1 in inv_to_sha.iteritems():
 
240
            if repository.has_revision(inv_id):
 
241
                # Note: branch.get_inventory_sha1() just returns the value that
 
242
                # is stored in the revision text, and that value may be out
 
243
                # of date. This is bogus, because that means we aren't
 
244
                # validating the actual text, just that we wrote and read the
 
245
                # string. But for now, what the hell.
 
246
                local_sha1 = repository.get_inventory_sha1(inv_id)
 
247
                if sha1 != local_sha1:
 
248
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
 
249
                                   'local: %s, bundle: %s' % 
 
250
                                   (inv_id, local_sha1, sha1))
 
251
                else:
 
252
                    count += 1
 
253
 
262
254
        if len(missing) > 0:
263
255
            # I don't know if this is an error yet
264
256
            warning('Not all revision hashes could be validated.'
265
257
                    ' Unable validate %d hashes' % len(missing))
266
258
        mutter('Verified %d sha hashes for the bundle.' % count)
267
 
        self._validated_revisions_against_repo = True
268
259
 
269
260
    def _validate_inventory(self, inv, revision_id):
270
261
        """At this point we should have generated the BundleTree,
271
262
        so build up an inventory, and make sure the hashes match.
272
263
        """
 
264
 
 
265
        assert inv is not None
 
266
 
273
267
        # Now we should have a complete inventory entry.
274
268
        s = serializer_v5.write_inventory_to_string(inv)
275
269
        sha1 = sha_string(s)
276
270
        # Target revision is the last entry in the real_revisions list
277
271
        rev = self.get_revision(revision_id)
278
 
        if rev.revision_id != revision_id:
279
 
            raise AssertionError()
 
272
        assert rev.revision_id == revision_id
280
273
        if sha1 != rev.inventory_sha1:
281
274
            open(',,bogus-inv', 'wb').write(s)
282
275
            warning('Inventory sha hash mismatch for revision %s. %s'
287
280
 
288
281
        # This is a mapping from each revision id to it's sha hash
289
282
        rev_to_sha1 = {}
290
 
 
 
283
        
291
284
        rev = self.get_revision(revision_id)
292
285
        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()
 
286
        assert rev.revision_id == rev_info.revision_id
 
287
        assert rev.revision_id == revision_id
297
288
        sha1 = self._testament_sha1(rev, inventory)
298
289
        if sha1 != rev_info.sha1:
299
290
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
311
302
 
312
303
        def get_rev_id(last_changed, path, kind):
313
304
            if last_changed is not None:
314
 
                # last_changed will be a Unicode string because of how it was
315
 
                # read. Convert it back to utf8.
316
 
                changed_revision_id = osutils.safe_revision_id(last_changed,
317
 
                                                               warn=False)
 
305
                changed_revision_id = osutils.safe_revision_id(last_changed)
318
306
            else:
319
307
                changed_revision_id = revision_id
320
308
            bundle_tree.note_last_changed(path, changed_revision_id)
331
319
                if name == 'last-changed':
332
320
                    last_changed = value
333
321
                elif name == 'executable':
 
322
                    assert value in ('yes', 'no'), value
334
323
                    val = (value == 'yes')
335
324
                    bundle_tree.note_executable(new_path, val)
336
325
                elif name == 'target':
340
329
            return last_changed, encoding
341
330
 
342
331
        def do_patch(path, lines, encoding):
343
 
            if encoding == 'base64':
 
332
            if encoding is not None:
 
333
                assert encoding == 'base64'
344
334
                patch = base64.decodestring(''.join(lines))
345
 
            elif encoding is None:
 
335
            else:
346
336
                patch =  ''.join(lines)
347
 
            else:
348
 
                raise ValueError(encoding)
349
337
            bundle_tree.note_patch(path, patch)
350
338
 
351
339
        def renamed(kind, extra, lines):
389
377
                        ': %r' % extra)
390
378
            # This will be Unicode because of how the stream is read. Turn it
391
379
            # back into a utf8 file_id
392
 
            file_id = osutils.safe_file_id(info[1][8:], warn=False)
 
380
            file_id = osutils.safe_file_id(info[1][8:])
393
381
 
394
382
            bundle_tree.note_id(file_id, path, kind)
395
383
            # this will be overridden in extra_info if executable is specified.
411
399
            revision = get_rev_id(last_modified, path, kind)
412
400
            if lines:
413
401
                do_patch(path, lines, encoding)
414
 
 
 
402
            
415
403
        valid_actions = {
416
404
            'renamed':renamed,
417
405
            'removed':removed,
440
428
                        ' (unrecognized action): %r' % action_line)
441
429
            valid_actions[action](kind, extra, lines)
442
430
 
443
 
    def install_revisions(self, target_repo, stream_input=True):
444
 
        """Install revisions and return the target revision
445
 
 
446
 
        :param target_repo: The repository to install into
447
 
        :param stream_input: Ignored by this implementation.
448
 
        """
449
 
        apply_bundle.install_bundle(target_repo, self)
450
 
        return self.target
451
 
 
452
 
    def get_merge_request(self, target_repo):
453
 
        """Provide data for performing a merge
454
 
 
455
 
        Returns suggested base, suggested target, and patch verification status
456
 
        """
457
 
        return None, self.target, 'inapplicable'
458
 
 
459
431
 
460
432
class BundleTree(Tree):
461
433
    def __init__(self, base_tree, revision_id):
479
451
 
480
452
    def note_rename(self, old_path, new_path):
481
453
        """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)
 
454
        assert new_path not in self._renamed
 
455
        assert old_path not in self._renamed_r
486
456
        self._renamed[new_path] = old_path
487
457
        self._renamed_r[old_path] = new_path
488
458
 
518
488
 
519
489
    def old_path(self, new_path):
520
490
        """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)
 
491
        assert new_path[:1] not in ('\\', '/')
523
492
        old_path = self._renamed.get(new_path)
524
493
        if old_path is not None:
525
494
            return old_path
539
508
        #renamed_r
540
509
        if old_path in self._renamed_r:
541
510
            return None
542
 
        return old_path
 
511
        return old_path 
543
512
 
544
513
    def new_path(self, old_path):
545
514
        """Get the new_path (path in the target_tree) for the file at old_path
546
515
        in the base tree.
547
516
        """
548
 
        if old_path[:1] in ('\\', '/'):
549
 
            raise ValueError(old_path)
 
517
        assert old_path[:1] not in ('\\', '/')
550
518
        new_path = self._renamed_r.get(old_path)
551
519
        if new_path is not None:
552
520
            return new_path
565
533
        #renamed_r
566
534
        if new_path in self._renamed:
567
535
            return None
568
 
        return new_path
 
536
        return new_path 
569
537
 
570
538
    def path2id(self, path):
571
539
        """Return the id of the file present at path in the target tree."""
605
573
                return None
606
574
        new_path = self.id2path(file_id)
607
575
        return self.base_tree.path2id(new_path)
608
 
 
 
576
        
609
577
    def get_file(self, file_id):
610
578
        """Return a file-like object containing the new contents of the
611
579
        file given by file_id.
622
590
            patch_original = None
623
591
        file_patch = self.patches.get(self.id2path(file_id))
624
592
        if file_patch is None:
625
 
            if (patch_original is None and
 
593
            if (patch_original is None and 
626
594
                self.get_kind(file_id) == 'directory'):
627
595
                return StringIO()
628
 
            if patch_original is None:
629
 
                raise AssertionError("None: %s" % file_id)
 
596
            assert patch_original is not None, "None: %s" % file_id
630
597
            return patch_original
631
598
 
632
 
        if file_patch.startswith('\\'):
633
 
            raise ValueError(
634
 
                'Malformed patch for %s, %r' % (file_id, file_patch))
 
599
        assert not file_patch.startswith('\\'), \
 
600
            'Malformed patch for %s, %r' % (file_id, file_patch)
635
601
        return patched_file(file_patch, patch_original)
636
602
 
637
603
    def get_symlink_target(self, file_id):
684
650
        This need to be called before ever accessing self.inventory
685
651
        """
686
652
        from os.path import dirname, basename
 
653
 
 
654
        assert self.base_tree is not None
687
655
        base_inv = self.base_tree.inventory
688
656
        inv = Inventory(None, self.revision_id)
689
657