/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/diff.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2014 Canonical Ltd.
 
1
# Copyright (C) 2005-2011 Canonical Ltd.
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
49
49
    )
50
50
from bzrlib.trace import mutter, note, warning
51
51
 
52
 
DEFAULT_CONTEXT_AMOUNT = 3
53
52
 
54
53
class AtTemplate(string.Template):
55
54
    """Templating class that uses @ instead of $."""
73
72
 
74
73
def internal_diff(old_filename, oldlines, new_filename, newlines, to_file,
75
74
                  allow_binary=False, sequence_matcher=None,
76
 
                  path_encoding='utf8', context_lines=DEFAULT_CONTEXT_AMOUNT):
 
75
                  path_encoding='utf8'):
77
76
    # FIXME: difflib is wrong if there is no trailing newline.
78
77
    # The syntax used by patch seems to be "\ No newline at
79
78
    # end of file" following the last diff line from that
99
98
    ud = patiencediff.unified_diff(oldlines, newlines,
100
99
                      fromfile=old_filename.encode(path_encoding, 'replace'),
101
100
                      tofile=new_filename.encode(path_encoding, 'replace'),
102
 
                      n=context_lines, sequencematcher=sequence_matcher)
 
101
                      sequencematcher=sequence_matcher)
103
102
 
104
103
    ud = list(ud)
105
104
    if len(ud) == 0: # Identical contents, nothing to do
119
118
 
120
119
 
121
120
def _spawn_external_diff(diffcmd, capture_errors=True):
122
 
    """Spawn the external diff process, and return the child handle.
 
121
    """Spawn the externall diff process, and return the child handle.
123
122
 
124
123
    :param diffcmd: The command list to spawn
125
124
    :param capture_errors: Capture stderr as well as setting LANG=C
154
153
 
155
154
    return pipe
156
155
 
157
 
# diff style options as of GNU diff v3.2
158
 
style_option_list = ['-c', '-C', '--context',
159
 
                     '-e', '--ed',
160
 
                     '-f', '--forward-ed',
161
 
                     '-q', '--brief',
162
 
                     '--normal',
163
 
                     '-n', '--rcs',
164
 
                     '-u', '-U', '--unified',
165
 
                     '-y', '--side-by-side',
166
 
                     '-D', '--ifdef']
167
 
 
168
 
def default_style_unified(diff_opts):
169
 
    """Default to unified diff style if alternative not specified in diff_opts.
170
 
 
171
 
        diff only allows one style to be specified; they don't override.
172
 
        Note that some of these take optargs, and the optargs can be
173
 
        directly appended to the options.
174
 
        This is only an approximate parser; it doesn't properly understand
175
 
        the grammar.
176
 
 
177
 
    :param diff_opts: List of options for external (GNU) diff.
178
 
    :return: List of options with default style=='unified'.
179
 
    """
180
 
    for s in style_option_list:
181
 
        for j in diff_opts:
182
 
            if j.startswith(s):
183
 
                break
184
 
        else:
185
 
            continue
186
 
        break
187
 
    else:
188
 
        diff_opts.append('-u')
189
 
    return diff_opts
190
 
 
191
156
 
192
157
def external_diff(old_filename, oldlines, new_filename, newlines, to_file,
193
158
                  diff_opts):
229
194
                   '--binary',
230
195
                  ]
231
196
 
232
 
        diff_opts = default_style_unified(diff_opts)
 
197
        # diff only allows one style to be specified; they don't override.
 
198
        # note that some of these take optargs, and the optargs can be
 
199
        # directly appended to the options.
 
200
        # this is only an approximate parser; it doesn't properly understand
 
201
        # the grammar.
 
202
        for s in ['-c', '-u', '-C', '-U',
 
203
                  '-e', '--ed',
 
204
                  '-q', '--brief',
 
205
                  '--normal',
 
206
                  '-n', '--rcs',
 
207
                  '-y', '--side-by-side',
 
208
                  '-D', '--ifdef']:
 
209
            for j in diff_opts:
 
210
                if j.startswith(s):
 
211
                    break
 
212
            else:
 
213
                continue
 
214
            break
 
215
        else:
 
216
            diffcmd.append('-u')
233
217
 
234
218
        if diff_opts:
235
219
            diffcmd.extend(diff_opts)
280
264
                msg = 'exit code %d' % rc
281
265
 
282
266
            raise errors.BzrError('external diff failed with %s; command: %r'
283
 
                                  % (msg, diffcmd))
 
267
                                  % (rc, diffcmd))
284
268
 
285
269
 
286
270
    finally:
287
271
        oldtmpf.close()                 # and delete
288
272
        newtmpf.close()
289
 
 
290
 
        def cleanup(path):
291
 
            # Warn in case the file couldn't be deleted (in case windows still
292
 
            # holds the file open, but not if the files have already been
293
 
            # deleted)
294
 
            try:
295
 
                os.remove(path)
296
 
            except OSError, e:
297
 
                if e.errno not in (errno.ENOENT,):
298
 
                    warning('Failed to delete temporary file: %s %s', path, e)
299
 
 
300
 
        cleanup(old_abspath)
301
 
        cleanup(new_abspath)
 
273
        # Clean up. Warn in case the files couldn't be deleted
 
274
        # (in case windows still holds the file open, but not
 
275
        # if the files have already been deleted)
 
276
        try:
 
277
            os.remove(old_abspath)
 
278
        except OSError, e:
 
279
            if e.errno not in (errno.ENOENT,):
 
280
                warning('Failed to delete temporary file: %s %s',
 
281
                        old_abspath, e)
 
282
        try:
 
283
            os.remove(new_abspath)
 
284
        except OSError:
 
285
            if e.errno not in (errno.ENOENT,):
 
286
                warning('Failed to delete temporary file: %s %s',
 
287
                        new_abspath, e)
302
288
 
303
289
 
304
290
def get_trees_and_branches_to_diff_locked(
440
426
                    extra_trees=None,
441
427
                    path_encoding='utf8',
442
428
                    using=None,
443
 
                    format_cls=None,
444
 
                    context=DEFAULT_CONTEXT_AMOUNT):
 
429
                    format_cls=None):
445
430
    """Show in text form the changes from one tree to another.
446
431
 
447
432
    :param to_file: The output stream.
454
439
        otherwise is supposed to be utf8
455
440
    :param format_cls: Formatter class (DiffTree subclass)
456
441
    """
457
 
    if context is None:
458
 
        context = DEFAULT_CONTEXT_AMOUNT
459
442
    if format_cls is None:
460
443
        format_cls = DiffTree
461
444
    old_tree.lock_read()
468
451
            differ = format_cls.from_trees_options(old_tree, new_tree, to_file,
469
452
                                                   path_encoding,
470
453
                                                   external_diff_options,
471
 
                                                   old_label, new_label, using,
472
 
                                                   context_lines=context)
 
454
                                                   old_label, new_label, using)
473
455
            return differ.show_diff(specific_files, extra_trees)
474
456
        finally:
475
457
            new_tree.unlock()
633
615
    # or removed in a diff.
634
616
    EPOCH_DATE = '1970-01-01 00:00:00 +0000'
635
617
 
636
 
    def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8', 
637
 
                 old_label='', new_label='', text_differ=internal_diff, 
638
 
                 context_lines=DEFAULT_CONTEXT_AMOUNT):
 
618
    def __init__(self, old_tree, new_tree, to_file, path_encoding='utf-8',
 
619
                 old_label='', new_label='', text_differ=internal_diff):
639
620
        DiffPath.__init__(self, old_tree, new_tree, to_file, path_encoding)
640
621
        self.text_differ = text_differ
641
622
        self.old_label = old_label
642
623
        self.new_label = new_label
643
624
        self.path_encoding = path_encoding
644
 
        self.context_lines = context_lines
645
625
 
646
626
    def diff(self, file_id, old_path, new_path, old_kind, new_kind):
647
627
        """Compare two files in unified diff format
695
675
            from_text = _get_text(self.old_tree, from_file_id, from_path)
696
676
            to_text = _get_text(self.new_tree, to_file_id, to_path)
697
677
            self.text_differ(from_label, from_text, to_label, to_text,
698
 
                             self.to_file, path_encoding=self.path_encoding,
699
 
                             context_lines=self.context_lines)
 
678
                             self.to_file, path_encoding=self.path_encoding)
700
679
        except errors.BinaryFile:
701
680
            self.to_file.write(
702
681
                  ("Binary files %s and %s differ\n" %
926
905
    @classmethod
927
906
    def from_trees_options(klass, old_tree, new_tree, to_file,
928
907
                           path_encoding, external_diff_options, old_label,
929
 
                           new_label, using, context_lines):
 
908
                           new_label, using):
930
909
        """Factory for producing a DiffTree.
931
910
 
932
911
        Designed to accept options used by show_diff_trees.
947
926
            extra_factories = []
948
927
        if external_diff_options:
949
928
            opts = external_diff_options.split()
950
 
            def diff_file(olab, olines, nlab, nlines, to_file, path_encoding=None, context_lines=None):
 
929
            def diff_file(olab, olines, nlab, nlines, to_file, path_encoding=None):
951
930
                """:param path_encoding: not used but required
952
931
                        to match the signature of internal_diff.
953
932
                """
955
934
        else:
956
935
            diff_file = internal_diff
957
936
        diff_text = DiffText(old_tree, new_tree, to_file, path_encoding,
958
 
                             old_label, new_label, diff_file, context_lines=context_lines)
 
937
                             old_label, new_label, diff_file)
959
938
        return klass(old_tree, new_tree, to_file, path_encoding, diff_text,
960
939
                     extra_factories)
961
940