~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: John Arbash Meinel
  • Date: 2006-08-16 22:00:19 UTC
  • mto: This revision was merged to the branch mainline in revision 1942.
  • Revision ID: john@arbash-meinel.com-20060816220019-541cb90093258ac3
Using real utf8 and cache_utf8 has similar performance, 272ms, and 363ms

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
19
20
import subprocess
20
21
import sys
21
22
import tempfile
22
23
import time
23
24
 
 
25
# compatability - plugins import compare_trees from diff!!!
 
26
# deprecated as of 0.10
24
27
from bzrlib.delta import compare_trees
25
28
from bzrlib.errors import BzrError
26
29
import bzrlib.errors as errors
85
88
    print >>to_file
86
89
 
87
90
 
 
91
def _set_lang_C():
 
92
    """Set the env var LANG=C"""
 
93
    os.environ['LANG'] = 'C'
 
94
 
 
95
 
 
96
def _spawn_external_diff(diffcmd, capture_errors=True):
 
97
    """Spawn the externall diff process, and return the child handle.
 
98
 
 
99
    :param diffcmd: The command list to spawn
 
100
    :param capture_errors: Capture stderr as well as setting LANG=C.
 
101
        This lets us read and understand the output of diff, and respond 
 
102
        to any errors.
 
103
    :return: A Popen object.
 
104
    """
 
105
    if capture_errors:
 
106
        preexec_fn = _set_lang_C
 
107
        stderr = subprocess.PIPE
 
108
    else:
 
109
        preexec_fn = None
 
110
        stderr = None
 
111
 
 
112
    try:
 
113
        pipe = subprocess.Popen(diffcmd,
 
114
                                stdin=subprocess.PIPE,
 
115
                                stdout=subprocess.PIPE,
 
116
                                stderr=stderr,
 
117
                                preexec_fn=preexec_fn)
 
118
    except OSError, e:
 
119
        if e.errno == errno.ENOENT:
 
120
            raise errors.NoDiff(str(e))
 
121
        raise
 
122
 
 
123
    return pipe
 
124
 
 
125
 
88
126
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
89
127
                  diff_opts):
90
128
    """Display a diff by calling out to the external diff program."""
91
 
    if hasattr(to_file, 'fileno'):
92
 
        out_file = to_file
93
 
        have_fileno = True
94
 
    else:
95
 
        out_file = subprocess.PIPE
96
 
        have_fileno = False
97
 
    
98
129
    # make sure our own output is properly ordered before the diff
99
130
    to_file.flush()
100
131
 
151
182
        if diff_opts:
152
183
            diffcmd.extend(diff_opts)
153
184
 
154
 
        try:
155
 
            pipe = subprocess.Popen(diffcmd,
156
 
                                    stdin=subprocess.PIPE,
157
 
                                    stdout=out_file)
158
 
        except OSError, e:
159
 
            if e.errno == errno.ENOENT:
160
 
                raise errors.NoDiff(str(e))
161
 
            raise
162
 
        pipe.stdin.close()
163
 
 
164
 
        if not have_fileno:
165
 
            bzrlib.osutils.pumpfile(pipe.stdout, to_file)
166
 
        rc = pipe.wait()
 
185
        pipe = _spawn_external_diff(diffcmd, capture_errors=True)
 
186
        out,err = pipe.communicate()
 
187
        rc = pipe.returncode
167
188
        
168
 
        if rc != 0 and rc != 1:
 
189
        # internal_diff() adds a trailing newline, add one here for consistency
 
190
        out += '\n'
 
191
        if rc == 2:
 
192
            # 'diff' gives retcode == 2 for all sorts of errors
 
193
            # one of those is 'Binary files differ'.
 
194
            # Bad options could also be the problem.
 
195
            # 'Binary files' is not a real error, so we suppress that error
 
196
            lang_c_out = out
 
197
 
 
198
            # Since we got here, we want to make sure to give an i18n error
 
199
            pipe = _spawn_external_diff(diffcmd, capture_errors=False)
 
200
            out, err = pipe.communicate()
 
201
 
 
202
            # Write out the new i18n diff response
 
203
            to_file.write(out+'\n')
 
204
            if pipe.returncode != 2:
 
205
                raise BzrError('external diff failed with exit code 2'
 
206
                               ' when run with LANG=C, but not when run'
 
207
                               ' natively: %r' % (diffcmd,))
 
208
 
 
209
            first_line = lang_c_out.split('\n', 1)[0]
 
210
            m = re.match('^binary files.*differ$', first_line, re.I)
 
211
            if m is None:
 
212
                raise BzrError('external diff failed with exit code 2;'
 
213
                               ' command: %r' % (diffcmd,))
 
214
            else:
 
215
                # Binary files differ, just return
 
216
                return
 
217
 
 
218
        # If we got to here, we haven't written out the output of diff
 
219
        # do so now
 
220
        to_file.write(out)
 
221
        if rc not in (0, 1):
169
222
            # returns 1 if files differ; that's OK
170
223
            if rc < 0:
171
224
                msg = 'signal %d' % (-rc)
172
225
            else:
173
226
                msg = 'exit code %d' % rc
174
227
                
175
 
            raise BzrError('external diff failed with %s; command: %r' % (rc, diffcmd))
 
228
            raise BzrError('external diff failed with %s; command: %r' 
 
229
                           % (rc, diffcmd))
 
230
 
 
231
 
176
232
    finally:
177
233
        oldtmpf.close()                 # and delete
178
234
        newtmpf.close()
332
388
    else:
333
389
        diff_file = internal_diff
334
390
    
335
 
    delta = compare_trees(old_tree, new_tree, want_unchanged=False,
336
 
                          specific_files=specific_files, 
337
 
                          extra_trees=extra_trees, require_versioned=True)
 
391
    delta = new_tree.changes_from(old_tree,
 
392
        specific_files=specific_files,
 
393
        extra_trees=extra_trees, require_versioned=True)
338
394
 
339
395
    has_changes = 0
340
396
    for path, file_id, kind in delta.removed: