1
# Copyright (C) 2005-2010 Canonical Ltd.
1
# Copyright (C) 2005-2011 Canonical Ltd.
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
99
97
if sequence_matcher is None:
100
98
sequence_matcher = patiencediff.PatienceSequenceMatcher
101
99
ud = patiencediff.unified_diff(oldlines, newlines,
102
fromfile=old_filename.encode(path_encoding),
103
tofile=new_filename.encode(path_encoding),
100
fromfile=old_filename.encode(path_encoding, 'replace'),
101
tofile=new_filename.encode(path_encoding, 'replace'),
104
102
sequencematcher=sequence_matcher)
421
419
# Get the specific files (all files is None, no files is [])
422
420
if make_paths_wt_relative and working_tree is not None:
424
from bzrlib.builtins import safe_relpath_files
425
other_paths = safe_relpath_files(working_tree, other_paths,
421
other_paths = working_tree.safe_relpath_files(
426
423
apply_view=apply_view)
427
except errors.FileInWrongBranch:
428
raise errors.BzrCommandError("Files are in different branches")
429
424
specific_files.extend(other_paths)
430
425
if len(specific_files) == 0:
431
426
specific_files = None
468
463
"""Show in text form the changes from one tree to another.
470
465
:param to_file: The output stream.
471
:param specific_files:Include only changes to these files - None for all
466
:param specific_files: Include only changes to these files - None for all
473
468
:param external_diff_options: If set, use an external GNU diff and pass
707
702
def _get_text(tree, file_id, path):
708
703
if file_id is not None:
709
return tree.get_file(file_id, path).readlines()
704
return tree.get_file_lines(file_id, path)
713
708
from_text = _get_text(self.old_tree, from_file_id, from_path)
714
709
to_text = _get_text(self.new_tree, to_file_id, to_path)
715
710
self.text_differ(from_label, from_text, to_label, to_text,
711
self.to_file, path_encoding=self.path_encoding)
717
712
except errors.BinaryFile:
718
713
self.to_file.write(
719
714
("Binary files %s and %s differ\n" %
720
(from_label, to_label)).encode(self.path_encoding))
715
(from_label, to_label)).encode(self.path_encoding,'replace'))
721
716
return self.CHANGED
742
def make_from_diff_tree(klass, command_string):
737
def make_from_diff_tree(klass, command_string, external_diff_options=None):
743
738
def from_diff_tree(diff_tree):
744
return klass.from_string(command_string, diff_tree.old_tree,
739
full_command_string = [command_string]
740
if external_diff_options is not None:
741
full_command_string += ' ' + external_diff_options
742
return klass.from_string(full_command_string, diff_tree.old_tree,
745
743
diff_tree.new_tree, diff_tree.to_file)
746
744
return from_diff_tree
748
746
def _get_command(self, old_path, new_path):
749
747
my_map = {'old_path': old_path, 'new_path': new_path}
750
return [AtTemplate(t).substitute(my_map) for t in
751
self.command_template]
748
command = [AtTemplate(t).substitute(my_map) for t in
749
self.command_template]
750
if sys.platform == 'win32': # Popen doesn't accept unicode on win32
753
if isinstance(c, unicode):
754
command_encoded.append(c.encode('mbcs'))
756
command_encoded.append(c)
757
return command_encoded
753
761
def _execute(self, old_path, new_path):
754
762
command = self._get_command(old_path, new_path)
787
"""Returns safe encoding for passing file path to diff tool"""
788
if sys.platform == 'win32':
791
# Don't fallback to 'utf-8' because subprocess may not be able to
792
# handle utf-8 correctly when locale is not utf-8.
793
return sys.getfilesystemencoding() or 'ascii'
795
def _is_safepath(self, path):
796
"""Return true if `path` may be able to pass to subprocess."""
799
return path == path.encode(fenc).decode(fenc)
803
def _safe_filename(self, prefix, relpath):
804
"""Replace unsafe character in `relpath` then join `self._root`,
805
`prefix` and `relpath`."""
807
# encoded_str.replace('?', '_') may break multibyte char.
808
# So we should encode, decode, then replace(u'?', u'_')
809
relpath_tmp = relpath.encode(fenc, 'replace').decode(fenc, 'replace')
810
relpath_tmp = relpath_tmp.replace(u'?', u'_')
811
return osutils.pathjoin(self._root, prefix, relpath_tmp)
777
813
def _write_file(self, file_id, tree, prefix, relpath, force_temp=False,
778
814
allow_write=False):
779
815
if not force_temp and isinstance(tree, WorkingTree):
780
return tree.abspath(tree.id2path(file_id))
782
full_path = osutils.pathjoin(self._root, prefix, relpath)
816
full_path = tree.abspath(tree.id2path(file_id))
817
if self._is_safepath(full_path):
820
full_path = self._safe_filename(prefix, relpath)
783
821
if not force_temp and self._try_symlink_root(tree, prefix):
785
823
parent_dir = osutils.dirname(full_path)
843
881
old_path = self.old_tree.id2path(file_id)
844
882
new_path = self.new_tree.id2path(file_id)
845
new_abs_path = self._prepare_files(file_id, old_path, new_path,
846
allow_write_new=True,
848
command = self._get_command(osutils.pathjoin('old', old_path),
849
osutils.pathjoin('new', new_path))
883
old_abs_path, new_abs_path = self._prepare_files(
884
file_id, old_path, new_path,
885
allow_write_new=True,
887
command = self._get_command(old_abs_path, new_abs_path)
850
888
subprocess.call(command, cwd=self._root)
851
new_file = open(new_abs_path, 'r')
889
new_file = open(new_abs_path, 'rb')
853
891
return new_file.read()
904
942
"""Factory for producing a DiffTree.
906
944
Designed to accept options used by show_diff_trees.
907
946
:param old_tree: The tree to show as old in the comparison
908
947
:param new_tree: The tree to show as new in the comparison
909
948
:param to_file: File to write comparisons to
915
954
:param using: Commandline to use to invoke an external diff tool
917
956
if using is not None:
918
extra_factories = [DiffFromTool.make_from_diff_tree(using)]
957
extra_factories = [DiffFromTool.make_from_diff_tree(using, external_diff_options)]
920
959
extra_factories = []
921
960
if external_diff_options:
922
961
opts = external_diff_options.split()
923
def diff_file(olab, olines, nlab, nlines, to_file):
962
def diff_file(olab, olines, nlab, nlines, to_file, path_encoding=None):
963
""":param path_encoding: not used but required
964
to match the signature of internal_diff.
924
966
external_diff(olab, olines, nlab, nlines, to_file, opts)
926
968
diff_file = internal_diff