~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-14 16:16:53 UTC
  • mto: (1946.2.6 reduce-knit-churn)
  • mto: This revision was merged to the branch mainline in revision 1919.
  • Revision ID: john@arbash-meinel.com-20060814161653-54cdcdadcd4e9003
Remove bogus entry from BRANCH.TODO

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
16
16
 
 
17
import errno
17
18
import os
18
19
import re
 
20
import subprocess
19
21
import sys
20
 
 
21
 
from bzrlib.lazy_import import lazy_import
22
 
lazy_import(globals(), """
23
 
import errno
24
 
import subprocess
25
22
import tempfile
26
23
import time
27
24
 
28
 
from bzrlib import (
29
 
    errors,
30
 
    osutils,
31
 
    patiencediff,
32
 
    textfile,
33
 
    )
34
 
""")
35
 
 
36
25
# compatability - plugins import compare_trees from diff!!!
37
26
# deprecated as of 0.10
38
27
from bzrlib.delta import compare_trees
39
 
from bzrlib.symbol_versioning import (
40
 
        deprecated_function,
41
 
        zero_eight,
42
 
        )
 
28
from bzrlib.errors import BzrError
 
29
import bzrlib.errors as errors
 
30
import bzrlib.osutils
 
31
from bzrlib.patiencediff import unified_diff
 
32
import bzrlib.patiencediff
 
33
from bzrlib.symbol_versioning import (deprecated_function,
 
34
        zero_eight)
 
35
from bzrlib.textfile import check_text_lines
43
36
from bzrlib.trace import mutter, warning
44
37
 
45
38
 
67
60
        return
68
61
    
69
62
    if allow_binary is False:
70
 
        textfile.check_text_lines(oldlines)
71
 
        textfile.check_text_lines(newlines)
 
63
        check_text_lines(oldlines)
 
64
        check_text_lines(newlines)
72
65
 
73
66
    if sequence_matcher is None:
74
 
        sequence_matcher = patiencediff.PatienceSequenceMatcher
75
 
    ud = patiencediff.unified_diff(oldlines, newlines,
 
67
        sequence_matcher = bzrlib.patiencediff.PatienceSequenceMatcher
 
68
    ud = unified_diff(oldlines, newlines,
76
69
                      fromfile=old_filename.encode(path_encoding),
77
70
                      tofile=new_filename.encode(path_encoding),
78
71
                      sequencematcher=sequence_matcher)
95
88
    print >>to_file
96
89
 
97
90
 
98
 
def _set_lang_C():
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)
104
 
 
105
 
 
106
 
def _spawn_external_diff(diffcmd, capture_errors=True):
107
 
    """Spawn the externall diff process, and return the child handle.
108
 
 
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 
112
 
        to any errors.
113
 
    :return: A Popen object.
114
 
    """
115
 
    if capture_errors:
116
 
        if sys.platform == 'win32':
117
 
            # Win32 doesn't support preexec_fn, but that is
118
 
            # okay, because it doesn't support LANG either.
119
 
            preexec_fn = None
120
 
        else:
121
 
            preexec_fn = _set_lang_C
122
 
        stderr = subprocess.PIPE
123
 
    else:
124
 
        preexec_fn = None
125
 
        stderr = None
126
 
 
127
 
    try:
128
 
        pipe = subprocess.Popen(diffcmd,
129
 
                                stdin=subprocess.PIPE,
130
 
                                stdout=subprocess.PIPE,
131
 
                                stderr=stderr,
132
 
                                preexec_fn=preexec_fn)
133
 
    except OSError, e:
134
 
        if e.errno == errno.ENOENT:
135
 
            raise errors.NoDiff(str(e))
136
 
        raise
137
 
 
138
 
    return pipe
139
 
 
140
 
 
141
91
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
142
92
                  diff_opts):
143
93
    """Display a diff by calling out to the external diff program."""
197
147
        if diff_opts:
198
148
            diffcmd.extend(diff_opts)
199
149
 
200
 
        pipe = _spawn_external_diff(diffcmd, capture_errors=True)
201
 
        out,err = pipe.communicate()
202
 
        rc = pipe.returncode
 
150
        try:
 
151
            pipe = subprocess.Popen(diffcmd,
 
152
                                    stdin=subprocess.PIPE,
 
153
                                    stdout=subprocess.PIPE)
 
154
        except OSError, e:
 
155
            if e.errno == errno.ENOENT:
 
156
                raise errors.NoDiff(str(e))
 
157
            raise
 
158
        pipe.stdin.close()
 
159
 
 
160
        first_line = pipe.stdout.readline()
 
161
        to_file.write(first_line)
 
162
        bzrlib.osutils.pumpfile(pipe.stdout, to_file)
 
163
        rc = pipe.wait()
203
164
        
204
 
        # internal_diff() adds a trailing newline, add one here for consistency
205
 
        out += '\n'
206
165
        if rc == 2:
207
166
            # 'diff' gives retcode == 2 for all sorts of errors
208
167
            # one of those is 'Binary files differ'.
209
168
            # Bad options could also be the problem.
210
 
            # 'Binary files' is not a real error, so we suppress that error.
211
 
            lang_c_out = out
212
 
 
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()
216
 
 
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,))
224
 
 
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)
228
 
            if m is None:
229
 
                raise errors.BzrError('external diff failed with exit code 2;'
230
 
                                      ' command: %r' % (diffcmd,))
231
 
            else:
232
 
                # Binary files differ, just return
233
 
                return
234
 
 
235
 
        # If we got to here, we haven't written out the output of diff
236
 
        # do so now
237
 
        to_file.write(out)
238
 
        if rc not in (0, 1):
 
169
            # 'Binary files' is not a real error, so we suppress that error
 
170
            m = re.match('^binary files.*differ$', first_line, re.I)
 
171
            if not m:
 
172
                raise BzrError('external diff failed with exit code 2;'
 
173
                               ' command: %r' % (diffcmd,))
 
174
        elif rc not in (0, 1):
239
175
            # returns 1 if files differ; that's OK
240
176
            if rc < 0:
241
177
                msg = 'signal %d' % (-rc)
242
178
            else:
243
179
                msg = 'exit code %d' % rc
244
180
                
245
 
            raise errors.BzrError('external diff failed with %s; command: %r' 
246
 
                                  % (rc, diffcmd))
 
181
            raise BzrError('external diff failed with %s; command: %r' 
 
182
                           % (rc, diffcmd))
247
183
 
 
184
        # internal_diff() adds a trailing newline, add one here for consistency
 
185
        to_file.write('\n')
248
186
 
249
187
    finally:
250
188
        oldtmpf.close()                 # and delete