~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-10-24 12:49:17 UTC
  • mfrom: (2935.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20071024124917-xb75eckyxx6vkrlg
Makefile fixes - hooks.html generation & allow python to be overridden (Ian Clatworthy)

Show diffs side-by-side

added added

removed removed

Lines of Context:
75
75
import time
76
76
import warnings
77
77
 
 
78
from bzrlib import (
 
79
    progress,
 
80
    )
78
81
from bzrlib.trace import mutter
79
82
from bzrlib.errors import (WeaveError, WeaveFormatError, WeaveParentMismatch,
80
83
        RevisionAlreadyPresent,
85
88
import bzrlib.errors as errors
86
89
from bzrlib.osutils import sha_strings
87
90
import bzrlib.patiencediff
88
 
from bzrlib.symbol_versioning import (deprecated_method,
89
 
        deprecated_function,
90
 
        zero_eight,
91
 
        )
92
91
from bzrlib.tsort import topo_sort
93
92
from bzrlib.versionedfile import VersionedFile, InterVersionedFile
94
93
from bzrlib.weavefile import _read_weave_v5, write_weave_v5
225
224
    def __ne__(self, other):
226
225
        return not self.__eq__(other)
227
226
 
228
 
    @deprecated_method(zero_eight)
229
 
    def idx_to_name(self, index):
230
 
        """Old public interface, the public interface is all names now."""
231
 
        return index
232
 
 
233
227
    def _idx_to_name(self, version):
234
228
        return self._names[version]
235
229
 
236
 
    @deprecated_method(zero_eight)
237
 
    def lookup(self, name):
238
 
        """Backwards compatibility thunk:
239
 
 
240
 
        Return name, as name is valid in the api now, and spew deprecation
241
 
        warnings everywhere.
242
 
        """
243
 
        return name
244
 
 
245
230
    def _lookup(self, name):
246
231
        """Convert symbolic version name to index."""
 
232
        self.check_not_reserved_id(name)
247
233
        try:
248
234
            return self._name_map[name]
249
235
        except KeyError:
250
236
            raise RevisionNotPresent(name, self._weave_name)
251
237
 
252
 
    @deprecated_method(zero_eight)
253
 
    def iter_names(self):
254
 
        """Deprecated convenience function, please see VersionedFile.names()."""
255
 
        return iter(self.names())
256
 
 
257
 
    @deprecated_method(zero_eight)
258
 
    def names(self):
259
 
        """See Weave.versions for the current api."""
260
 
        return self.versions()
261
 
 
262
238
    def versions(self):
263
239
        """See VersionedFile.versions."""
264
240
        return self._names[:]
265
241
 
266
242
    def has_version(self, version_id):
267
243
        """See VersionedFile.has_version."""
268
 
        return self._name_map.has_key(version_id)
 
244
        return (version_id in self._name_map)
269
245
 
270
246
    __contains__ = has_version
271
247
 
272
 
    def get_delta(self, version_id):
273
 
        """See VersionedFile.get_delta."""
274
 
        return self.get_deltas([version_id])[version_id]
275
 
 
276
 
    def get_deltas(self, version_ids):
277
 
        """See VersionedFile.get_deltas."""
278
 
        version_ids = self.get_ancestry(version_ids)
279
 
        for version_id in version_ids:
280
 
            if not self.has_version(version_id):
281
 
                raise RevisionNotPresent(version_id, self)
282
 
        # try extracting all versions; parallel extraction is used
283
 
        nv = self.num_versions()
284
 
        sha1s = {}
285
 
        deltas = {}
286
 
        texts = {}
287
 
        inclusions = {}
288
 
        noeols = {}
289
 
        last_parent_lines = {}
290
 
        parents = {}
291
 
        parent_inclusions = {}
292
 
        parent_linenums = {}
293
 
        parent_noeols = {}
294
 
        current_hunks = {}
295
 
        diff_hunks = {}
296
 
        # its simplest to generate a full set of prepared variables.
297
 
        for i in range(nv):
298
 
            name = self._names[i]
299
 
            sha1s[name] = self.get_sha1(name)
300
 
            parents_list = self.get_parents(name)
301
 
            try:
302
 
                parent = parents_list[0]
303
 
                parents[name] = parent
304
 
                parent_inclusions[name] = inclusions[parent]
305
 
            except IndexError:
306
 
                parents[name] = None
307
 
                parent_inclusions[name] = set()
308
 
            # we want to emit start, finish, replacement_length, replacement_lines tuples.
309
 
            diff_hunks[name] = []
310
 
            current_hunks[name] = [0, 0, 0, []] # #start, finish, repl_length, repl_tuples
311
 
            parent_linenums[name] = 0
312
 
            noeols[name] = False
313
 
            parent_noeols[name] = False
314
 
            last_parent_lines[name] = None
315
 
            new_inc = set([name])
316
 
            for p in self._parents[i]:
317
 
                new_inc.update(inclusions[self._idx_to_name(p)])
318
 
            # debug only, known good so far.
319
 
            #assert set(new_inc) == set(self.get_ancestry(name)), \
320
 
            #    'failed %s != %s' % (set(new_inc), set(self.get_ancestry(name)))
321
 
            inclusions[name] = new_inc
322
 
 
323
 
        nlines = len(self._weave)
324
 
 
325
 
        for lineno, inserted, deletes, line in self._walk_internal():
326
 
            # a line is active in a version if:
327
 
            # insert is in the versions inclusions
328
 
            # and
329
 
            # deleteset & the versions inclusions is an empty set.
330
 
            # so - if we have a included by mapping - version is included by
331
 
            # children, we get a list of children to examine for deletes affect
332
 
            # ing them, which is less than the entire set of children.
333
 
            for version_id in version_ids:  
334
 
                # The active inclusion must be an ancestor,
335
 
                # and no ancestors must have deleted this line,
336
 
                # because we don't support resurrection.
337
 
                parent_inclusion = parent_inclusions[version_id]
338
 
                inclusion = inclusions[version_id]
339
 
                parent_active = inserted in parent_inclusion and not (deletes & parent_inclusion)
340
 
                version_active = inserted in inclusion and not (deletes & inclusion)
341
 
                if not parent_active and not version_active:
342
 
                    # unrelated line of ancestry
343
 
                    continue
344
 
                elif parent_active and version_active:
345
 
                    # shared line
346
 
                    parent_linenum = parent_linenums[version_id]
347
 
                    if current_hunks[version_id] != [parent_linenum, parent_linenum, 0, []]:
348
 
                        diff_hunks[version_id].append(tuple(current_hunks[version_id]))
349
 
                    parent_linenum += 1
350
 
                    current_hunks[version_id] = [parent_linenum, parent_linenum, 0, []]
351
 
                    parent_linenums[version_id] = parent_linenum
352
 
                    try:
353
 
                        if line[-1] != '\n':
354
 
                            noeols[version_id] = True
355
 
                    except IndexError:
356
 
                        pass
357
 
                elif parent_active and not version_active:
358
 
                    # deleted line
359
 
                    current_hunks[version_id][1] += 1
360
 
                    parent_linenums[version_id] += 1
361
 
                    last_parent_lines[version_id] = line
362
 
                elif not parent_active and version_active:
363
 
                    # replacement line
364
 
                    # noeol only occurs at the end of a file because we 
365
 
                    # diff linewise. We want to show noeol changes as a
366
 
                    # empty diff unless the actual eol-less content changed.
367
 
                    theline = line
368
 
                    try:
369
 
                        if last_parent_lines[version_id][-1] != '\n':
370
 
                            parent_noeols[version_id] = True
371
 
                    except (TypeError, IndexError):
372
 
                        pass
373
 
                    try:
374
 
                        if theline[-1] != '\n':
375
 
                            noeols[version_id] = True
376
 
                    except IndexError:
377
 
                        pass
378
 
                    new_line = False
379
 
                    parent_should_go = False
380
 
 
381
 
                    if parent_noeols[version_id] == noeols[version_id]:
382
 
                        # no noeol toggle, so trust the weaves statement
383
 
                        # that this line is changed.
384
 
                        new_line = True
385
 
                        if parent_noeols[version_id]:
386
 
                            theline = theline + '\n'
387
 
                    elif parent_noeols[version_id]:
388
 
                        # parent has no eol, we do:
389
 
                        # our line is new, report as such..
390
 
                        new_line = True
391
 
                    elif noeols[version_id]:
392
 
                        # append a eol so that it looks like
393
 
                        # a normalised delta
394
 
                        theline = theline + '\n'
395
 
                        if parents[version_id] is not None:
396
 
                        #if last_parent_lines[version_id] is not None:
397
 
                            parent_should_go = True
398
 
                        if last_parent_lines[version_id] != theline:
399
 
                            # but changed anyway
400
 
                            new_line = True
401
 
                            #parent_should_go = False
402
 
                    if new_line:
403
 
                        current_hunks[version_id][2] += 1
404
 
                        current_hunks[version_id][3].append((inserted, theline))
405
 
                    if parent_should_go:
406
 
                        # last hunk last parent line is not eaten
407
 
                        current_hunks[version_id][1] -= 1
408
 
                    if current_hunks[version_id][1] < 0:
409
 
                        current_hunks[version_id][1] = 0
410
 
                        # import pdb;pdb.set_trace()
411
 
                    # assert current_hunks[version_id][1] >= 0
412
 
 
413
 
        # flush last hunk
414
 
        for i in range(nv):
415
 
            version = self._idx_to_name(i)
416
 
            if current_hunks[version] != [0, 0, 0, []]:
417
 
                diff_hunks[version].append(tuple(current_hunks[version]))
418
 
        result = {}
419
 
        for version_id in version_ids:
420
 
            result[version_id] = (
421
 
                                  parents[version_id],
422
 
                                  sha1s[version_id],
423
 
                                  noeols[version_id],
424
 
                                  diff_hunks[version_id],
425
 
                                  )
426
 
        return result
427
 
 
428
248
    def get_parents(self, version_id):
429
249
        """See VersionedFile.get_parent."""
430
250
        return map(self._idx_to_name, self._parents[self._lookup(version_id)])
440
260
            raise RevisionAlreadyPresent(name, self._weave_name)
441
261
        return idx
442
262
 
443
 
    @deprecated_method(zero_eight)
444
 
    def add_identical(self, old_rev_id, new_rev_id, parents):
445
 
        """Please use Weave.clone_text now."""
446
 
        return self.clone_text(new_rev_id, old_rev_id, parents)
447
 
 
448
 
    def _add_lines(self, version_id, parents, lines, parent_texts):
 
263
    def _add_lines(self, version_id, parents, lines, parent_texts,
 
264
       left_matching_blocks, nostore_sha, random_id, check_content):
449
265
        """See VersionedFile.add_lines."""
450
 
        return self._add(version_id, lines, map(self._lookup, parents))
451
 
 
452
 
    @deprecated_method(zero_eight)
453
 
    def add(self, name, parents, text, sha1=None):
454
 
        """See VersionedFile.add_lines for the non deprecated api."""
455
 
        return self._add(name, text, map(self._maybe_lookup, parents), sha1)
456
 
 
457
 
    def _add(self, version_id, lines, parents, sha1=None):
 
266
        idx = self._add(version_id, lines, map(self._lookup, parents),
 
267
            nostore_sha=nostore_sha)
 
268
        return sha_strings(lines), sum(map(len, lines)), idx
 
269
 
 
270
    def _add(self, version_id, lines, parents, sha1=None, nostore_sha=None):
458
271
        """Add a single text on top of the weave.
459
272
  
460
273
        Returns the index number of the newly added version.
468
281
            
469
282
        lines
470
283
            Sequence of lines to be added in the new version.
 
284
 
 
285
        :param nostore_sha: See VersionedFile.add_lines.
471
286
        """
472
 
 
473
287
        assert isinstance(version_id, basestring)
474
288
        self._check_lines_not_unicode(lines)
475
289
        self._check_lines_are_lines(lines)
476
290
        if not sha1:
477
291
            sha1 = sha_strings(lines)
 
292
        if sha1 == nostore_sha:
 
293
            raise errors.ExistingContent
478
294
        if version_id in self._name_map:
479
295
            return self._check_repeated_add(version_id, parents, lines, sha1)
480
296
 
524
340
        # another small special case: a merge, producing the same text
525
341
        # as auto-merge
526
342
        if lines == basis_lines:
527
 
            return new_version            
 
343
            return new_version
528
344
 
529
345
        # add a sentinel, because we can also match against the final line
530
346
        basis_lineno.append(len(self._weave))
593
409
        ## except IndexError:
594
410
        ##     raise ValueError("version %d not present in weave" % v)
595
411
 
596
 
    @deprecated_method(zero_eight)
597
 
    def inclusions(self, version_ids):
598
 
        """Deprecated - see VersionedFile.get_ancestry for the replacement."""
599
 
        if not version_ids:
600
 
            return []
601
 
        if isinstance(version_ids[0], int):
602
 
            return [self._idx_to_name(v) for v in self._inclusions(version_ids)]
603
 
        else:
604
 
            return self.get_ancestry(version_ids)
605
 
 
606
 
    def get_ancestry(self, version_ids):
 
412
    def get_ancestry(self, version_ids, topo_sorted=True):
607
413
        """See VersionedFile.get_ancestry."""
608
414
        if isinstance(version_ids, basestring):
609
415
            version_ids = [version_ids]
637
443
        """
638
444
        return len(other_parents.difference(my_parents)) == 0
639
445
 
640
 
    def annotate(self, version_id):
641
 
        if isinstance(version_id, int):
642
 
            warnings.warn('Weave.annotate(int) is deprecated. Please use version names'
643
 
                 ' in all circumstances as of 0.8',
644
 
                 DeprecationWarning,
645
 
                 stacklevel=2
646
 
                 )
647
 
            result = []
648
 
            for origin, lineno, text in self._extract([version_id]):
649
 
                result.append((origin, text))
650
 
            return result
651
 
        else:
652
 
            return super(Weave, self).annotate(version_id)
653
 
    
654
446
    def annotate_iter(self, version_id):
655
447
        """Yield list of (version-id, line) pairs for the specified version.
656
448
 
659
451
        for origin, lineno, text in self._extract(incls):
660
452
            yield self._idx_to_name(origin), text
661
453
 
662
 
    @deprecated_method(zero_eight)
663
 
    def _walk(self):
664
 
        """_walk has become visit, a supported api."""
665
 
        return self._walk_internal()
666
 
 
667
 
    def iter_lines_added_or_present_in_versions(self, version_ids=None):
 
454
    def iter_lines_added_or_present_in_versions(self, version_ids=None,
 
455
                                                pb=None):
668
456
        """See VersionedFile.iter_lines_added_or_present_in_versions()."""
669
457
        if version_ids is None:
670
458
            version_ids = self.versions()
679
467
            else:
680
468
                yield line
681
469
 
682
 
    #@deprecated_method(zero_eight)
683
 
    def walk(self, version_ids=None):
684
 
        """See VersionedFile.walk."""
685
 
        return self._walk_internal(version_ids)
686
 
 
687
470
    def _walk_internal(self, version_ids=None):
688
471
        """Helper method for weave actions."""
689
472
        
732
515
        inc_b = set(self.get_ancestry([ver_b]))
733
516
        inc_c = inc_a & inc_b
734
517
 
735
 
        for lineno, insert, deleteset, line in\
736
 
            self.walk([ver_a, ver_b]):
 
518
        for lineno, insert, deleteset, line in self._walk_internal([ver_a, ver_b]):
737
519
            if deleteset & inc_c:
738
520
                # killed in parent; can't be in either a or b
739
521
                # not relevant to our work
855
637
                                   % dset)
856
638
        return result
857
639
 
858
 
    @deprecated_method(zero_eight)
859
 
    def get_iter(self, name_or_index):
860
 
        """Deprecated, please do not use. Lookups are not not needed.
861
 
        
862
 
        Please use get_lines now.
863
 
        """
864
 
        return iter(self.get_lines(self._maybe_lookup(name_or_index)))
865
 
 
866
 
    @deprecated_method(zero_eight)
867
 
    def maybe_lookup(self, name_or_index):
868
 
        """Deprecated, please do not use. Lookups are not not needed."""
869
 
        return self._maybe_lookup(name_or_index)
870
 
 
871
640
    def _maybe_lookup(self, name_or_index):
872
641
        """Convert possible symbolic name to index, or pass through indexes.
873
642
        
878
647
        else:
879
648
            return self._lookup(name_or_index)
880
649
 
881
 
    @deprecated_method(zero_eight)
882
 
    def get(self, version_id):
883
 
        """Please use either Weave.get_text or Weave.get_lines as desired."""
884
 
        return self.get_lines(version_id)
885
 
 
886
650
    def get_lines(self, version_id):
887
651
        """See VersionedFile.get_lines()."""
888
652
        int_index = self._maybe_lookup(version_id)
900
664
        """See VersionedFile.get_sha1()."""
901
665
        return self._sha1s[self._lookup(version_id)]
902
666
 
903
 
    @deprecated_method(zero_eight)
904
 
    def numversions(self):
905
 
        """How many versions are in this weave?
906
 
 
907
 
        Deprecated in favour of num_versions.
908
 
        """
909
 
        return self.num_versions()
 
667
    def get_sha1s(self, version_ids):
 
668
        """See VersionedFile.get_sha1s()."""
 
669
        return [self._sha1s[self._lookup(v)] for v in version_ids]
910
670
 
911
671
    def num_versions(self):
912
672
        """How many versions are in this weave?"""
1071
831
        else:
1072
832
            return False
1073
833
 
1074
 
    @deprecated_method(zero_eight)
1075
 
    def reweave(self, other, pb=None, msg=None):
1076
 
        """reweave has been superseded by plain use of join."""
1077
 
        return self.join(other, pb, msg)
1078
 
 
1079
834
    def _reweave(self, other, pb, msg):
1080
835
        """Reweave self with other - internal helper for join().
1081
836
 
1114
869
            # new file, save it
1115
870
            self._save()
1116
871
 
1117
 
    def _add_lines(self, version_id, parents, lines, parent_texts):
 
872
    def _add_lines(self, version_id, parents, lines, parent_texts,
 
873
        left_matching_blocks, nostore_sha, random_id, check_content):
1118
874
        """Add a version and save the weave."""
 
875
        self.check_not_reserved_id(version_id)
1119
876
        result = super(WeaveFile, self)._add_lines(version_id, parents, lines,
1120
 
                                                   parent_texts)
 
877
            parent_texts, left_matching_blocks, nostore_sha, random_id,
 
878
            check_content)
1121
879
        self._save()
1122
880
        return result
1123
881
 
1132
890
        sio = StringIO()
1133
891
        write_weave_v5(self, sio)
1134
892
        sio.seek(0)
1135
 
        transport.put(name + WeaveFile.WEAVE_SUFFIX, sio, self._filemode)
 
893
        transport.put_file(name + WeaveFile.WEAVE_SUFFIX, sio, self._filemode)
1136
894
 
1137
895
    def create_empty(self, name, transport, filemode=None):
1138
896
        return WeaveFile(name, transport, filemode, create=True)
1143
901
        sio = StringIO()
1144
902
        write_weave_v5(self, sio)
1145
903
        sio.seek(0)
1146
 
        self._transport.put(self._weave_name + WeaveFile.WEAVE_SUFFIX,
1147
 
                            sio,
1148
 
                            self._filemode)
 
904
        self._transport.put_file(self._weave_name + WeaveFile.WEAVE_SUFFIX,
 
905
                                 sio,
 
906
                                 self._filemode)
1149
907
 
1150
908
    @staticmethod
1151
909
    def get_suffixes():
1159
917
        self._save()
1160
918
 
1161
919
 
1162
 
@deprecated_function(zero_eight)
1163
 
def reweave(wa, wb, pb=None, msg=None):
1164
 
    """reweaving is deprecation, please just use weave.join()."""
1165
 
    _reweave(wa, wb, pb, msg)
1166
 
 
1167
920
def _reweave(wa, wb, pb=None, msg=None):
1168
921
    """Combine two weaves and return the result.
1169
922
 
1449
1202
        if self.target.versions() == [] and version_ids is None:
1450
1203
            self.target._copy_weave_content(self.source)
1451
1204
            return
1452
 
        try:
1453
 
            self.target._join(self.source, pb, msg, version_ids, ignore_missing)
1454
 
        except errors.WeaveParentMismatch:
1455
 
            self.target._reweave(self.source, pb, msg)
 
1205
        self.target._join(self.source, pb, msg, version_ids, ignore_missing)
1456
1206
 
1457
1207
 
1458
1208
InterVersionedFile.register_optimiser(InterWeave)