~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Aaron Bentley
  • Date: 2007-12-26 21:13:53 UTC
  • mto: This revision was merged to the branch mainline in revision 3145.
  • Revision ID: aaron.bentley@utoronto.ca-20071226211353-zum7avm4gfgyfwam
Implement diff --using natively

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
import difflib
18
18
import os
19
19
import re
 
20
import shutil
20
21
import sys
21
22
 
22
23
from bzrlib.lazy_import import lazy_import
28
29
 
29
30
from bzrlib import (
30
31
    bzrdir,
 
32
    commands,
31
33
    errors,
32
34
    osutils,
33
35
    patiencediff,
467
469
                    external_diff_options=None,
468
470
                    old_label='a/', new_label='b/',
469
471
                    extra_trees=None,
470
 
                    path_encoding='utf8'):
 
472
                    path_encoding='utf8',
 
473
                    using=None):
471
474
    """Show in text form the changes from one tree to another.
472
475
 
473
476
    to_file
494
497
        new_tree.lock_read()
495
498
        try:
496
499
            differ = DiffTree.from_trees_options(old_tree, new_tree, to_file,
497
 
                                                   path_encoding,
498
 
                                                   external_diff_options,
499
 
                                                   old_label, new_label)
 
500
                                                 path_encoding,
 
501
                                                 external_diff_options,
 
502
                                                 old_label, new_label, using)
500
503
            return differ.show_diff(specific_files, extra_trees)
501
504
        finally:
502
505
            new_tree.unlock()
565
568
        self.to_file = to_file
566
569
        self.path_encoding = path_encoding
567
570
 
 
571
    def finish(self):
 
572
        pass
 
573
 
568
574
    @classmethod
569
575
    def from_diff_tree(klass, diff_tree):
570
576
        return klass(diff_tree.old_tree, diff_tree.new_tree,
590
596
    def __init__(self, differs):
591
597
        self.differs = differs
592
598
 
 
599
    def finish(self):
 
600
        pass
 
601
 
593
602
    @classmethod
594
603
    def from_diff_tree(klass, diff_tree):
595
604
        return klass(diff_tree.differs)
736
745
        return self.CHANGED
737
746
 
738
747
 
 
748
class DiffFromTool(DiffPath):
 
749
 
 
750
    def __init__(self, command_template, old_tree, new_tree, to_file,
 
751
                 path_encoding='utf-8'):
 
752
        DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
 
753
        self.command_template = command_template
 
754
        self._root = tempfile.mkdtemp(prefix='bzr-diff-')
 
755
 
 
756
    @classmethod
 
757
    def from_string(klass, command_string, old_tree, new_tree, to_file,
 
758
                    path_encoding='utf-8'):
 
759
        command_template = commands.shlex_split_unicode(command_string)
 
760
        command_template.extend(['%(old_path)s', '%(new_path)s'])
 
761
        return klass(command_template, old_tree, new_tree, to_file,
 
762
                     path_encoding)
 
763
 
 
764
    @classmethod
 
765
    def make_from_diff_tree(klass, command_string):
 
766
        def from_diff_tree(diff_tree):
 
767
            return klass.from_string(command_string, diff_tree.old_tree,
 
768
                                     diff_tree.new_tree, diff_tree.to_file)
 
769
        return from_diff_tree
 
770
 
 
771
    def _get_command(self, old_path, new_path):
 
772
        my_map = {'old_path': old_path, 'new_path': new_path}
 
773
        return [t % my_map for t in self.command_template]
 
774
 
 
775
    def _execute(self, old_path, new_path):
 
776
        proc = subprocess.Popen(self._get_command(old_path, new_path),
 
777
                                stdout=subprocess.PIPE, cwd=self._root)
 
778
        self.to_file.write(proc.stdout.read())
 
779
        return proc.wait()
 
780
 
 
781
    def _write_file(self, file_id, tree, prefix, old_path):
 
782
        full_old_path = osutils.pathjoin(self._root, prefix, old_path)
 
783
        parent_dir = osutils.dirname(full_old_path)
 
784
        try:
 
785
            os.makedirs(parent_dir)
 
786
        except OSError, e:
 
787
            if e.errno != errno.EEXIST:
 
788
                raise
 
789
        source = tree.get_file(file_id)
 
790
        try:
 
791
            target = open(full_old_path, 'wb')
 
792
            try:
 
793
                osutils.pumpfile(source, target)
 
794
            finally:
 
795
                target.close()
 
796
        finally:
 
797
            source.close()
 
798
        return full_old_path
 
799
 
 
800
    def _prepare_files(self, file_id, old_path, new_path):
 
801
        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
 
802
                                         old_path)
 
803
        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
 
804
                                         new_path)
 
805
        return old_disk_path, new_disk_path
 
806
 
 
807
    def finish(self):
 
808
        shutil.rmtree(self._root)
 
809
 
 
810
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
 
811
        if (old_kind, new_kind) != ('file', 'file'):
 
812
            return DiffPath.CANNOT_DIFF
 
813
        self._prepare_files(file_id, old_path, new_path)
 
814
        self._execute(osutils.pathjoin('old', old_path),
 
815
                      osutils.pathjoin('new', new_path))
 
816
 
 
817
 
739
818
class DiffTree(object):
740
819
    """Provides textual representations of the difference between two trees.
741
820
 
781
860
    @classmethod
782
861
    def from_trees_options(klass, old_tree, new_tree, to_file,
783
862
                           path_encoding, external_diff_options, old_label,
784
 
                           new_label):
 
863
                           new_label, using):
785
864
        """Factory for producing a DiffTree.
786
865
 
787
866
        Designed to accept options used by show_diff_trees.
793
872
            binary to perform file comparison, using supplied options.
794
873
        :param old_label: Prefix to use for old file labels
795
874
        :param new_label: Prefix to use for new file labels
 
875
        :param using: Commandline to use to invoke an external diff tool
796
876
        """
 
877
        if using is not None:
 
878
            extra_factories = [DiffFromTool.make_from_diff_tree(using)]
 
879
        else:
 
880
            extra_factories = []
797
881
        if external_diff_options:
798
882
            assert isinstance(external_diff_options, basestring)
799
883
            opts = external_diff_options.split()
803
887
            diff_file = internal_diff
804
888
        diff_text = DiffText(old_tree, new_tree, to_file, path_encoding,
805
889
                             old_label, new_label, diff_file)
806
 
        return klass(old_tree, new_tree, to_file, path_encoding, diff_text)
 
890
        return klass(old_tree, new_tree, to_file, path_encoding, diff_text,
 
891
                     extra_factories)
807
892
 
808
893
    def show_diff(self, specific_files, extra_trees=None):
809
894
        """Write tree diff to self.to_file
811
896
        :param sepecific_files: the specific files to compare (recursive)
812
897
        :param extra_trees: extra trees to use for mapping paths to file_ids
813
898
        """
 
899
        try:
 
900
            return self._show_diff(specific_files, extra_trees)
 
901
        finally:
 
902
            for differ in self.differs:
 
903
                differ.finish()
 
904
 
 
905
    def _show_diff(self, specific_files, extra_trees):
814
906
        # TODO: Generation of pseudo-diffs for added/deleted files could
815
907
        # be usefully made into a much faster special case.
816
908
        iterator = self.new_tree._iter_changes(self.old_tree,