~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Aaron Bentley
  • Date: 2007-11-21 15:27:30 UTC
  • mto: (3008.1.2 transform_preview)
  • mto: This revision was merged to the branch mainline in revision 3036.
  • Revision ID: abentley@panoramicfeedback.com-20071121152730-ttbjijxcc88131zp
Convert show_diff_trees into a Differ method

Show diffs side-by-side

added added

removed removed

Lines of Context:
371
371
                tree.lock_read()
372
372
        new_tree.lock_read()
373
373
        try:
374
 
            return _show_diff_trees(old_tree, new_tree, to_file,
375
 
                                    specific_files, external_diff_options,
376
 
                                    old_label=old_label, new_label=new_label,
377
 
                                    extra_trees=extra_trees,
378
 
                                    path_encoding=path_encoding)
 
374
            differ = Differ.from_trees_options(old_tree, new_tree, to_file,
 
375
                                               external_diff_options,
 
376
                                               path_encoding)
 
377
            return differ.show_diff(specific_files, old_label, new_label,
 
378
                                    extra_trees)
379
379
        finally:
380
380
            new_tree.unlock()
381
381
            if extra_trees is not None:
385
385
        old_tree.unlock()
386
386
 
387
387
 
388
 
def _show_diff_trees(old_tree, new_tree, to_file,
389
 
                     specific_files, external_diff_options, path_encoding,
390
 
                     old_label='a/', new_label='b/', extra_trees=None):
391
 
 
392
 
    # GNU Patch uses the epoch date to detect files that are being added
393
 
    # or removed in a diff.
394
 
    EPOCH_DATE = '1970-01-01 00:00:00 +0000'
395
 
 
396
 
    # TODO: Generation of pseudo-diffs for added/deleted files could
397
 
    # be usefully made into a much faster special case.
398
 
 
399
 
    if external_diff_options:
400
 
        assert isinstance(external_diff_options, basestring)
401
 
        opts = external_diff_options.split()
402
 
        def diff_file(olab, olines, nlab, nlines, to_file):
403
 
            external_diff(olab, olines, nlab, nlines, to_file, opts)
404
 
    else:
405
 
        diff_file = internal_diff
406
 
    
407
 
    delta = new_tree.changes_from(old_tree,
408
 
        specific_files=specific_files,
409
 
        extra_trees=extra_trees, require_versioned=True)
410
 
 
411
 
    has_changes = 0
412
 
    differ = Differ(old_tree, new_tree, to_file, diff_file)
413
 
    for path, file_id, kind in delta.removed:
414
 
        has_changes = 1
415
 
        path_encoded = path.encode(path_encoding, "replace")
416
 
        to_file.write("=== removed %s '%s'\n" % (kind, path_encoded))
417
 
        old_name = '%s%s\t%s' % (old_label, path,
418
 
                                 _patch_header_date(old_tree, file_id, path))
419
 
        new_name = '%s%s\t%s' % (new_label, path, EPOCH_DATE)
420
 
        differ.diff(file_id, old_name, new_name)
421
 
    for path, file_id, kind in delta.added:
422
 
        has_changes = 1
423
 
        path_encoded = path.encode(path_encoding, "replace")
424
 
        to_file.write("=== added %s '%s'\n" % (kind, path_encoded))
425
 
        old_name = '%s%s\t%s' % (old_label, path, EPOCH_DATE)
426
 
        new_name = '%s%s\t%s' % (new_label, path,
427
 
                                 _patch_header_date(new_tree, file_id, path))
428
 
        differ.diff(file_id, old_name, new_name)
429
 
    for (old_path, new_path, file_id, kind,
430
 
         text_modified, meta_modified) in delta.renamed:
431
 
        has_changes = 1
432
 
        prop_str = get_prop_change(meta_modified)
433
 
        oldpath_encoded = old_path.encode(path_encoding, "replace")
434
 
        newpath_encoded = new_path.encode(path_encoding, "replace")
435
 
        to_file.write("=== renamed %s '%s' => '%s'%s\n" % (kind,
436
 
                            oldpath_encoded, newpath_encoded, prop_str))
437
 
        old_name = '%s%s\t%s' % (old_label, old_path,
438
 
                                 _patch_header_date(old_tree, file_id,
439
 
                                                    old_path))
440
 
        new_name = '%s%s\t%s' % (new_label, new_path,
441
 
                                 _patch_header_date(new_tree, file_id,
442
 
                                                    new_path))
443
 
        _maybe_diff_file_or_symlink(old_name, old_tree, file_id,
444
 
                                    new_name, new_tree,
445
 
                                    text_modified, differ)
446
 
    for path, file_id, kind, text_modified, meta_modified in delta.modified:
447
 
        has_changes = 1
448
 
        prop_str = get_prop_change(meta_modified)
449
 
        path_encoded = path.encode(path_encoding, "replace")
450
 
        to_file.write("=== modified %s '%s'%s\n" % (kind,
451
 
                            path_encoded, prop_str))
452
 
        # The file may be in a different location in the old tree (because
453
 
        # the containing dir was renamed, but the file itself was not)
454
 
        old_path = old_tree.id2path(file_id)
455
 
        old_name = '%s%s\t%s' % (old_label, old_path,
456
 
                                 _patch_header_date(old_tree, file_id, old_path))
457
 
        new_name = '%s%s\t%s' % (new_label, path,
458
 
                                 _patch_header_date(new_tree, file_id, path))
459
 
        if text_modified:
460
 
            _maybe_diff_file_or_symlink(old_name, old_tree, file_id, new_name,
461
 
                                        new_tree, True, differ)
462
 
 
463
 
    return has_changes
464
388
 
465
389
 
466
390
def _patch_header_date(tree, file_id, path):
506
430
 
507
431
class Differ(object):
508
432
 
509
 
    def __init__(self, old_tree, new_tree, to_file, text_diff):
 
433
    def __init__(self, old_tree, new_tree, to_file, text_diff,
 
434
                 path_encoding='utf-8'):
510
435
        self.old_tree = old_tree
511
436
        self.new_tree = new_tree
512
437
        self.to_file = to_file
513
438
        self.text_diff = text_diff
 
439
        self.path_encoding = path_encoding
 
440
 
 
441
    @classmethod
 
442
    def from_trees_options(klass, old_tree, new_tree, to_file,
 
443
                           external_diff_options, path_encoding):
 
444
        if external_diff_options:
 
445
            assert isinstance(external_diff_options, basestring)
 
446
            opts = external_diff_options.split()
 
447
            def diff_file(olab, olines, nlab, nlines, to_file):
 
448
                external_diff(olab, olines, nlab, nlines, to_file, opts)
 
449
        else:
 
450
            diff_file = internal_diff
 
451
        return klass(old_tree, new_tree, to_file, diff_file, path_encoding)
 
452
 
 
453
    def show_diff(self, specific_files, old_label='', new_label='',
 
454
                  extra_trees=None):
 
455
        # GNU Patch uses the epoch date to detect files that are being added
 
456
        # or removed in a diff.
 
457
        EPOCH_DATE = '1970-01-01 00:00:00 +0000'
 
458
 
 
459
        # TODO: Generation of pseudo-diffs for added/deleted files could
 
460
        # be usefully made into a much faster special case.
 
461
 
 
462
        delta = self.new_tree.changes_from(self.old_tree,
 
463
            specific_files=specific_files,
 
464
            extra_trees=extra_trees, require_versioned=True)
 
465
 
 
466
        has_changes = 0
 
467
        for path, file_id, kind in delta.removed:
 
468
            has_changes = 1
 
469
            path_encoded = path.encode(self.path_encoding, "replace")
 
470
            self.to_file.write("=== removed %s '%s'\n" % (kind, path_encoded))
 
471
            old_name = '%s%s\t%s' % (old_label, path,
 
472
                                     _patch_header_date(self.old_tree, file_id,
 
473
                                     path))
 
474
            new_name = '%s%s\t%s' % (new_label, path, EPOCH_DATE)
 
475
            self.diff(file_id, old_name, new_name)
 
476
 
 
477
        for path, file_id, kind in delta.added:
 
478
            has_changes = 1
 
479
            path_encoded = path.encode(self.path_encoding, "replace")
 
480
            self.to_file.write("=== added %s '%s'\n" % (kind, path_encoded))
 
481
            old_name = '%s%s\t%s' % (old_label, path, EPOCH_DATE)
 
482
            new_name = '%s%s\t%s' % (new_label, path,
 
483
                                     _patch_header_date(self.new_tree, file_id,
 
484
                                                        path))
 
485
            self.diff(file_id, old_name, new_name)
 
486
        for (old_path, new_path, file_id, kind,
 
487
             text_modified, meta_modified) in delta.renamed:
 
488
            has_changes = 1
 
489
            prop_str = get_prop_change(meta_modified)
 
490
            oldpath_encoded = old_path.encode(self.path_encoding, "replace")
 
491
            newpath_encoded = new_path.encode(self.path_encoding, "replace")
 
492
            self.to_file.write("=== renamed %s '%s' => '%s'%s\n" % (kind,
 
493
                                oldpath_encoded, newpath_encoded, prop_str))
 
494
            old_name = '%s%s\t%s' % (old_label, old_path,
 
495
                                     _patch_header_date(self.old_tree, file_id,
 
496
                                                        old_path))
 
497
            new_name = '%s%s\t%s' % (new_label, new_path,
 
498
                                     _patch_header_date(self.new_tree, file_id,
 
499
                                                        new_path))
 
500
            _maybe_diff_file_or_symlink(old_name, self.old_tree, file_id,
 
501
                                        new_name, self.new_tree,
 
502
                                        text_modified, self)
 
503
        for path, file_id, kind, text_modified, meta_modified in\
 
504
            delta.modified:
 
505
            has_changes = 1
 
506
            prop_str = get_prop_change(meta_modified)
 
507
            path_encoded = path.encode(self.path_encoding, "replace")
 
508
            self.to_file.write("=== modified %s '%s'%s\n" % (kind,
 
509
                                path_encoded, prop_str))
 
510
            # The file may be in a different location in the old tree (because
 
511
            # the containing dir was renamed, but the file itself was not)
 
512
            old_path = self.old_tree.id2path(file_id)
 
513
            old_date = _patch_header_date(self.old_tree, file_id, old_path)
 
514
            old_name = '%s%s\t%s' % (old_label, old_path, old_date)
 
515
            new_date = _patch_header_date(self.new_tree, file_id, path)
 
516
            new_name = '%s%s\t%s' % (new_label, path, new_date)
 
517
            if text_modified:
 
518
                _maybe_diff_file_or_symlink(old_name, self.old_tree, file_id,
 
519
                                            new_name, self.new_tree, True,
 
520
                                            self)
 
521
        return has_changes
514
522
 
515
523
    def diff(self, file_id, old_name, new_name):
516
524
        try: