/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

(John Arbash Meinel) Fix bug #75721: 'bzr push' should only connect a single time.

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
28
from bzrlib.bundle import apply_bundle
30
 
from bzrlib.errors import (TestamentMismatch, BzrError,
 
29
from bzrlib.errors import (TestamentMismatch, BzrError, 
31
30
                           MalformedHeader, MalformedPatches, NotABundle)
32
31
from bzrlib.inventory import (Inventory, InventoryEntry,
33
32
                              InventoryDirectory, InventoryFile,
78
77
            for property in self.properties:
79
78
                key_end = property.find(': ')
80
79
                if key_end == -1:
81
 
                    if not property.endswith(':'):
82
 
                        raise ValueError(property)
 
80
                    assert property.endswith(':')
83
81
                    key = str(property[:-1])
84
82
                    value = ''
85
83
                else:
89
87
 
90
88
        return rev
91
89
 
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
90
 
106
91
class BundleInfo(object):
107
92
    """This contains the meta information. Stuff that allows you to
108
93
    recreate the revision or inventory XML.
109
94
    """
110
 
    def __init__(self, bundle_format=None):
111
 
        self.bundle_format = None
 
95
    def __init__(self):
112
96
        self.committer = None
113
97
        self.date = None
114
98
        self.message = None
125
109
        self.timestamp = None
126
110
        self.timezone = None
127
111
 
128
 
        # Have we checked the repository yet?
129
 
        self._validated_revisions_against_repo = False
130
 
 
131
112
    def __str__(self):
132
113
        return pprint.pformat(self.__dict__)
133
114
 
159
140
    def get_base(self, revision):
160
141
        revision_info = self.get_revision_info(revision.revision_id)
161
142
        if revision_info.base_id is not None:
162
 
            return revision_info.base_id
 
143
            if revision_info.base_id == NULL_REVISION:
 
144
                return None
 
145
            else:
 
146
                return revision_info.base_id
163
147
        if len(revision.parent_ids) == 0:
164
148
            # There is no base listed, and
165
149
            # the lowest revision doesn't have a parent
166
150
            # so this is probably against the empty tree
167
 
            # and thus base truly is NULL_REVISION
168
 
            return NULL_REVISION
 
151
            # and thus base truly is None
 
152
            return None
169
153
        else:
170
154
            return revision.parent_ids[-1]
171
155
 
192
176
        raise KeyError(revision_id)
193
177
 
194
178
    def revision_tree(self, repository, revision_id, base=None):
 
179
        revision_id = osutils.safe_revision_id(revision_id)
195
180
        revision = self.get_revision(revision_id)
196
181
        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)
 
182
        assert base != revision_id
 
183
        self._validate_references_from_repository(repository)
201
184
        revision_info = self.get_revision_info(revision_id)
202
185
        inventory_revision_id = revision_id
203
 
        bundle_tree = BundleTree(repository.revision_tree(base),
 
186
        bundle_tree = BundleTree(repository.revision_tree(base), 
204
187
                                  inventory_revision_id)
205
188
        self._update_tree(bundle_tree, revision_id)
206
189
 
239
222
        for rev_info in self.revisions:
240
223
            checked[rev_info.revision_id] = True
241
224
            add_sha(rev_to_sha, rev_info.revision_id, rev_info.sha1)
242
 
 
 
225
                
243
226
        for (rev, rev_info) in zip(self.real_revisions, self.revisions):
244
227
            add_sha(inv_to_sha, rev_info.revision_id, rev_info.inventory_sha1)
245
228
 
247
230
        missing = {}
248
231
        for revision_id, sha1 in rev_to_sha.iteritems():
249
232
            if repository.has_revision(revision_id):
250
 
                testament = StrictTestament.from_revision(repository,
 
233
                testament = StrictTestament.from_revision(repository, 
251
234
                                                          revision_id)
252
235
                local_sha1 = self._testament_sha1_from_revision(repository,
253
236
                                                                revision_id)
254
237
                if sha1 != local_sha1:
255
 
                    raise BzrError('sha1 mismatch. For revision id {%s}'
 
238
                    raise BzrError('sha1 mismatch. For revision id {%s}' 
256
239
                            'local: %s, bundle: %s' % (revision_id, local_sha1, sha1))
257
240
                else:
258
241
                    count += 1
259
242
            elif revision_id not in checked:
260
243
                missing[revision_id] = sha1
261
244
 
 
245
        for inv_id, sha1 in inv_to_sha.iteritems():
 
246
            if repository.has_revision(inv_id):
 
247
                # Note: branch.get_inventory_sha1() just returns the value that
 
248
                # is stored in the revision text, and that value may be out
 
249
                # of date. This is bogus, because that means we aren't
 
250
                # validating the actual text, just that we wrote and read the
 
251
                # string. But for now, what the hell.
 
252
                local_sha1 = repository.get_inventory_sha1(inv_id)
 
253
                if sha1 != local_sha1:
 
254
                    raise BzrError('sha1 mismatch. For inventory id {%s}' 
 
255
                                   'local: %s, bundle: %s' % 
 
256
                                   (inv_id, local_sha1, sha1))
 
257
                else:
 
258
                    count += 1
 
259
 
262
260
        if len(missing) > 0:
263
261
            # I don't know if this is an error yet
264
262
            warning('Not all revision hashes could be validated.'
265
263
                    ' Unable validate %d hashes' % len(missing))
266
264
        mutter('Verified %d sha hashes for the bundle.' % count)
267
 
        self._validated_revisions_against_repo = True
268
265
 
269
266
    def _validate_inventory(self, inv, revision_id):
270
267
        """At this point we should have generated the BundleTree,
271
268
        so build up an inventory, and make sure the hashes match.
272
269
        """
 
270
 
 
271
        assert inv is not None
 
272
 
273
273
        # Now we should have a complete inventory entry.
274
274
        s = serializer_v5.write_inventory_to_string(inv)
275
275
        sha1 = sha_string(s)
276
276
        # Target revision is the last entry in the real_revisions list
277
277
        rev = self.get_revision(revision_id)
278
 
        if rev.revision_id != revision_id:
279
 
            raise AssertionError()
 
278
        assert rev.revision_id == revision_id
280
279
        if sha1 != rev.inventory_sha1:
281
280
            open(',,bogus-inv', 'wb').write(s)
282
281
            warning('Inventory sha hash mismatch for revision %s. %s'
287
286
 
288
287
        # This is a mapping from each revision id to it's sha hash
289
288
        rev_to_sha1 = {}
290
 
 
 
289
        
291
290
        rev = self.get_revision(revision_id)
292
291
        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()
 
292
        assert rev.revision_id == rev_info.revision_id
 
293
        assert rev.revision_id == revision_id
297
294
        sha1 = self._testament_sha1(rev, inventory)
298
295
        if sha1 != rev_info.sha1:
299
296
            raise TestamentMismatch(rev.revision_id, rev_info.sha1, sha1)
331
328
                if name == 'last-changed':
332
329
                    last_changed = value
333
330
                elif name == 'executable':
 
331
                    assert value in ('yes', 'no'), value
334
332
                    val = (value == 'yes')
335
333
                    bundle_tree.note_executable(new_path, val)
336
334
                elif name == 'target':
340
338
            return last_changed, encoding
341
339
 
342
340
        def do_patch(path, lines, encoding):
343
 
            if encoding == 'base64':
 
341
            if encoding is not None:
 
342
                assert encoding == 'base64'
344
343
                patch = base64.decodestring(''.join(lines))
345
 
            elif encoding is None:
 
344
            else:
346
345
                patch =  ''.join(lines)
347
 
            else:
348
 
                raise ValueError(encoding)
349
346
            bundle_tree.note_patch(path, patch)
350
347
 
351
348
        def renamed(kind, extra, lines):
411
408
            revision = get_rev_id(last_modified, path, kind)
412
409
            if lines:
413
410
                do_patch(path, lines, encoding)
414
 
 
 
411
            
415
412
        valid_actions = {
416
413
            'renamed':renamed,
417
414
            'removed':removed,
440
437
                        ' (unrecognized action): %r' % action_line)
441
438
            valid_actions[action](kind, extra, lines)
442
439
 
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
 
        """
 
440
    def install_revisions(self, target_repo):
 
441
        """Install revisions and return the target revision"""
449
442
        apply_bundle.install_bundle(target_repo, self)
450
443
        return self.target
451
444
 
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
445
 
460
446
class BundleTree(Tree):
461
447
    def __init__(self, base_tree, revision_id):
479
465
 
480
466
    def note_rename(self, old_path, new_path):
481
467
        """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)
 
468
        assert new_path not in self._renamed
 
469
        assert old_path not in self._renamed_r
486
470
        self._renamed[new_path] = old_path
487
471
        self._renamed_r[old_path] = new_path
488
472
 
518
502
 
519
503
    def old_path(self, new_path):
520
504
        """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)
 
505
        assert new_path[:1] not in ('\\', '/')
523
506
        old_path = self._renamed.get(new_path)
524
507
        if old_path is not None:
525
508
            return old_path
539
522
        #renamed_r
540
523
        if old_path in self._renamed_r:
541
524
            return None
542
 
        return old_path
 
525
        return old_path 
543
526
 
544
527
    def new_path(self, old_path):
545
528
        """Get the new_path (path in the target_tree) for the file at old_path
546
529
        in the base tree.
547
530
        """
548
 
        if old_path[:1] in ('\\', '/'):
549
 
            raise ValueError(old_path)
 
531
        assert old_path[:1] not in ('\\', '/')
550
532
        new_path = self._renamed_r.get(old_path)
551
533
        if new_path is not None:
552
534
            return new_path
565
547
        #renamed_r
566
548
        if new_path in self._renamed:
567
549
            return None
568
 
        return new_path
 
550
        return new_path 
569
551
 
570
552
    def path2id(self, path):
571
553
        """Return the id of the file present at path in the target tree."""
605
587
                return None
606
588
        new_path = self.id2path(file_id)
607
589
        return self.base_tree.path2id(new_path)
608
 
 
 
590
        
609
591
    def get_file(self, file_id):
610
592
        """Return a file-like object containing the new contents of the
611
593
        file given by file_id.
622
604
            patch_original = None
623
605
        file_patch = self.patches.get(self.id2path(file_id))
624
606
        if file_patch is None:
625
 
            if (patch_original is None and
 
607
            if (patch_original is None and 
626
608
                self.get_kind(file_id) == 'directory'):
627
609
                return StringIO()
628
 
            if patch_original is None:
629
 
                raise AssertionError("None: %s" % file_id)
 
610
            assert patch_original is not None, "None: %s" % file_id
630
611
            return patch_original
631
612
 
632
 
        if file_patch.startswith('\\'):
633
 
            raise ValueError(
634
 
                'Malformed patch for %s, %r' % (file_id, file_patch))
 
613
        assert not file_patch.startswith('\\'), \
 
614
            'Malformed patch for %s, %r' % (file_id, file_patch)
635
615
        return patched_file(file_patch, patch_original)
636
616
 
637
617
    def get_symlink_target(self, file_id):
684
664
        This need to be called before ever accessing self.inventory
685
665
        """
686
666
        from os.path import dirname, basename
 
667
 
 
668
        assert self.base_tree is not None
687
669
        base_inv = self.base_tree.inventory
688
670
        inv = Inventory(None, self.revision_id)
689
671