~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: John Arbash Meinel
  • Date: 2009-03-06 20:42:40 UTC
  • mto: This revision was merged to the branch mainline in revision 4088.
  • Revision ID: john@arbash-meinel.com-20090306204240-mzjavv31z3gu1x7i
Fix a small bug in setup.py when an extension fails to build

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
import difflib
18
18
import os
19
19
import re
20
20
import shutil
21
 
import string
22
21
import sys
23
22
 
24
23
from bzrlib.lazy_import import lazy_import
39
38
    timestamp,
40
39
    views,
41
40
    )
42
 
 
43
 
from bzrlib.workingtree import WorkingTree
44
41
""")
45
42
 
46
43
from bzrlib.symbol_versioning import (
47
 
    deprecated_function,
48
 
    )
 
44
        deprecated_function,
 
45
        one_three
 
46
        )
49
47
from bzrlib.trace import mutter, note, warning
50
48
 
51
49
 
52
 
class AtTemplate(string.Template):
53
 
    """Templating class that uses @ instead of $."""
54
 
 
55
 
    delimiter = '@'
56
 
 
57
 
 
58
50
# TODO: Rather than building a changeset object, we should probably
59
51
# invoke callbacks on an object.  That object can either accumulate a
60
52
# list, write them out directly, etc etc.
180
172
 
181
173
        if not diff_opts:
182
174
            diff_opts = []
183
 
        if sys.platform == 'win32':
184
 
            # Popen doesn't do the proper encoding for external commands
185
 
            # Since we are dealing with an ANSI api, use mbcs encoding
186
 
            old_filename = old_filename.encode('mbcs')
187
 
            new_filename = new_filename.encode('mbcs')
188
175
        diffcmd = ['diff',
189
176
                   '--label', old_filename,
190
177
                   old_abspath,
286
273
                        new_abspath, e)
287
274
 
288
275
 
289
 
def get_trees_and_branches_to_diff(path_list, revision_specs, old_url, new_url,
290
 
                                   apply_view=True):
 
276
def _get_trees_to_diff(path_list, revision_specs, old_url, new_url,
 
277
    apply_view=True):
291
278
    """Get the trees and specific files to diff given a list of paths.
292
279
 
293
280
    This method works out the trees to be diff'ed and the files of
308
295
        if True and a view is set, apply the view or check that the paths
309
296
        are within it
310
297
    :returns:
311
 
        a tuple of (old_tree, new_tree, old_branch, new_branch,
312
 
        specific_files, extra_trees) where extra_trees is a sequence of
313
 
        additional trees to search in for file-ids.
 
298
        a tuple of (old_tree, new_tree, specific_files, extra_trees) where
 
299
        extra_trees is a sequence of additional trees to search in for
 
300
        file-ids.
314
301
    """
315
302
    # Get the old and new revision specs
316
303
    old_revision_spec = None
350
337
            views.check_path_in_view(working_tree, relpath)
351
338
        specific_files.append(relpath)
352
339
    old_tree = _get_tree_to_diff(old_revision_spec, working_tree, branch)
353
 
    old_branch = branch
354
340
 
355
341
    # Get the new location
356
342
    if new_url is None:
364
350
            specific_files.append(relpath)
365
351
    new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
366
352
        basis_is_default=working_tree is None)
367
 
    new_branch = branch
368
353
 
369
354
    # Get the specific files (all files is None, no files is [])
370
355
    if make_paths_wt_relative and working_tree is not None:
383
368
            if view_files:
384
369
                specific_files = view_files
385
370
                view_str = views.view_display_str(view_files)
386
 
                note("*** Ignoring files outside view. View is %s" % view_str)
 
371
                note("*** ignoring files outside view: %s" % view_str)
387
372
 
388
373
    # Get extra trees that ought to be searched for file-ids
389
374
    extra_trees = None
390
375
    if working_tree is not None and working_tree not in (old_tree, new_tree):
391
376
        extra_trees = (working_tree,)
392
 
    return old_tree, new_tree, old_branch, new_branch, specific_files, extra_trees
393
 
 
 
377
    return old_tree, new_tree, specific_files, extra_trees
394
378
 
395
379
def _get_tree_to_diff(spec, tree=None, branch=None, basis_is_default=True):
396
380
    if branch is None and tree is not None:
453
437
 
454
438
def _patch_header_date(tree, file_id, path):
455
439
    """Returns a timestamp suitable for use in a patch header."""
456
 
    try:
457
 
        mtime = tree.get_file_mtime(file_id, path)
458
 
    except errors.FileTimestampUnavailable:
459
 
        mtime = 0
 
440
    mtime = tree.get_file_mtime(file_id, path)
460
441
    return timestamp.format_patch_date(mtime)
461
442
 
462
443
 
 
444
@deprecated_function(one_three)
 
445
def get_prop_change(meta_modified):
 
446
    if meta_modified:
 
447
        return " (properties changed)"
 
448
    else:
 
449
        return  ""
 
450
 
463
451
def get_executable_change(old_is_x, new_is_x):
464
452
    descr = { True:"+x", False:"-x", None:"??" }
465
453
    if old_is_x != new_is_x:
640
628
            return self.CANNOT_DIFF
641
629
        from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
642
630
        to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
643
 
        return self.diff_text(from_file_id, to_file_id, from_label, to_label,
644
 
            old_path, new_path)
 
631
        return self.diff_text(from_file_id, to_file_id, from_label, to_label)
645
632
 
646
 
    def diff_text(self, from_file_id, to_file_id, from_label, to_label,
647
 
        from_path=None, to_path=None):
 
633
    def diff_text(self, from_file_id, to_file_id, from_label, to_label):
648
634
        """Diff the content of given files in two trees
649
635
 
650
636
        :param from_file_id: The id of the file in the from tree.  If None,
652
638
        :param to_file_id: The id of the file in the to tree.  This may refer
653
639
            to a different file from from_file_id.  If None,
654
640
            the file is not present in the to tree.
655
 
        :param from_path: The path in the from tree or None if unknown.
656
 
        :param to_path: The path in the to tree or None if unknown.
657
641
        """
658
 
        def _get_text(tree, file_id, path):
 
642
        def _get_text(tree, file_id):
659
643
            if file_id is not None:
660
 
                return tree.get_file(file_id, path).readlines()
 
644
                return tree.get_file(file_id).readlines()
661
645
            else:
662
646
                return []
663
647
        try:
664
 
            from_text = _get_text(self.old_tree, from_file_id, from_path)
665
 
            to_text = _get_text(self.new_tree, to_file_id, to_path)
 
648
            from_text = _get_text(self.old_tree, from_file_id)
 
649
            to_text = _get_text(self.new_tree, to_file_id)
666
650
            self.text_differ(from_label, from_text, to_label, to_text,
667
651
                             self.to_file)
668
652
        except errors.BinaryFile:
684
668
    def from_string(klass, command_string, old_tree, new_tree, to_file,
685
669
                    path_encoding='utf-8'):
686
670
        command_template = commands.shlex_split_unicode(command_string)
687
 
        if '@' not in command_string:
688
 
            command_template.extend(['@old_path', '@new_path'])
 
671
        command_template.extend(['%(old_path)s', '%(new_path)s'])
689
672
        return klass(command_template, old_tree, new_tree, to_file,
690
673
                     path_encoding)
691
674
 
698
681
 
699
682
    def _get_command(self, old_path, new_path):
700
683
        my_map = {'old_path': old_path, 'new_path': new_path}
701
 
        return [AtTemplate(t).substitute(my_map) for t in
702
 
                self.command_template]
 
684
        return [t % my_map for t in self.command_template]
703
685
 
704
686
    def _execute(self, old_path, new_path):
705
687
        command = self._get_command(old_path, new_path)
725
707
                raise
726
708
        return True
727
709
 
728
 
    def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
729
 
                    allow_write=False):
730
 
        if not force_temp and isinstance(tree, WorkingTree):
731
 
            return tree.abspath(tree.id2path(file_id))
732
 
        
 
710
    def _write_file(self, file_id, tree, prefix, relpath):
733
711
        full_path = osutils.pathjoin(self._root, prefix, relpath)
734
 
        if not force_temp and self._try_symlink_root(tree, prefix):
 
712
        if self._try_symlink_root(tree, prefix):
735
713
            return full_path
736
714
        parent_dir = osutils.dirname(full_path)
737
715
        try:
748
726
                target.close()
749
727
        finally:
750
728
            source.close()
751
 
        if not allow_write:
752
 
            osutils.make_readonly(full_path)
753
 
        try:
754
 
            mtime = tree.get_file_mtime(file_id)
755
 
        except errors.FileTimestampUnavailable:
756
 
            mtime = 0
 
729
        osutils.make_readonly(full_path)
 
730
        mtime = tree.get_file_mtime(file_id)
757
731
        os.utime(full_path, (mtime, mtime))
758
732
        return full_path
759
733
 
760
 
    def _prepare_files(self, file_id, old_path, new_path, force_temp=False,
761
 
                       allow_write_new=False):
 
734
    def _prepare_files(self, file_id, old_path, new_path):
762
735
        old_disk_path = self._write_file(file_id, self.old_tree, 'old',
763
 
                                         old_path, force_temp)
 
736
                                         old_path)
764
737
        new_disk_path = self._write_file(file_id, self.new_tree, 'new',
765
 
                                         new_path, force_temp,
766
 
                                         allow_write=allow_write_new)
 
738
                                         new_path)
767
739
        return old_disk_path, new_disk_path
768
740
 
769
741
    def finish(self):
770
 
        try:
771
 
            osutils.rmtree(self._root)
772
 
        except OSError, e:
773
 
            if e.errno != errno.ENOENT:
774
 
                mutter("The temporary directory \"%s\" was not "
775
 
                        "cleanly removed: %s." % (self._root, e))
 
742
        osutils.rmtree(self._root)
776
743
 
777
744
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
778
745
        if (old_kind, new_kind) != ('file', 'file'):
779
746
            return DiffPath.CANNOT_DIFF
780
 
        (old_disk_path, new_disk_path) = self._prepare_files(
781
 
                                                file_id, old_path, new_path)
782
 
        self._execute(old_disk_path, new_disk_path)
783
 
 
784
 
    def edit_file(self, file_id):
785
 
        """Use this tool to edit a file.
786
 
 
787
 
        A temporary copy will be edited, and the new contents will be
788
 
        returned.
789
 
 
790
 
        :param file_id: The id of the file to edit.
791
 
        :return: The new contents of the file.
792
 
        """
793
 
        old_path = self.old_tree.id2path(file_id)
794
 
        new_path = self.new_tree.id2path(file_id)
795
 
        new_abs_path = self._prepare_files(file_id, old_path, new_path,
796
 
                                           allow_write_new=True,
797
 
                                           force_temp=True)[1]
798
 
        command = self._get_command(osutils.pathjoin('old', old_path),
799
 
                                    osutils.pathjoin('new', new_path))
800
 
        subprocess.call(command, cwd=self._root)
801
 
        new_file = open(new_abs_path, 'r')
802
 
        try:
803
 
            return new_file.read()
804
 
        finally:
805
 
            new_file.close()
 
747
        self._prepare_files(file_id, old_path, new_path)
 
748
        self._execute(osutils.pathjoin('old', old_path),
 
749
                      osutils.pathjoin('new', new_path))
806
750
 
807
751
 
808
752
class DiffTree(object):
946
890
                self.to_file.write("=== modified %s '%s'%s\n" % (kind[0],
947
891
                                   newpath_encoded, prop_str))
948
892
            if changed_content:
949
 
                self._diff(file_id, oldpath, newpath, kind[0], kind[1])
 
893
                self.diff(file_id, oldpath, newpath)
950
894
                has_changes = 1
951
895
            if renamed:
952
896
                has_changes = 1
967
911
            new_kind = self.new_tree.kind(file_id)
968
912
        except (errors.NoSuchId, errors.NoSuchFile):
969
913
            new_kind = None
970
 
        self._diff(file_id, old_path, new_path, old_kind, new_kind)
971
 
 
972
 
 
973
 
    def _diff(self, file_id, old_path, new_path, old_kind, new_kind):
 
914
 
974
915
        result = DiffPath._diff_many(self.differs, file_id, old_path,
975
916
                                       new_path, old_kind, new_kind)
976
917
        if result is DiffPath.CANNOT_DIFF: