73
74
self.opcodes = None
76
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file,
77
def internal_diff(old_label, oldlines, new_label, newlines, to_file,
77
78
allow_binary=False, sequence_matcher=None,
78
79
path_encoding='utf8', context_lines=DEFAULT_CONTEXT_AMOUNT):
79
80
# FIXME: difflib is wrong if there is no trailing newline.
86
87
# In the meantime we at least make sure the patch isn't
90
# Special workaround for Python2.3, where difflib fails if
91
# both sequences are empty.
92
if not oldlines and not newlines:
95
90
if allow_binary is False:
96
91
textfile.check_text_lines(oldlines)
97
92
textfile.check_text_lines(newlines)
99
94
if sequence_matcher is None:
100
95
sequence_matcher = patiencediff.PatienceSequenceMatcher
101
ud = patiencediff.unified_diff(oldlines, newlines,
102
fromfile=old_filename.encode(path_encoding, 'replace'),
103
tofile=new_filename.encode(path_encoding, 'replace'),
96
ud = patiencediff.unified_diff_bytes(oldlines, newlines,
97
fromfile=old_label.encode(path_encoding, 'replace'),
98
tofile=new_label.encode(path_encoding, 'replace'),
104
99
n=context_lines, sequencematcher=sequence_matcher)
109
104
# work-around for difflib being too smart for its own good
110
105
# if /dev/null is "1,0", patch won't recognize it as /dev/null
112
ud[2] = ud[2].replace('-1,0', '-0,0')
107
ud[2] = ud[2].replace(b'-1,0', b'-0,0')
113
108
elif not newlines:
114
ud[2] = ud[2].replace('+1,0', '+0,0')
109
ud[2] = ud[2].replace(b'+1,0', b'+0,0')
117
112
to_file.write(line)
118
if not line.endswith('\n'):
119
to_file.write("\n\\ No newline at end of file\n")
113
if not line.endswith(b'\n'):
114
to_file.write(b"\n\\ No newline at end of file\n")
123
118
def _spawn_external_diff(diffcmd, capture_errors=True):
194
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
189
def external_diff(old_label, oldlines, new_label, newlines, to_file,
196
191
"""Display a diff by calling out to the external diff program."""
197
192
# make sure our own output is properly ordered before the diff
221
216
if sys.platform == 'win32':
222
217
# Popen doesn't do the proper encoding for external commands
223
218
# Since we are dealing with an ANSI api, use mbcs encoding
224
old_filename = old_filename.encode('mbcs')
225
new_filename = new_filename.encode('mbcs')
219
old_label = old_label.encode('mbcs')
220
new_label = new_label.encode('mbcs')
226
221
diffcmd = ['diff',
227
'--label', old_filename,
222
'--label', old_label,
229
'--label', new_filename,
224
'--label', new_label,
254
249
out, err = pipe.communicate()
256
251
# Write out the new i18n diff response
257
to_file.write(out+'\n')
252
to_file.write(out+b'\n')
258
253
if pipe.returncode != 2:
259
254
raise errors.BzrError(
260
255
'external diff failed with exit code 2'
261
256
' when run with LANG=C and LC_ALL=C,'
262
257
' but not when run natively: %r' % (diffcmd,))
264
first_line = lang_c_out.split('\n', 1)[0]
259
first_line = lang_c_out.split(b'\n', 1)[0]
265
260
# Starting with diffutils 2.8.4 the word "binary" was dropped.
266
m = re.match('^(binary )?files.*differ$', first_line, re.I)
261
m = re.match(b'^(binary )?files.*differ$', first_line, re.I)
268
263
raise errors.BzrError('external diff failed with exit code 2;'
269
264
' command: %r' % (diffcmd,))
491
486
def get_executable_change(old_is_x, new_is_x):
492
descr = { True:"+x", False:"-x", None:"??" }
487
descr = { True:b"+x", False:b"-x", None:b"??" }
493
488
if old_is_x != new_is_x:
494
return ["%s to %s" % (descr[old_is_x], descr[new_is_x],)]
489
return [b"%s to %s" % (descr[old_is_x], descr[new_is_x],)]
618
613
def diff_symlink(self, old_target, new_target):
619
614
if old_target is None:
620
self.to_file.write('=== target is %r\n' % new_target)
615
self.to_file.write(b'=== target is \'%s\'\n' %
616
new_target.encode(self.path_encoding, 'replace'))
621
617
elif new_target is None:
622
self.to_file.write('=== target was %r\n' % old_target)
618
self.to_file.write(b'=== target was \'%s\'\n' %
619
old_target.encode(self.path_encoding, 'replace'))
624
self.to_file.write('=== target changed %r => %r\n' %
625
(old_target, new_target))
621
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')))
626
624
return self.CHANGED
668
666
to_file_id = None
670
668
return self.CANNOT_DIFF
671
from_label = '%s%s\t%s' % (self.old_label, old_path, old_date)
672
to_label = '%s%s\t%s' % (self.new_label, new_path, new_date)
669
from_label = '%s%s\t%s' % (self.old_label, old_path,
671
to_label = '%s%s\t%s' % (self.new_label, new_path,
673
673
return self.diff_text(old_path, new_path, from_label, to_label,
674
674
from_file_id, to_file_id)
738
738
if sys.platform == 'win32': # Popen doesn't accept unicode on win32
739
739
command_encoded = []
740
740
for c in command:
741
if isinstance(c, unicode):
741
if isinstance(c, text_type):
742
742
command_encoded.append(c.encode('mbcs'))
744
744
command_encoded.append(c)
996
997
properties_changed.extend(get_executable_change(executable[0], executable[1]))
998
999
if properties_changed:
999
prop_str = " (properties changed: %s)" % (", ".join(properties_changed),)
1000
prop_str = b" (properties changed: %s)" % (
1001
b", ".join(properties_changed),)
1003
1005
if (old_present, new_present) == (True, False):
1004
self.to_file.write("=== removed %s '%s'\n" %
1005
(kind[0], oldpath_encoded))
1006
self.to_file.write(b"=== removed %s '%s'\n" %
1007
(kind[0].encode('ascii'), oldpath_encoded))
1006
1008
newpath = oldpath
1007
1009
elif (old_present, new_present) == (False, True):
1008
self.to_file.write("=== added %s '%s'\n" %
1009
(kind[1], newpath_encoded))
1010
self.to_file.write(b"=== added %s '%s'\n" %
1011
(kind[1].encode('ascii'), newpath_encoded))
1010
1012
oldpath = newpath
1012
self.to_file.write("=== renamed %s '%s' => '%s'%s\n" %
1013
(kind[0], oldpath_encoded, newpath_encoded, prop_str))
1014
self.to_file.write(b"=== renamed %s '%s' => '%s'%s\n" %
1015
(kind[0].encode('ascii'), oldpath_encoded, newpath_encoded, prop_str))
1015
1017
# if it was produced by iter_changes, it must be
1016
1018
# modified *somehow*, either content or execute bit.
1017
self.to_file.write("=== modified %s '%s'%s\n" % (kind[0],
1019
self.to_file.write(b"=== modified %s '%s'%s\n" % (kind[0].encode('ascii'),
1018
1020
newpath_encoded, prop_str))
1019
1021
if changed_content:
1020
1022
self._diff(oldpath, newpath, kind[0], kind[1], file_id=file_id)
1043
1045
def _diff(self, old_path, new_path, old_kind, new_kind, file_id):
1044
1046
result = DiffPath._diff_many(self.differs, file_id, old_path,
1045
new_path, old_kind, new_kind)
1047
new_path, old_kind, new_kind)
1046
1048
if result is DiffPath.CANNOT_DIFF:
1047
1049
error_path = new_path
1048
1050
if error_path is None: