~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weave.py

  • Committer: Aaron Bentley
  • Date: 2007-07-11 19:44:51 UTC
  • mto: This revision was merged to the branch mainline in revision 2606.
  • Revision ID: abentley@panoramicfeedback.com-20070711194451-3jqhye1nnd02a9uv
Restore original Branch.last_revision behavior, fix bits that care

Show diffs side-by-side

added added

removed removed

Lines of Context:
88
88
import bzrlib.errors as errors
89
89
from bzrlib.osutils import sha_strings
90
90
import bzrlib.patiencediff
 
91
from bzrlib.symbol_versioning import (deprecated_method,
 
92
        deprecated_function,
 
93
        zero_eight,
 
94
        )
91
95
from bzrlib.tsort import topo_sort
92
96
from bzrlib.versionedfile import VersionedFile, InterVersionedFile
93
97
from bzrlib.weavefile import _read_weave_v5, write_weave_v5
224
228
    def __ne__(self, other):
225
229
        return not self.__eq__(other)
226
230
 
 
231
    @deprecated_method(zero_eight)
 
232
    def idx_to_name(self, index):
 
233
        """Old public interface, the public interface is all names now."""
 
234
        return index
 
235
 
227
236
    def _idx_to_name(self, version):
228
237
        return self._names[version]
229
238
 
 
239
    @deprecated_method(zero_eight)
 
240
    def lookup(self, name):
 
241
        """Backwards compatibility thunk:
 
242
 
 
243
        Return name, as name is valid in the api now, and spew deprecation
 
244
        warnings everywhere.
 
245
        """
 
246
        return name
 
247
 
230
248
    def _lookup(self, name):
231
249
        """Convert symbolic version name to index."""
232
250
        self.check_not_reserved_id(name)
235
253
        except KeyError:
236
254
            raise RevisionNotPresent(name, self._weave_name)
237
255
 
 
256
    @deprecated_method(zero_eight)
 
257
    def iter_names(self):
 
258
        """Deprecated convenience function, please see VersionedFile.names()."""
 
259
        return iter(self.names())
 
260
 
 
261
    @deprecated_method(zero_eight)
 
262
    def names(self):
 
263
        """See Weave.versions for the current api."""
 
264
        return self.versions()
 
265
 
238
266
    def versions(self):
239
267
        """See VersionedFile.versions."""
240
268
        return self._names[:]
245
273
 
246
274
    __contains__ = has_version
247
275
 
 
276
    def get_delta(self, version_id):
 
277
        """See VersionedFile.get_delta."""
 
278
        return self.get_deltas([version_id])[version_id]
 
279
 
 
280
    def get_deltas(self, version_ids):
 
281
        """See VersionedFile.get_deltas."""
 
282
        version_ids = self.get_ancestry(version_ids)
 
283
        for version_id in version_ids:
 
284
            if not self.has_version(version_id):
 
285
                raise RevisionNotPresent(version_id, self)
 
286
        # try extracting all versions; parallel extraction is used
 
287
        nv = self.num_versions()
 
288
        sha1s = {}
 
289
        deltas = {}
 
290
        texts = {}
 
291
        inclusions = {}
 
292
        noeols = {}
 
293
        last_parent_lines = {}
 
294
        parents = {}
 
295
        parent_inclusions = {}
 
296
        parent_linenums = {}
 
297
        parent_noeols = {}
 
298
        current_hunks = {}
 
299
        diff_hunks = {}
 
300
        # its simplest to generate a full set of prepared variables.
 
301
        for i in range(nv):
 
302
            name = self._names[i]
 
303
            sha1s[name] = self.get_sha1(name)
 
304
            parents_list = self.get_parents(name)
 
305
            try:
 
306
                parent = parents_list[0]
 
307
                parents[name] = parent
 
308
                parent_inclusions[name] = inclusions[parent]
 
309
            except IndexError:
 
310
                parents[name] = None
 
311
                parent_inclusions[name] = set()
 
312
            # we want to emit start, finish, replacement_length, replacement_lines tuples.
 
313
            diff_hunks[name] = []
 
314
            current_hunks[name] = [0, 0, 0, []] # #start, finish, repl_length, repl_tuples
 
315
            parent_linenums[name] = 0
 
316
            noeols[name] = False
 
317
            parent_noeols[name] = False
 
318
            last_parent_lines[name] = None
 
319
            new_inc = set([name])
 
320
            for p in self._parents[i]:
 
321
                new_inc.update(inclusions[self._idx_to_name(p)])
 
322
            # debug only, known good so far.
 
323
            #assert set(new_inc) == set(self.get_ancestry(name)), \
 
324
            #    'failed %s != %s' % (set(new_inc), set(self.get_ancestry(name)))
 
325
            inclusions[name] = new_inc
 
326
 
 
327
        nlines = len(self._weave)
 
328
 
 
329
        for lineno, inserted, deletes, line in self._walk_internal():
 
330
            # a line is active in a version if:
 
331
            # insert is in the versions inclusions
 
332
            # and
 
333
            # deleteset & the versions inclusions is an empty set.
 
334
            # so - if we have a included by mapping - version is included by
 
335
            # children, we get a list of children to examine for deletes affect
 
336
            # ing them, which is less than the entire set of children.
 
337
            for version_id in version_ids:  
 
338
                # The active inclusion must be an ancestor,
 
339
                # and no ancestors must have deleted this line,
 
340
                # because we don't support resurrection.
 
341
                parent_inclusion = parent_inclusions[version_id]
 
342
                inclusion = inclusions[version_id]
 
343
                parent_active = inserted in parent_inclusion and not (deletes & parent_inclusion)
 
344
                version_active = inserted in inclusion and not (deletes & inclusion)
 
345
                if not parent_active and not version_active:
 
346
                    # unrelated line of ancestry
 
347
                    continue
 
348
                elif parent_active and version_active:
 
349
                    # shared line
 
350
                    parent_linenum = parent_linenums[version_id]
 
351
                    if current_hunks[version_id] != [parent_linenum, parent_linenum, 0, []]:
 
352
                        diff_hunks[version_id].append(tuple(current_hunks[version_id]))
 
353
                    parent_linenum += 1
 
354
                    current_hunks[version_id] = [parent_linenum, parent_linenum, 0, []]
 
355
                    parent_linenums[version_id] = parent_linenum
 
356
                    try:
 
357
                        if line[-1] != '\n':
 
358
                            noeols[version_id] = True
 
359
                    except IndexError:
 
360
                        pass
 
361
                elif parent_active and not version_active:
 
362
                    # deleted line
 
363
                    current_hunks[version_id][1] += 1
 
364
                    parent_linenums[version_id] += 1
 
365
                    last_parent_lines[version_id] = line
 
366
                elif not parent_active and version_active:
 
367
                    # replacement line
 
368
                    # noeol only occurs at the end of a file because we 
 
369
                    # diff linewise. We want to show noeol changes as a
 
370
                    # empty diff unless the actual eol-less content changed.
 
371
                    theline = line
 
372
                    try:
 
373
                        if last_parent_lines[version_id][-1] != '\n':
 
374
                            parent_noeols[version_id] = True
 
375
                    except (TypeError, IndexError):
 
376
                        pass
 
377
                    try:
 
378
                        if theline[-1] != '\n':
 
379
                            noeols[version_id] = True
 
380
                    except IndexError:
 
381
                        pass
 
382
                    new_line = False
 
383
                    parent_should_go = False
 
384
 
 
385
                    if parent_noeols[version_id] == noeols[version_id]:
 
386
                        # no noeol toggle, so trust the weaves statement
 
387
                        # that this line is changed.
 
388
                        new_line = True
 
389
                        if parent_noeols[version_id]:
 
390
                            theline = theline + '\n'
 
391
                    elif parent_noeols[version_id]:
 
392
                        # parent has no eol, we do:
 
393
                        # our line is new, report as such..
 
394
                        new_line = True
 
395
                    elif noeols[version_id]:
 
396
                        # append a eol so that it looks like
 
397
                        # a normalised delta
 
398
                        theline = theline + '\n'
 
399
                        if parents[version_id] is not None:
 
400
                        #if last_parent_lines[version_id] is not None:
 
401
                            parent_should_go = True
 
402
                        if last_parent_lines[version_id] != theline:
 
403
                            # but changed anyway
 
404
                            new_line = True
 
405
                            #parent_should_go = False
 
406
                    if new_line:
 
407
                        current_hunks[version_id][2] += 1
 
408
                        current_hunks[version_id][3].append((inserted, theline))
 
409
                    if parent_should_go:
 
410
                        # last hunk last parent line is not eaten
 
411
                        current_hunks[version_id][1] -= 1
 
412
                    if current_hunks[version_id][1] < 0:
 
413
                        current_hunks[version_id][1] = 0
 
414
                        # import pdb;pdb.set_trace()
 
415
                    # assert current_hunks[version_id][1] >= 0
 
416
 
 
417
        # flush last hunk
 
418
        for i in range(nv):
 
419
            version = self._idx_to_name(i)
 
420
            if current_hunks[version] != [0, 0, 0, []]:
 
421
                diff_hunks[version].append(tuple(current_hunks[version]))
 
422
        result = {}
 
423
        for version_id in version_ids:
 
424
            result[version_id] = (
 
425
                                  parents[version_id],
 
426
                                  sha1s[version_id],
 
427
                                  noeols[version_id],
 
428
                                  diff_hunks[version_id],
 
429
                                  )
 
430
        return result
 
431
 
248
432
    def get_parents(self, version_id):
249
433
        """See VersionedFile.get_parent."""
250
434
        return map(self._idx_to_name, self._parents[self._lookup(version_id)])
260
444
            raise RevisionAlreadyPresent(name, self._weave_name)
261
445
        return idx
262
446
 
263
 
    def _add_lines(self, version_id, parents, lines, parent_texts,
264
 
       left_matching_blocks, nostore_sha, random_id, check_content):
 
447
    @deprecated_method(zero_eight)
 
448
    def add_identical(self, old_rev_id, new_rev_id, parents):
 
449
        """Please use Weave.clone_text now."""
 
450
        return self.clone_text(new_rev_id, old_rev_id, parents)
 
451
 
 
452
    def _add_lines(self, version_id, parents, lines, parent_texts):
265
453
        """See VersionedFile.add_lines."""
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):
 
454
        return self._add(version_id, lines, map(self._lookup, parents))
 
455
 
 
456
    @deprecated_method(zero_eight)
 
457
    def add(self, name, parents, text, sha1=None):
 
458
        """See VersionedFile.add_lines for the non deprecated api."""
 
459
        return self._add(name, text, map(self._maybe_lookup, parents), sha1)
 
460
 
 
461
    def _add(self, version_id, lines, parents, sha1=None):
271
462
        """Add a single text on top of the weave.
272
463
  
273
464
        Returns the index number of the newly added version.
281
472
            
282
473
        lines
283
474
            Sequence of lines to be added in the new version.
 
475
        """
284
476
 
285
 
        :param nostore_sha: See VersionedFile.add_lines.
286
 
        """
287
477
        assert isinstance(version_id, basestring)
288
478
        self._check_lines_not_unicode(lines)
289
479
        self._check_lines_are_lines(lines)
290
480
        if not sha1:
291
481
            sha1 = sha_strings(lines)
292
 
        if sha1 == nostore_sha:
293
 
            raise errors.ExistingContent
294
482
        if version_id in self._name_map:
295
483
            return self._check_repeated_add(version_id, parents, lines, sha1)
296
484
 
340
528
        # another small special case: a merge, producing the same text
341
529
        # as auto-merge
342
530
        if lines == basis_lines:
343
 
            return new_version
 
531
            return new_version            
344
532
 
345
533
        # add a sentinel, because we can also match against the final line
346
534
        basis_lineno.append(len(self._weave))
409
597
        ## except IndexError:
410
598
        ##     raise ValueError("version %d not present in weave" % v)
411
599
 
 
600
    @deprecated_method(zero_eight)
 
601
    def inclusions(self, version_ids):
 
602
        """Deprecated - see VersionedFile.get_ancestry for the replacement."""
 
603
        if not version_ids:
 
604
            return []
 
605
        if isinstance(version_ids[0], int):
 
606
            return [self._idx_to_name(v) for v in self._inclusions(version_ids)]
 
607
        else:
 
608
            return self.get_ancestry(version_ids)
 
609
 
412
610
    def get_ancestry(self, version_ids, topo_sorted=True):
413
611
        """See VersionedFile.get_ancestry."""
414
612
        if isinstance(version_ids, basestring):
451
649
        for origin, lineno, text in self._extract(incls):
452
650
            yield self._idx_to_name(origin), text
453
651
 
 
652
    @deprecated_method(zero_eight)
 
653
    def _walk(self):
 
654
        """_walk has become visit, a supported api."""
 
655
        return self._walk_internal()
 
656
 
454
657
    def iter_lines_added_or_present_in_versions(self, version_ids=None,
455
658
                                                pb=None):
456
659
        """See VersionedFile.iter_lines_added_or_present_in_versions()."""
463
666
            # properly, we do not filter down to that
464
667
            # if inserted not in version_ids: continue
465
668
            if line[-1] != '\n':
466
 
                yield line + '\n', inserted
 
669
                yield line + '\n'
467
670
            else:
468
 
                yield line, inserted
 
671
                yield line
 
672
 
 
673
    #@deprecated_method(zero_eight)
 
674
    def walk(self, version_ids=None):
 
675
        """See VersionedFile.walk."""
 
676
        return self._walk_internal(version_ids)
469
677
 
470
678
    def _walk_internal(self, version_ids=None):
471
679
        """Helper method for weave actions."""
515
723
        inc_b = set(self.get_ancestry([ver_b]))
516
724
        inc_c = inc_a & inc_b
517
725
 
518
 
        for lineno, insert, deleteset, line in self._walk_internal([ver_a, ver_b]):
 
726
        for lineno, insert, deleteset, line in\
 
727
            self.walk([ver_a, ver_b]):
519
728
            if deleteset & inc_c:
520
729
                # killed in parent; can't be in either a or b
521
730
                # not relevant to our work
637
846
                                   % dset)
638
847
        return result
639
848
 
 
849
    @deprecated_method(zero_eight)
 
850
    def get_iter(self, name_or_index):
 
851
        """Deprecated, please do not use. Lookups are not not needed.
 
852
        
 
853
        Please use get_lines now.
 
854
        """
 
855
        return iter(self.get_lines(self._maybe_lookup(name_or_index)))
 
856
 
 
857
    @deprecated_method(zero_eight)
 
858
    def maybe_lookup(self, name_or_index):
 
859
        """Deprecated, please do not use. Lookups are not not needed."""
 
860
        return self._maybe_lookup(name_or_index)
 
861
 
640
862
    def _maybe_lookup(self, name_or_index):
641
863
        """Convert possible symbolic name to index, or pass through indexes.
642
864
        
647
869
        else:
648
870
            return self._lookup(name_or_index)
649
871
 
 
872
    @deprecated_method(zero_eight)
 
873
    def get(self, version_id):
 
874
        """Please use either Weave.get_text or Weave.get_lines as desired."""
 
875
        return self.get_lines(version_id)
 
876
 
650
877
    def get_lines(self, version_id):
651
878
        """See VersionedFile.get_lines()."""
652
879
        int_index = self._maybe_lookup(version_id)
664
891
        """See VersionedFile.get_sha1()."""
665
892
        return self._sha1s[self._lookup(version_id)]
666
893
 
667
 
    def get_sha1s(self, version_ids):
668
 
        """See VersionedFile.get_sha1s()."""
669
 
        return [self._sha1s[self._lookup(v)] for v in version_ids]
 
894
    @deprecated_method(zero_eight)
 
895
    def numversions(self):
 
896
        """How many versions are in this weave?
 
897
 
 
898
        Deprecated in favour of num_versions.
 
899
        """
 
900
        return self.num_versions()
670
901
 
671
902
    def num_versions(self):
672
903
        """How many versions are in this weave?"""
831
1062
        else:
832
1063
            return False
833
1064
 
 
1065
    @deprecated_method(zero_eight)
 
1066
    def reweave(self, other, pb=None, msg=None):
 
1067
        """reweave has been superseded by plain use of join."""
 
1068
        return self.join(other, pb, msg)
 
1069
 
834
1070
    def _reweave(self, other, pb, msg):
835
1071
        """Reweave self with other - internal helper for join().
836
1072
 
869
1105
            # new file, save it
870
1106
            self._save()
871
1107
 
872
 
    def _add_lines(self, version_id, parents, lines, parent_texts,
873
 
        left_matching_blocks, nostore_sha, random_id, check_content):
 
1108
    def _add_lines(self, version_id, parents, lines, parent_texts):
874
1109
        """Add a version and save the weave."""
875
1110
        self.check_not_reserved_id(version_id)
876
1111
        result = super(WeaveFile, self)._add_lines(version_id, parents, lines,
877
 
            parent_texts, left_matching_blocks, nostore_sha, random_id,
878
 
            check_content)
 
1112
                                                   parent_texts)
879
1113
        self._save()
880
1114
        return result
881
1115
 
917
1151
        self._save()
918
1152
 
919
1153
 
 
1154
@deprecated_function(zero_eight)
 
1155
def reweave(wa, wb, pb=None, msg=None):
 
1156
    """reweaving is deprecation, please just use weave.join()."""
 
1157
    _reweave(wa, wb, pb, msg)
 
1158
 
920
1159
def _reweave(wa, wb, pb=None, msg=None):
921
1160
    """Combine two weaves and return the result.
922
1161
 
1202
1441
        if self.target.versions() == [] and version_ids is None:
1203
1442
            self.target._copy_weave_content(self.source)
1204
1443
            return
1205
 
        self.target._join(self.source, pb, msg, version_ids, ignore_missing)
 
1444
        try:
 
1445
            self.target._join(self.source, pb, msg, version_ids, ignore_missing)
 
1446
        except errors.WeaveParentMismatch:
 
1447
            self.target._reweave(self.source, pb, msg)
1206
1448
 
1207
1449
 
1208
1450
InterVersionedFile.register_optimiser(InterWeave)