90
91
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
92
93
"""Display a diff by calling out to the external diff program."""
93
fileno_func = getattr(to_file, 'fileno', None)
94
if fileno_func is not None:
95
fileno = fileno_func()
96
# WORKAROUND: jam 20060731 python2.4 subprocess.py will
97
# close too many file descriptors if you pass stdout=sys.stdout
98
# so if we are about to pass stdout, just pass None
105
out_file = subprocess.PIPE
108
94
# make sure our own output is properly ordered before the diff
165
151
pipe = subprocess.Popen(diffcmd,
166
152
stdin=subprocess.PIPE,
153
stdout=subprocess.PIPE)
168
154
except OSError, e:
169
155
if e.errno == errno.ENOENT:
170
156
raise errors.NoDiff(str(e))
172
158
pipe.stdin.close()
175
bzrlib.osutils.pumpfile(pipe.stdout, to_file)
160
first_line = pipe.stdout.readline()
161
to_file.write(first_line)
162
bzrlib.osutils.pumpfile(pipe.stdout, to_file)
178
# TODO: jam 20060731 'diff' returns exit code '2' if binary
179
# files differ. But it also returns '2' if a file you
180
# are comparing doesn't exist, etc.
182
# "An exit status of 0 means no differences were found,
183
# 1 means some differences were found, and 2 means trouble."
184
# If we assume we do things correctly, we can assume '2' means
185
# 'Binary files .* differ'
186
# Alternatively, we could read the output of diff, and look
187
# for the "Binary files .* differ" line, but that would mean
188
# we always need to buffer the diff output.
189
if rc not in (0, 1, 2):
166
# 'diff' gives retcode == 2 for all sorts of errors
167
# one of those is 'Binary files differ'.
168
# 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):
190
175
# returns 1 if files differ; that's OK
192
177
msg = 'signal %d' % (-rc)