14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
from bzrlib.lazy_import import lazy_import
22
lazy_import(globals(), """
25
36
# compatability - plugins import compare_trees from diff!!!
26
37
# deprecated as of 0.10
27
38
from bzrlib.delta import compare_trees
28
from bzrlib.errors import BzrError
29
import bzrlib.errors as errors
31
from bzrlib.patiencediff import unified_diff
32
import bzrlib.patiencediff
33
from bzrlib.symbol_versioning import (deprecated_function,
35
from bzrlib.textfile import check_text_lines
39
from bzrlib.symbol_versioning import (
36
43
from bzrlib.trace import mutter, warning
62
69
if allow_binary is False:
63
check_text_lines(oldlines)
64
check_text_lines(newlines)
70
textfile.check_text_lines(oldlines)
71
textfile.check_text_lines(newlines)
66
73
if sequence_matcher is None:
67
sequence_matcher = bzrlib.patiencediff.PatienceSequenceMatcher
68
ud = unified_diff(oldlines, newlines,
74
sequence_matcher = patiencediff.PatienceSequenceMatcher
75
ud = patiencediff.unified_diff(oldlines, newlines,
69
76
fromfile=old_filename.encode(path_encoding),
70
77
tofile=new_filename.encode(path_encoding),
71
78
sequencematcher=sequence_matcher)
99
"""Set the env var LANG=C"""
100
osutils.set_or_unset_env('LANG', 'C')
101
osutils.set_or_unset_env('LC_ALL', None)
102
osutils.set_or_unset_env('LC_CTYPE', None)
103
osutils.set_or_unset_env('LANGUAGE', None)
106
def _spawn_external_diff(diffcmd, capture_errors=True):
107
"""Spawn the externall diff process, and return the child handle.
109
:param diffcmd: The command list to spawn
110
:param capture_errors: Capture stderr as well as setting LANG=C.
111
This lets us read and understand the output of diff, and respond
113
:return: A Popen object.
116
if sys.platform == 'win32':
117
# Win32 doesn't support preexec_fn, but that is
118
# okay, because it doesn't support LANG either.
121
preexec_fn = _set_lang_C
122
stderr = subprocess.PIPE
128
pipe = subprocess.Popen(diffcmd,
129
stdin=subprocess.PIPE,
130
stdout=subprocess.PIPE,
132
preexec_fn=preexec_fn)
134
if e.errno == errno.ENOENT:
135
raise errors.NoDiff(str(e))
91
141
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
93
143
"""Display a diff by calling out to the external diff program."""
148
198
diffcmd.extend(diff_opts)
151
pipe = subprocess.Popen(diffcmd,
152
stdin=subprocess.PIPE,
153
stdout=subprocess.PIPE)
155
if e.errno == errno.ENOENT:
156
raise errors.NoDiff(str(e))
160
first_line = pipe.stdout.readline()
161
to_file.write(first_line)
162
bzrlib.osutils.pumpfile(pipe.stdout, to_file)
200
pipe = _spawn_external_diff(diffcmd, capture_errors=True)
201
out,err = pipe.communicate()
204
# internal_diff() adds a trailing newline, add one here for consistency
166
207
# 'diff' gives retcode == 2 for all sorts of errors
167
208
# one of those is 'Binary files differ'.
168
209
# Bad options could also be the problem.
169
# 'Binary files' is not a real error, so we suppress that error
170
m = re.match('^binary files.*differ$', first_line, re.I)
172
raise BzrError('external diff failed with exit code 2;'
173
' command: %r' % (diffcmd,))
174
elif rc not in (0, 1):
210
# 'Binary files' is not a real error, so we suppress that error.
213
# Since we got here, we want to make sure to give an i18n error
214
pipe = _spawn_external_diff(diffcmd, capture_errors=False)
215
out, err = pipe.communicate()
217
# Write out the new i18n diff response
218
to_file.write(out+'\n')
219
if pipe.returncode != 2:
220
raise errors.BzrError(
221
'external diff failed with exit code 2'
222
' when run with LANG=C, but not when run'
223
' natively: %r' % (diffcmd,))
225
first_line = lang_c_out.split('\n', 1)[0]
226
# Starting with diffutils 2.8.4 the word "binary" was dropped.
227
m = re.match('^(binary )?files.*differ$', first_line, re.I)
229
raise errors.BzrError('external diff failed with exit code 2;'
230
' command: %r' % (diffcmd,))
232
# Binary files differ, just return
235
# If we got to here, we haven't written out the output of diff
175
239
# returns 1 if files differ; that's OK
177
241
msg = 'signal %d' % (-rc)
179
243
msg = 'exit code %d' % rc
181
raise BzrError('external diff failed with %s; command: %r'
245
raise errors.BzrError('external diff failed with %s; command: %r'
184
# internal_diff() adds a trailing newline, add one here for consistency
188
250
oldtmpf.close() # and delete