94
94
if sequence_matcher is None:
95
95
sequence_matcher = patiencediff.PatienceSequenceMatcher
96
96
ud = patiencediff.unified_diff_bytes(oldlines, newlines,
97
fromfile=old_label.encode(path_encoding, 'replace'),
98
tofile=new_label.encode(path_encoding, 'replace'),
99
n=context_lines, sequencematcher=sequence_matcher)
97
fromfile=old_label.encode(
98
path_encoding, 'replace'),
99
tofile=new_label.encode(
100
path_encoding, 'replace'),
101
n=context_lines, sequencematcher=sequence_matcher)
102
if len(ud) == 0: # Identical contents, nothing to do
104
if len(ud) == 0: # Identical contents, nothing to do
104
106
# work-around for difflib being too smart for its own good
105
107
# if /dev/null is "1,0", patch won't recognize it as /dev/null
249
253
out, err = pipe.communicate()
251
255
# Write out the new i18n diff response
252
to_file.write(out+b'\n')
256
to_file.write(out + b'\n')
253
257
if pipe.returncode != 2:
254
258
raise errors.BzrError(
255
'external diff failed with exit code 2'
256
' when run with LANG=C and LC_ALL=C,'
257
' but not when run natively: %r' % (diffcmd,))
259
'external diff failed with exit code 2'
260
' when run with LANG=C and LC_ALL=C,'
261
' but not when run natively: %r' % (diffcmd,))
259
263
first_line = lang_c_out.split(b'\n', 1)[0]
260
264
# Starting with diffutils 2.8.4 the word "binary" was dropped.
301
304
def get_trees_and_branches_to_diff_locked(
302
path_list, revision_specs, old_url, new_url, add_cleanup, apply_view=True):
305
path_list, revision_specs, old_url, new_url, add_cleanup, apply_view=True):
303
306
"""Get the trees and specific files to diff given a list of paths.
305
308
This method works out the trees to be diff'ed and the files of
390
393
views.check_path_in_view(working_tree, relpath)
391
394
specific_files.append(relpath)
392
395
new_tree = _get_tree_to_diff(new_revision_spec, working_tree, branch,
393
basis_is_default=working_tree is None)
396
basis_is_default=working_tree is None)
394
397
new_branch = branch
396
399
# Get the specific files (all files is None, no files is [])
444
447
:param to_file: The output stream.
445
448
:param specific_files: Include only changes to these files - None for all
447
:param external_diff_options: If set, use an external GNU diff and pass
450
:param external_diff_options: If set, use an external GNU diff and pass
449
452
:param extra_trees: If set, more Trees to use for looking up file ids
450
:param path_encoding: If set, the path will be encoded as specified,
453
:param path_encoding: If set, the path will be encoded as specified,
451
454
otherwise is supposed to be utf8
452
455
:param format_cls: Formatter class (DiffTree subclass)
477
def _patch_header_date(tree, file_id, path):
480
def _patch_header_date(tree, path):
478
481
"""Returns a timestamp suitable for use in a patch header."""
480
mtime = tree.get_file_mtime(path, file_id)
483
mtime = tree.get_file_mtime(path)
481
484
except FileTimestampUnavailable:
483
486
return timestamp.format_patch_date(mtime)
486
489
def get_executable_change(old_is_x, new_is_x):
487
descr = { True:b"+x", False:b"-x", None:b"??" }
490
descr = {True: b"+x", False: b"-x", None: b"??"}
488
491
if old_is_x != new_is_x:
489
492
return [b"%s to %s" % (descr[old_is_x], descr[new_is_x],)]
523
526
diff_tree.to_file, diff_tree.path_encoding)
526
def _diff_many(differs, file_id, old_path, new_path, old_kind, new_kind):
529
def _diff_many(differs, old_path, new_path, old_kind, new_kind):
527
530
for file_differ in differs:
528
result = file_differ.diff(file_id, old_path, new_path, old_kind,
531
result = file_differ.diff(old_path, new_path, old_kind, new_kind)
530
532
if result is not DiffPath.CANNOT_DIFF:
549
552
def from_diff_tree(klass, diff_tree):
550
553
return klass(diff_tree.differs)
552
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
555
def diff(self, old_path, new_path, old_kind, new_kind):
553
556
"""Perform comparison
555
:param file_id: The file_id of the file to compare
556
558
:param old_path: Path of the file in the old tree
557
559
:param new_path: Path of the file in the new tree
558
560
:param old_kind: Old file-kind of the file
561
563
if None in (old_kind, new_kind):
562
564
return DiffPath.CANNOT_DIFF
563
result = DiffPath._diff_many(self.differs, file_id, old_path,
564
new_path, old_kind, None)
565
result = DiffPath._diff_many(
566
self.differs, old_path, new_path, old_kind, None)
565
567
if result is DiffPath.CANNOT_DIFF:
567
return DiffPath._diff_many(self.differs, file_id, old_path, new_path,
569
return DiffPath._diff_many(
570
self.differs, old_path, new_path, None, new_kind)
571
573
class DiffDirectory(DiffPath):
573
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
575
def diff(self, old_path, new_path, old_kind, new_kind):
574
576
"""Perform comparison between two directories. (dummy)
586
588
class DiffSymlink(DiffPath):
588
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
590
def diff(self, old_path, new_path, old_kind, new_kind):
589
591
"""Perform comparison between two symlinks
591
:param file_id: The file_id of the file to compare
592
593
:param old_path: Path of the file in the old tree
593
594
:param new_path: Path of the file in the new tree
594
595
:param old_kind: Old file-kind of the file
597
598
if 'symlink' not in (old_kind, new_kind):
598
599
return self.CANNOT_DIFF
599
600
if old_kind == 'symlink':
600
old_target = self.old_tree.get_symlink_target(old_path, file_id)
601
old_target = self.old_tree.get_symlink_target(old_path)
601
602
elif old_kind is None:
602
603
old_target = None
604
605
return self.CANNOT_DIFF
605
606
if new_kind == 'symlink':
606
new_target = self.new_tree.get_symlink_target(new_path, file_id)
607
new_target = self.new_tree.get_symlink_target(new_path)
607
608
elif new_kind is None:
608
609
new_target = None
613
614
def diff_symlink(self, old_target, new_target):
614
615
if old_target is None:
615
616
self.to_file.write(b'=== target is \'%s\'\n' %
616
new_target.encode(self.path_encoding, 'replace'))
617
new_target.encode(self.path_encoding, 'replace'))
617
618
elif new_target is None:
618
619
self.to_file.write(b'=== target was \'%s\'\n' %
619
old_target.encode(self.path_encoding, 'replace'))
620
old_target.encode(self.path_encoding, 'replace'))
621
622
self.to_file.write(b'=== target changed \'%s\' => \'%s\'\n' %
622
(old_target.encode(self.path_encoding, 'replace'),
623
new_target.encode(self.path_encoding, 'replace')))
623
(old_target.encode(self.path_encoding, 'replace'),
624
new_target.encode(self.path_encoding, 'replace')))
624
625
return self.CHANGED
630
631
# or removed in a diff.
631
632
EPOCH_DATE = '1970-01-01 00:00:00 +0000'
633
def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8',
634
old_label='', new_label='', text_differ=internal_diff,
634
def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8',
635
old_label='', new_label='', text_differ=internal_diff,
635
636
context_lines=DEFAULT_CONTEXT_AMOUNT):
636
637
DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
637
638
self.text_differ = text_differ
640
641
self.path_encoding = path_encoding
641
642
self.context_lines = context_lines
643
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
644
def diff(self, old_path, new_path, old_kind, new_kind):
644
645
"""Compare two files in unified diff format
646
:param file_id: The file_id of the file to compare
647
647
:param old_path: Path of the file in the old tree
648
648
:param new_path: Path of the file in the new tree
649
649
:param old_kind: Old file-kind of the file
652
652
if 'file' not in (old_kind, new_kind):
653
653
return self.CANNOT_DIFF
654
from_file_id = to_file_id = file_id
655
654
if old_kind == 'file':
656
old_date = _patch_header_date(self.old_tree, file_id, old_path)
655
old_date = _patch_header_date(self.old_tree, old_path)
657
656
elif old_kind is None:
658
657
old_date = self.EPOCH_DATE
661
659
return self.CANNOT_DIFF
662
660
if new_kind == 'file':
663
new_date = _patch_header_date(self.new_tree, file_id, new_path)
661
new_date = _patch_header_date(self.new_tree, new_path)
664
662
elif new_kind is None:
665
663
new_date = self.EPOCH_DATE
668
665
return self.CANNOT_DIFF
669
666
from_label = '%s%s\t%s' % (self.old_label, old_path,
671
668
to_label = '%s%s\t%s' % (self.new_label, new_path,
673
return self.diff_text(old_path, new_path, from_label, to_label,
674
from_file_id, to_file_id)
670
return self.diff_text(old_path, new_path, from_label, to_label)
676
def diff_text(self, from_path, to_path, from_label, to_label,
677
from_file_id=None, to_file_id=None):
672
def diff_text(self, from_path, to_path, from_label, to_label):
678
673
"""Diff the content of given files in two trees
680
675
:param from_path: The path in the from tree. If None,
682
677
:param to_path: The path in the to tree. This may refer
683
678
to a different file from from_path. If None,
684
679
the file is not present in the to tree.
685
:param from_file_id: The id of the file in the from tree or None if
687
:param to_file_id: The id of the file in the to tree or None if
690
def _get_text(tree, file_id, path):
693
return tree.get_file_lines(path, file_id)
681
def _get_text(tree, path):
685
return tree.get_file_lines(path)
686
except errors.NoSuchFile:
695
from_text = _get_text(self.old_tree, from_file_id, from_path)
696
to_text = _get_text(self.new_tree, to_file_id, to_path)
689
from_text = _get_text(self.old_tree, from_path)
690
to_text = _get_text(self.new_tree, to_path)
697
691
self.text_differ(from_label, from_text, to_label, to_text,
698
692
self.to_file, path_encoding=self.path_encoding,
699
693
context_lines=self.context_lines)
700
694
except errors.BinaryFile:
701
695
self.to_file.write(
702
("Binary files %s and %s differ\n" %
703
(from_label, to_label)).encode(self.path_encoding, 'replace'))
696
("Binary files %s and %s differ\n" %
697
(from_label, to_label)).encode(self.path_encoding, 'replace'))
704
698
return self.CHANGED
761
755
return proc.wait()
763
757
def _try_symlink_root(self, tree, prefix):
764
if (getattr(tree, 'abspath', None) is None
765
or not osutils.host_os_dereferences_symlinks()):
758
if (getattr(tree, 'abspath', None) is None or
759
not osutils.host_os_dereferences_symlinks()):
768
762
os.symlink(tree.abspath(''), osutils.pathjoin(self._root, prefix))
800
794
return osutils.pathjoin(self._root, prefix, relpath_tmp)
802
796
def _write_file(self, relpath, tree, prefix, force_temp=False,
803
allow_write=False, file_id=None):
804
798
if not force_temp and isinstance(tree, WorkingTree):
805
799
full_path = tree.abspath(relpath)
806
800
if self._is_safepath(full_path):
815
809
except OSError as e:
816
810
if e.errno != errno.EEXIST:
818
source = tree.get_file(relpath, file_id)
812
source = tree.get_file(relpath)
820
814
with open(full_path, 'wb') as target:
821
815
osutils.pumpfile(source, target)
825
mtime = tree.get_file_mtime(relpath, file_id)
819
mtime = tree.get_file_mtime(relpath)
826
820
except FileTimestampUnavailable:
834
828
def _prepare_files(self, old_path, new_path, force_temp=False,
835
allow_write_new=False, file_id=None):
836
old_disk_path = self._write_file(old_path, self.old_tree, 'old',
837
force_temp, file_id=file_id)
838
new_disk_path = self._write_file(new_path, self.new_tree, 'new',
839
force_temp, file_id=file_id,
840
allow_write=allow_write_new)
829
allow_write_new=False):
830
old_disk_path = self._write_file(
831
old_path, self.old_tree, 'old', force_temp)
832
new_disk_path = self._write_file(
833
new_path, self.new_tree, 'new', force_temp,
834
allow_write=allow_write_new)
841
835
return old_disk_path, new_disk_path
843
837
def finish(self):
846
840
except OSError as e:
847
841
if e.errno != errno.ENOENT:
848
842
mutter("The temporary directory \"%s\" was not "
849
"cleanly removed: %s." % (self._root, e))
843
"cleanly removed: %s." % (self._root, e))
851
def diff(self, file_id, old_path, new_path, old_kind, new_kind):
845
def diff(self, old_path, new_path, old_kind, new_kind):
852
846
if (old_kind, new_kind) != ('file', 'file'):
853
847
return DiffPath.CANNOT_DIFF
854
848
(old_disk_path, new_disk_path) = self._prepare_files(
855
old_path, new_path, file_id=file_id)
856
850
self._execute(old_disk_path, new_disk_path)
858
def edit_file(self, old_path, new_path, file_id=None):
852
def edit_file(self, old_path, new_path):
859
853
"""Use this tool to edit a file.
861
855
A temporary copy will be edited, and the new contents will be
864
:param file_id: The id of the file to edit.
865
858
:return: The new contents of the file.
867
860
old_abs_path, new_abs_path = self._prepare_files(
868
old_path, new_path, allow_write_new=True, force_temp=True,
861
old_path, new_path, allow_write_new=True, force_temp=True)
870
862
command = self._get_command(old_abs_path, new_abs_path)
871
863
subprocess.call(command, cwd=self._root)
872
864
with open(new_abs_path, 'rb') as new_file:
934
926
:param using: Commandline to use to invoke an external diff tool
936
928
if using is not None:
937
extra_factories = [DiffFromTool.make_from_diff_tree(using, external_diff_options)]
929
extra_factories = [DiffFromTool.make_from_diff_tree(
930
using, external_diff_options)]
939
932
extra_factories = []
940
933
if external_diff_options:
941
934
opts = external_diff_options.split()
942
936
def diff_file(olab, olines, nlab, nlines, to_file, path_encoding=None, context_lines=None):
943
937
""":param path_encoding: not used but required
944
938
to match the signature of internal_diff.
967
961
# TODO: Generation of pseudo-diffs for added/deleted files could
968
962
# be usefully made into a much faster special case.
969
963
iterator = self.new_tree.iter_changes(self.old_tree,
970
specific_files=specific_files,
971
extra_trees=extra_trees,
972
require_versioned=True)
964
specific_files=specific_files,
965
extra_trees=extra_trees,
966
require_versioned=True)
974
969
def changes_key(change):
975
970
old_path, new_path = change[1]
980
976
def get_encoded_path(path):
981
977
if path is not None:
982
978
return path.encode(self.path_encoding, "replace")
986
982
# is, missing) in both trees are skipped as well.
987
983
if parent == (None, None) or kind == (None, None):
989
if kind[0] == 'symlink' and not osutils.has_symlinks():
990
warning('bzr: warning: Ignoring "%s" as symlinks are not '
985
if kind[0] == 'symlink' and not self.new_tree.supports_symlinks():
987
'bzr: warning: Ignoring "%s" as symlinks are not '
991
988
'supported on this platform.' % (paths[0],))
993
990
oldpath, newpath = paths
998
995
renamed = (parent[0], name[0]) != (parent[1], name[1])
1000
997
properties_changed = []
1001
properties_changed.extend(get_executable_change(executable[0], executable[1]))
998
properties_changed.extend(
999
get_executable_change(executable[0], executable[1]))
1003
1001
if properties_changed:
1004
1002
prop_str = b" (properties changed: %s)" % (
1005
b", ".join(properties_changed),)
1003
b", ".join(properties_changed),)
1016
1014
oldpath = newpath
1018
1016
self.to_file.write(b"=== renamed %s '%s' => '%s'%s\n" %
1019
(kind[0].encode('ascii'), oldpath_encoded, newpath_encoded, prop_str))
1017
(kind[0].encode('ascii'), oldpath_encoded, newpath_encoded, prop_str))
1021
1019
# if it was produced by iter_changes, it must be
1022
1020
# modified *somehow*, either content or execute bit.
1023
1021
self.to_file.write(b"=== modified %s '%s'%s\n" % (kind[0].encode('ascii'),
1024
newpath_encoded, prop_str))
1022
newpath_encoded, prop_str))
1025
1023
if changed_content:
1026
self._diff(oldpath, newpath, kind[0], kind[1], file_id=file_id)
1024
self._diff(oldpath, newpath, kind[0], kind[1])
1027
1025
has_changes = 1
1029
1027
has_changes = 1
1030
1028
return has_changes
1032
def diff(self, file_id, old_path, new_path):
1030
def diff(self, old_path, new_path):
1033
1031
"""Perform a diff of a single file
1035
:param file_id: file-id of the file
1036
1033
:param old_path: The path of the file in the old tree
1037
1034
:param new_path: The path of the file in the new tree
1039
1036
if old_path is None:
1040
1037
old_kind = None
1042
old_kind = self.old_tree.kind(old_path, file_id)
1039
old_kind = self.old_tree.kind(old_path)
1043
1040
if new_path is None:
1044
1041
new_kind = None
1046
new_kind = self.new_tree.kind(new_path, file_id)
1047
self._diff(old_path, new_path, old_kind, new_kind, file_id=file_id)
1043
new_kind = self.new_tree.kind(new_path)
1044
self._diff(old_path, new_path, old_kind, new_kind)
1049
def _diff(self, old_path, new_path, old_kind, new_kind, file_id):
1050
result = DiffPath._diff_many(self.differs, file_id, old_path,
1051
new_path, old_kind, new_kind)
1046
def _diff(self, old_path, new_path, old_kind, new_kind):
1047
result = DiffPath._diff_many(
1048
self.differs, old_path, new_path, old_kind, new_kind)
1052
1049
if result is DiffPath.CANNOT_DIFF:
1053
1050
error_path = new_path
1054
1051
if error_path is None: