~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-31 16:12:57 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060731161257-91a231523255332c
new official bzr.ico

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import errno
18
18
import os
19
 
import re
20
19
import subprocess
21
20
import sys
22
21
import tempfile
23
22
import time
24
23
 
25
 
from bzrlib import (
26
 
    errors,
27
 
    osutils,
28
 
    )
29
24
# compatability - plugins import compare_trees from diff!!!
30
25
# deprecated as of 0.10
31
26
from bzrlib.delta import compare_trees
32
27
from bzrlib.errors import BzrError
 
28
import bzrlib.errors as errors
 
29
import bzrlib.osutils
33
30
from bzrlib.patiencediff import unified_diff
34
31
import bzrlib.patiencediff
35
32
from bzrlib.symbol_versioning import (deprecated_function,
90
87
    print >>to_file
91
88
 
92
89
 
93
 
def _set_lang_C():
94
 
    """Set the env var LANG=C"""
95
 
    osutils.set_or_unset_env('LANG', 'C')
96
 
    osutils.set_or_unset_env('LC_ALL', None)
97
 
    osutils.set_or_unset_env('LC_CTYPE', None)
98
 
    osutils.set_or_unset_env('LANGUAGE', None)
99
 
 
100
 
 
101
 
def _spawn_external_diff(diffcmd, capture_errors=True):
102
 
    """Spawn the externall diff process, and return the child handle.
103
 
 
104
 
    :param diffcmd: The command list to spawn
105
 
    :param capture_errors: Capture stderr as well as setting LANG=C.
106
 
        This lets us read and understand the output of diff, and respond 
107
 
        to any errors.
108
 
    :return: A Popen object.
109
 
    """
110
 
    if capture_errors:
111
 
        if sys.platform == 'win32':
112
 
            # Win32 doesn't support preexec_fn, but that is
113
 
            # okay, because it doesn't support LANG either.
114
 
            preexec_fn = None
115
 
        else:
116
 
            preexec_fn = _set_lang_C
117
 
        stderr = subprocess.PIPE
118
 
    else:
119
 
        preexec_fn = None
120
 
        stderr = None
121
 
 
122
 
    try:
123
 
        pipe = subprocess.Popen(diffcmd,
124
 
                                stdin=subprocess.PIPE,
125
 
                                stdout=subprocess.PIPE,
126
 
                                stderr=stderr,
127
 
                                preexec_fn=preexec_fn)
128
 
    except OSError, e:
129
 
        if e.errno == errno.ENOENT:
130
 
            raise errors.NoDiff(str(e))
131
 
        raise
132
 
 
133
 
    return pipe
134
 
 
135
 
 
136
90
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
137
91
                  diff_opts):
138
92
    """Display a diff by calling out to the external diff program."""
 
93
    if hasattr(to_file, 'fileno'):
 
94
        out_file = to_file
 
95
        have_fileno = True
 
96
    else:
 
97
        out_file = subprocess.PIPE
 
98
        have_fileno = False
 
99
    
139
100
    # make sure our own output is properly ordered before the diff
140
101
    to_file.flush()
141
102
 
192
153
        if diff_opts:
193
154
            diffcmd.extend(diff_opts)
194
155
 
195
 
        pipe = _spawn_external_diff(diffcmd, capture_errors=True)
196
 
        out,err = pipe.communicate()
197
 
        rc = pipe.returncode
 
156
        try:
 
157
            pipe = subprocess.Popen(diffcmd,
 
158
                                    stdin=subprocess.PIPE,
 
159
                                    stdout=out_file)
 
160
        except OSError, e:
 
161
            if e.errno == errno.ENOENT:
 
162
                raise errors.NoDiff(str(e))
 
163
            raise
 
164
        pipe.stdin.close()
 
165
 
 
166
        if not have_fileno:
 
167
            bzrlib.osutils.pumpfile(pipe.stdout, to_file)
 
168
        rc = pipe.wait()
198
169
        
199
 
        # internal_diff() adds a trailing newline, add one here for consistency
200
 
        out += '\n'
201
 
        if rc == 2:
202
 
            # 'diff' gives retcode == 2 for all sorts of errors
203
 
            # one of those is 'Binary files differ'.
204
 
            # Bad options could also be the problem.
205
 
            # 'Binary files' is not a real error, so we suppress that error.
206
 
            lang_c_out = out
207
 
 
208
 
            # Since we got here, we want to make sure to give an i18n error
209
 
            pipe = _spawn_external_diff(diffcmd, capture_errors=False)
210
 
            out, err = pipe.communicate()
211
 
 
212
 
            # Write out the new i18n diff response
213
 
            to_file.write(out+'\n')
214
 
            if pipe.returncode != 2:
215
 
                raise BzrError('external diff failed with exit code 2'
216
 
                               ' when run with LANG=C, but not when run'
217
 
                               ' natively: %r' % (diffcmd,))
218
 
 
219
 
            first_line = lang_c_out.split('\n', 1)[0]
220
 
            # Starting with diffutils 2.8.4 the word "binary" was dropped.
221
 
            m = re.match('^(binary )?files.*differ$', first_line, re.I)
222
 
            if m is None:
223
 
                raise BzrError('external diff failed with exit code 2;'
224
 
                               ' command: %r' % (diffcmd,))
225
 
            else:
226
 
                # Binary files differ, just return
227
 
                return
228
 
 
229
 
        # If we got to here, we haven't written out the output of diff
230
 
        # do so now
231
 
        to_file.write(out)
232
 
        if rc not in (0, 1):
 
170
        if rc != 0 and rc != 1:
233
171
            # returns 1 if files differ; that's OK
234
172
            if rc < 0:
235
173
                msg = 'signal %d' % (-rc)
236
174
            else:
237
175
                msg = 'exit code %d' % rc
238
176
                
239
 
            raise BzrError('external diff failed with %s; command: %r' 
240
 
                           % (rc, diffcmd))
241
 
 
242
 
 
 
177
            raise BzrError('external diff failed with %s; command: %r' % (rc, diffcmd))
243
178
    finally:
244
179
        oldtmpf.close()                 # and delete
245
180
        newtmpf.close()