/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to diff.py

  • Committer: Curtis Hovey
  • Date: 2011-07-31 15:52:43 UTC
  • mto: This revision was merged to the branch mainline in revision 741.
  • Revision ID: sinzui.is@verizon.net-20110731155243-ln8istmxbryhb4pz
Mechanical changes made by pygi.convert.sh.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: UTF-8 -*-
2
1
"""Difference window.
3
2
 
4
3
This module contains the code to manage the diff window which shows
5
4
the changes made between two revisions on a branch.
6
5
"""
7
6
 
8
 
__copyright__ = "Copyright © 2005 Canonical Ltd."
 
7
__copyright__ = "Copyright 2005 Canonical Ltd."
9
8
__author__    = "Scott James Remnant <scott@ubuntu.com>"
10
9
 
11
10
 
13
12
 
14
13
import pygtk
15
14
pygtk.require("2.0")
16
 
import gtk
17
 
import pango
 
15
from gi.repository import Gtk
 
16
from gi.repository import Pango
18
17
import os
19
18
import re
20
19
import sys
 
20
import inspect
 
21
try:
 
22
    from xml.etree.ElementTree import Element, SubElement, tostring
 
23
except ImportError:
 
24
    from elementtree.ElementTree import Element, SubElement, tostring
21
25
 
22
26
try:
23
 
    import gtksourceview
 
27
    from gi.repository import GtkSource
24
28
    have_gtksourceview = True
25
29
except ImportError:
26
30
    have_gtksourceview = False
27
31
try:
28
 
    import gconf
 
32
    from gi.repository import GConf
29
33
    have_gconf = True
30
34
except ImportError:
31
35
    have_gconf = False
32
36
 
33
37
from bzrlib import (
 
38
    errors,
34
39
    merge as _mod_merge,
35
40
    osutils,
36
 
    progress,
37
41
    urlutils,
38
42
    workingtree,
39
43
)
40
 
from bzrlib.diff import show_diff_trees, internal_diff
41
 
from bzrlib.errors import NoSuchFile
 
44
from bzrlib.diff import show_diff_trees
42
45
from bzrlib.patches import parse_patches
43
46
from bzrlib.trace import warning
44
 
from bzrlib.plugins.gtk import _i18n
 
47
from bzrlib.plugins.gtk.dialog import (
 
48
    error_dialog,
 
49
    info_dialog,
 
50
    warning_dialog,
 
51
    )
 
52
from bzrlib.plugins.gtk.i18n import _i18n
45
53
from bzrlib.plugins.gtk.window import Window
46
 
from dialog import error_dialog, info_dialog, warning_dialog
 
54
 
 
55
 
 
56
def fallback_guess_language(slm, content_type):
 
57
    for lang_id in slm.get_language_ids():
 
58
        lang = slm.get_language(lang_id)
 
59
        if "text/x-patch" in lang.get_mime_types():
 
60
            return lang
 
61
    return None
47
62
 
48
63
 
49
64
class SelectCancelled(Exception):
51
66
    pass
52
67
 
53
68
 
54
 
class DiffFileView(gtk.ScrolledWindow):
 
69
class DiffFileView(Gtk.ScrolledWindow):
55
70
    """Window for displaying diffs from a diff file"""
56
71
 
57
72
    def __init__(self):
58
 
        gtk.ScrolledWindow.__init__(self)
 
73
        GObject.GObject.__init__(self)
59
74
        self.construct()
60
75
        self._diffs = {}
61
76
 
62
77
    def construct(self):
63
 
        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
64
 
        self.set_shadow_type(gtk.SHADOW_IN)
 
78
        self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
 
79
        self.set_shadow_type(Gtk.ShadowType.IN)
65
80
 
66
81
        if have_gtksourceview:
67
 
            self.buffer = gtksourceview.SourceBuffer()
68
 
            slm = gtksourceview.SourceLanguagesManager()
69
 
            gsl = slm.get_language_from_mime_type("text/x-patch")
 
82
            self.buffer = GtkSource.Buffer()
 
83
            slm = GtkSource.LanguageManager()
 
84
            guess_language = getattr(GtkSource.LanguageManager, 
 
85
                "guess_language", fallback_guess_language)
 
86
            gsl = guess_language(slm, content_type="text/x-patch")
70
87
            if have_gconf:
71
 
                self.apply_gedit_colors(gsl)
72
 
            self.apply_colordiff_colors(gsl)
 
88
                self.apply_gedit_colors(self.buffer)
 
89
            self.apply_colordiff_colors(self.buffer)
73
90
            self.buffer.set_language(gsl)
74
 
            self.buffer.set_highlight(True)
 
91
            self.buffer.set_highlight_syntax(True)
75
92
 
76
 
            sourceview = gtksourceview.SourceView(self.buffer)
 
93
            self.sourceview = GtkSource.View(self.buffer)
77
94
        else:
78
 
            self.buffer = gtk.TextBuffer()
79
 
            sourceview = gtk.TextView(self.buffer)
 
95
            self.buffer = Gtk.TextBuffer()
 
96
            self.sourceview = Gtk.TextView(self.buffer)
80
97
 
81
 
        sourceview.set_editable(False)
82
 
        sourceview.modify_font(pango.FontDescription("Monospace"))
83
 
        self.add(sourceview)
84
 
        sourceview.show()
 
98
        self.sourceview.set_editable(False)
 
99
        self.sourceview.modify_font(Pango.FontDescription("Monospace"))
 
100
        self.add(self.sourceview)
 
101
        self.sourceview.show()
85
102
 
86
103
    @staticmethod
87
 
    def apply_gedit_colors(lang):
88
 
        """Set style for lang to that specified in gedit configuration.
 
104
    def apply_gedit_colors(buf):
 
105
        """Set style to that specified in gedit configuration.
89
106
 
90
107
        This method needs the gconf module.
91
108
 
92
 
        :param lang: a gtksourceview.SourceLanguage object.
 
109
        :param buf: a GtkSource.Buffer object.
93
110
        """
94
 
        GEDIT_SYNTAX_PATH = '/apps/gedit-2/preferences/syntax_highlighting'
95
 
        GEDIT_LANG_PATH = GEDIT_SYNTAX_PATH + '/' + lang.get_id()
96
 
 
97
 
        client = gconf.client_get_default()
98
 
        client.add_dir(GEDIT_LANG_PATH, gconf.CLIENT_PRELOAD_NONE)
99
 
 
100
 
        for tag in lang.get_tags():
101
 
            tag_id = tag.get_id()
102
 
            gconf_key = GEDIT_LANG_PATH + '/' + tag_id
103
 
            style_string = client.get_string(gconf_key)
104
 
 
105
 
            if style_string is None:
106
 
                continue
107
 
 
108
 
            # function to get a bool from a string that's either '0' or '1'
109
 
            string_bool = lambda x: bool(int(x))
110
 
 
111
 
            # style_string is a string like "2/#FFCCAA/#000000/0/1/0/0"
112
 
            # values are: mask, fg, bg, italic, bold, underline, strike
113
 
            # this packs them into (str_value, attr_name, conv_func) tuples
114
 
            items = zip(style_string.split('/'), ['mask', 'foreground',
115
 
                'background', 'italic', 'bold', 'underline', 'strikethrough' ],
116
 
                [ int, gtk.gdk.color_parse, gtk.gdk.color_parse, string_bool,
117
 
                    string_bool, string_bool, string_bool ]
118
 
            )
119
 
 
120
 
            style = gtksourceview.SourceTagStyle()
121
 
 
122
 
            # XXX The mask attribute controls whether the present values of
123
 
            # foreground and background color should in fact be used. Ideally
124
 
            # (and that's what gedit does), one could set all three attributes,
125
 
            # and let the TagStyle object figure out which colors to use.
126
 
            # However, in the GtkSourceview python bindings, the mask attribute
127
 
            # is read-only, and it's derived instead from the colors being
128
 
            # set or not. This means that we have to sometimes refrain from
129
 
            # setting fg or bg colors, depending on the value of the mask.
130
 
            # This code could go away if mask were writable.
131
 
            mask = int(items[0][0])
132
 
            if not (mask & 1): # GTK_SOURCE_TAG_STYLE_USE_BACKGROUND
133
 
                items[2:3] = []
134
 
            if not (mask & 2): # GTK_SOURCE_TAG_STYLE_USE_FOREGROUND
135
 
                items[1:2] = []
136
 
            items[0:1] = [] # skip the mask unconditionally
137
 
 
138
 
            for value, attr, func in items:
139
 
                try:
140
 
                    value = func(value)
141
 
                except ValueError:
142
 
                    warning('gconf key %s contains an invalid value: %s'
143
 
                            % gconf_key, value)
144
 
                else:
145
 
                    setattr(style, attr, value)
146
 
 
147
 
            lang.set_tag_style(tag_id, style)
 
111
        GEDIT_SCHEME_PATH = '/apps/gedit-2/preferences/editor/colors/scheme'
 
112
        GEDIT_USER_STYLES_PATH = os.path.expanduser('~/.gnome2/gedit/styles')
 
113
 
 
114
        client = GConf.Client.get_default()
 
115
        style_scheme_name = client.get_string(GEDIT_SCHEME_PATH)
 
116
        if style_scheme_name is not None:
 
117
            style_scheme_mgr = GtkSource.StyleSchemeManager()
 
118
            style_scheme_mgr.append_search_path(GEDIT_USER_STYLES_PATH)
 
119
            
 
120
            style_scheme = style_scheme_mgr.get_scheme(style_scheme_name)
 
121
            
 
122
            if style_scheme is not None:
 
123
                buf.set_style_scheme(style_scheme)
148
124
 
149
125
    @classmethod
150
 
    def apply_colordiff_colors(klass, lang):
 
126
    def apply_colordiff_colors(klass, buf):
151
127
        """Set style colors for lang using the colordiff configuration file.
152
128
 
153
129
        Both ~/.colordiffrc and ~/.colordiffrc.bzr-gtk are read.
154
130
 
155
 
        :param lang: a "Diff" gtksourceview.SourceLanguage object.
 
131
        :param buf: a "Diff" GtkSource.Buffer object.
156
132
        """
157
 
        colors = {}
158
 
 
159
 
        for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
160
 
            f = os.path.expanduser(f)
161
 
            if os.path.exists(f):
162
 
                try:
163
 
                    f = file(f)
164
 
                except IOError, e:
165
 
                    warning('could not open file %s: %s' % (f, str(e)))
166
 
                else:
167
 
                    colors.update(klass.parse_colordiffrc(f))
168
 
                    f.close()
169
 
 
170
 
        if not colors:
171
 
            # ~/.colordiffrc does not exist
172
 
            return
173
 
 
174
 
        mapping = {
175
 
                # map GtkSourceView tags to colordiff names
 
133
        scheme_manager = GtkSource.StyleSchemeManager()
 
134
        style_scheme = scheme_manager.get_scheme('colordiff')
 
135
        
 
136
        # if style scheme not found, we'll generate it from colordiffrc
 
137
        # TODO: reload if colordiffrc has changed.
 
138
        if style_scheme is None:
 
139
            colors = {}
 
140
 
 
141
            for f in ('~/.colordiffrc', '~/.colordiffrc.bzr-gtk'):
 
142
                f = os.path.expanduser(f)
 
143
                if os.path.exists(f):
 
144
                    try:
 
145
                        f = file(f)
 
146
                    except IOError, e:
 
147
                        warning('could not open file %s: %s' % (f, str(e)))
 
148
                    else:
 
149
                        colors.update(klass.parse_colordiffrc(f))
 
150
                        f.close()
 
151
 
 
152
            if not colors:
 
153
                # ~/.colordiffrc does not exist
 
154
                return
 
155
            
 
156
            mapping = {
 
157
                # map GtkSourceView2 scheme styles to colordiff names
176
158
                # since GSV is richer, accept new names for extra bits,
177
159
                # defaulting to old names if they're not present
178
 
                'Added@32@line': ['newtext'],
179
 
                'Removed@32@line': ['oldtext'],
180
 
                'Location': ['location', 'diffstuff'],
181
 
                'Diff@32@file': ['file', 'diffstuff'],
182
 
                'Special@32@case': ['specialcase', 'diffstuff'],
183
 
        }
184
 
 
185
 
        for tag in lang.get_tags():
186
 
            tag_id = tag.get_id()
187
 
            keys = mapping.get(tag_id, [])
188
 
            color = None
189
 
 
190
 
            for key in keys:
191
 
                color = colors.get(key, None)
192
 
                if color is not None:
193
 
                    break
194
 
 
195
 
            if color is None:
196
 
                continue
197
 
 
198
 
            style = gtksourceview.SourceTagStyle()
199
 
            try:
200
 
                style.foreground = gtk.gdk.color_parse(color)
201
 
            except ValueError:
202
 
                warning('not a valid color: %s' % color)
203
 
            else:
204
 
                lang.set_tag_style(tag_id, style)
 
160
                'diff:added-line': ['newtext'],
 
161
                'diff:removed-line': ['oldtext'],
 
162
                'diff:location': ['location', 'diffstuff'],
 
163
                'diff:file': ['file', 'diffstuff'],
 
164
                'diff:special-case': ['specialcase', 'diffstuff'],
 
165
            }
 
166
            
 
167
            converted_colors = {}
 
168
            for name, values in mapping.items():
 
169
                color = None
 
170
                for value in values:
 
171
                    color = colors.get(value, None)
 
172
                    if color is not None:
 
173
                        break
 
174
                if color is None:
 
175
                    continue
 
176
                converted_colors[name] = color
 
177
            
 
178
            # some xml magic to produce needed style scheme description
 
179
            e_style_scheme = Element('style-scheme')
 
180
            e_style_scheme.set('id', 'colordiff')
 
181
            e_style_scheme.set('_name', 'ColorDiff')
 
182
            e_style_scheme.set('version', '1.0')
 
183
            for name, color in converted_colors.items():
 
184
                style = SubElement(e_style_scheme, 'style')
 
185
                style.set('name', name)
 
186
                style.set('foreground', '#%s' % color)
 
187
            
 
188
            scheme_xml = tostring(e_style_scheme, 'UTF-8')
 
189
            if not os.path.exists(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles')):
 
190
                os.makedirs(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles'))
 
191
            file(os.path.expanduser('~/.local/share/gtksourceview-2.0/styles/colordiff.xml'), 'w').write(scheme_xml)
 
192
            
 
193
            scheme_manager.force_rescan()
 
194
            style_scheme = scheme_manager.get_scheme('colordiff')
 
195
        
 
196
        buf.set_style_scheme(style_scheme)
205
197
 
206
198
    @staticmethod
207
199
    def parse_colordiffrc(fileobj):
292
284
        self.buffer.set_text(decoded.encode('UTF-8'))
293
285
 
294
286
 
295
 
class DiffWidget(gtk.HPaned):
 
287
class DiffWidget(Gtk.HPaned):
296
288
    """Diff widget
297
289
 
298
290
    """
300
292
        super(DiffWidget, self).__init__()
301
293
 
302
294
        # The file hierarchy: a scrollable treeview
303
 
        scrollwin = gtk.ScrolledWindow()
304
 
        scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
305
 
        scrollwin.set_shadow_type(gtk.SHADOW_IN)
 
295
        scrollwin = Gtk.ScrolledWindow()
 
296
        scrollwin.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
 
297
        scrollwin.set_shadow_type(Gtk.ShadowType.IN)
306
298
        self.pack1(scrollwin)
307
299
        scrollwin.show()
308
 
 
309
 
        self.model = gtk.TreeStore(str, str)
310
 
        self.treeview = gtk.TreeView(self.model)
 
300
        
 
301
        self.model = Gtk.TreeStore(str, str)
 
302
        self.treeview = Gtk.TreeView(self.model)
311
303
        self.treeview.set_headers_visible(False)
312
304
        self.treeview.set_search_column(1)
313
305
        self.treeview.connect("cursor-changed", self._treeview_cursor_cb)
314
306
        scrollwin.add(self.treeview)
315
307
        self.treeview.show()
316
308
 
317
 
        cell = gtk.CellRendererText()
 
309
        cell = Gtk.CellRendererText()
318
310
        cell.set_property("width-chars", 20)
319
 
        column = gtk.TreeViewColumn()
320
 
        column.pack_start(cell, expand=True)
 
311
        column = Gtk.TreeViewColumn()
 
312
        column.pack_start(cell, True, True, 0)
321
313
        column.add_attribute(cell, "text", 0)
322
314
        self.treeview.append_column(column)
323
315
 
330
322
        # text view
331
323
 
332
324
    def set_diff_text_sections(self, sections):
333
 
        self.diff_view = DiffFileView()
 
325
        if getattr(self, 'diff_view', None) is None:
 
326
            self.diff_view = DiffFileView()
 
327
            self.pack2(self.diff_view)
334
328
        self.diff_view.show()
335
 
        self.pack2(self.diff_view)
336
329
        for oldname, newname, patch in sections:
337
330
            self.diff_view._diffs[newname] = str(patch)
338
331
            if newname is None:
346
339
        Compares the two trees and populates the window with the
347
340
        differences.
348
341
        """
349
 
        self.diff_view = DiffView()
350
 
        self.pack2(self.diff_view)
 
342
        if getattr(self, 'diff_view', None) is None:
 
343
            self.diff_view = DiffView()
 
344
            self.pack2(self.diff_view)
351
345
        self.diff_view.show()
352
346
        self.diff_view.set_trees(rev_tree, parent_tree)
353
347
        self.rev_tree = rev_tree
380
374
                self.model.append(titer, [ path, path ])
381
375
 
382
376
        self.treeview.expand_all()
 
377
        self.diff_view.show_diff(None)
383
378
 
384
379
    def set_file(self, file_path):
385
380
        """Select the current file to display"""
390
385
                    tv_path = child.path
391
386
                    break
392
387
        if tv_path is None:
393
 
            raise NoSuchFile(file_path)
 
388
            raise errors.NoSuchFile(file_path)
394
389
        self.treeview.set_cursor(tv_path)
395
390
        self.treeview.scroll_to_cell(tv_path)
396
391
 
402
397
            return
403
398
        elif specific_files == [ "" ]:
404
399
            specific_files = None
405
 
 
 
400
        
406
401
        self.diff_view.show_diff(specific_files)
407
 
 
 
402
    
 
403
    def _on_wraplines_toggled(self, widget=None, wrap=False):
 
404
        """Callback for when the wrap lines checkbutton is toggled"""
 
405
        if wrap or widget.get_active():
 
406
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.WORD)
 
407
        else:
 
408
            self.diff_view.sourceview.set_wrap_mode(Gtk.WrapMode.NONE)
408
409
 
409
410
class DiffWindow(Window):
410
411
    """Diff window.
428
429
 
429
430
    def construct(self, operations):
430
431
        """Construct the window contents."""
431
 
        self.vbox = gtk.VBox()
 
432
        self.vbox = Gtk.VBox()
432
433
        self.add(self.vbox)
433
434
        self.vbox.show()
 
435
        self.diff = DiffWidget()
 
436
        self.vbox.pack_end(self.diff, True, True, 0)
 
437
        self.diff.show_all()
 
438
        # Build after DiffWidget to connect signals
 
439
        menubar = self._get_menu_bar()
 
440
        self.vbox.pack_start(menubar, False, False, 0)
434
441
        hbox = self._get_button_bar(operations)
435
442
        if hbox is not None:
436
 
            self.vbox.pack_start(hbox, expand=False, fill=True)
437
 
        self.diff = DiffWidget()
438
 
        self.vbox.add(self.diff)
439
 
        self.diff.show_all()
440
 
 
 
443
            self.vbox.pack_start(hbox, False, True, 0)
 
444
        
 
445
    
 
446
    def _get_menu_bar(self):
 
447
        menubar = Gtk.MenuBar()
 
448
        # View menu
 
449
        mb_view = Gtk.MenuItem(_i18n("_View"))
 
450
        mb_view_menu = Gtk.Menu()
 
451
        mb_view_wrapsource = Gtk.CheckMenuItem(_i18n("Wrap _Long Lines"))
 
452
        mb_view_wrapsource.connect('activate', self.diff._on_wraplines_toggled)
 
453
        mb_view_wrapsource.show()
 
454
        mb_view_menu.append(mb_view_wrapsource)
 
455
        mb_view.show()
 
456
        mb_view.set_submenu(mb_view_menu)
 
457
        mb_view.show()
 
458
        menubar.append(mb_view)
 
459
        menubar.show()
 
460
        return menubar
 
461
    
441
462
    def _get_button_bar(self, operations):
442
463
        """Return a button bar to use.
443
464
 
445
466
        """
446
467
        if operations is None:
447
468
            return None
448
 
        hbox = gtk.HButtonBox()
449
 
        hbox.set_layout(gtk.BUTTONBOX_START)
 
469
        hbox = Gtk.HButtonBox()
 
470
        hbox.set_layout(Gtk.ButtonBoxStyle.START)
450
471
        for title, method in operations:
451
 
            merge_button = gtk.Button(title)
 
472
            merge_button = Gtk.Button(title)
452
473
            merge_button.show()
453
 
            merge_button.set_relief(gtk.RELIEF_NONE)
 
474
            merge_button.set_relief(Gtk.ReliefStyle.NONE)
454
475
            merge_button.connect("clicked", method)
455
476
            hbox.pack_start(merge_button, expand=False, fill=True)
456
477
        hbox.show()
457
478
        return hbox
458
479
 
459
480
    def _get_merge_target(self):
460
 
        d = gtk.FileChooserDialog('Merge branch', self,
461
 
                                  gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
462
 
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
463
 
                                           gtk.STOCK_CANCEL,
464
 
                                           gtk.RESPONSE_CANCEL,))
 
481
        d = Gtk.FileChooserDialog('Merge branch', self,
 
482
                                  Gtk.FileChooserAction.SELECT_FOLDER,
 
483
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
 
484
                                           Gtk.STOCK_CANCEL,
 
485
                                           Gtk.ResponseType.CANCEL,))
465
486
        try:
466
487
            result = d.run()
467
 
            if result != gtk.RESPONSE_OK:
 
488
            if result != Gtk.ResponseType.OK:
468
489
                raise SelectCancelled()
469
490
            return d.get_current_folder_uri()
470
491
        finally:
484
505
        error_dialog('Error', str(e))
485
506
 
486
507
    def _get_save_path(self, basename):
487
 
        d = gtk.FileChooserDialog('Save As', self,
488
 
                                  gtk.FILE_CHOOSER_ACTION_SAVE,
489
 
                                  buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK,
490
 
                                           gtk.STOCK_CANCEL,
491
 
                                           gtk.RESPONSE_CANCEL,))
 
508
        d = Gtk.FileChooserDialog('Save As', self,
 
509
                                  Gtk.FileChooserAction.SAVE,
 
510
                                  buttons=(Gtk.STOCK_OK, Gtk.ResponseType.OK,
 
511
                                           Gtk.STOCK_CANCEL,
 
512
                                           Gtk.ResponseType.CANCEL,))
492
513
        d.set_current_name(basename)
493
514
        try:
494
515
            result = d.run()
495
 
            if result != gtk.RESPONSE_OK:
 
516
            if result != Gtk.ResponseType.OK:
496
517
                raise SelectCancelled()
497
518
            return urlutils.local_path_from_url(d.get_uri())
498
519
        finally:
513
534
 
514
535
class DiffController(object):
515
536
 
516
 
    def __init__(self, path, patch, window=None):
 
537
    def __init__(self, path, patch, window=None, allow_dirty=False):
517
538
        self.path = path
518
539
        self.patch = patch
 
540
        self.allow_dirty = allow_dirty
519
541
        if window is None:
520
542
            window = DiffWindow(operations=self._provide_operations())
521
543
            self.initialize_window(window)
527
549
 
528
550
    def get_diff_sections(self):
529
551
        yield "Complete Diff", None, ''.join(self.patch)
530
 
        for patch in parse_patches(self.patch):
 
552
        # allow_dirty was added to parse_patches in bzrlib 2.2b1
 
553
        if 'allow_dirty' in inspect.getargspec(parse_patches).args:
 
554
            patches = parse_patches(self.patch, allow_dirty=self.allow_dirty)
 
555
        else:
 
556
            patches = parse_patches(self.patch)
 
557
        for patch in patches:
531
558
            oldname = patch.oldname.split('\t')[0]
532
559
            newname = patch.newname.split('\t')[0]
533
560
            yield oldname, newname, str(patch)
572
599
        tree.lock_write()
573
600
        try:
574
601
            try:
575
 
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
576
 
                    self.directive, progress.DummyProgress())
577
 
                merger.check_basis(True)
 
602
                if tree.has_changes():
 
603
                    raise errors.UncommittedChanges(tree)
 
604
                merger, verified = _mod_merge.Merger.from_mergeable(
 
605
                    tree, self.directive, pb=None)
578
606
                merger.merge_type = _mod_merge.Merge3Merger
579
607
                conflict_count = merger.do_merge()
580
608
                merger.set_pending()
606
634
    renamed_and_modified = 'renamed and modified'
607
635
    modified = 'modified'
608
636
    kind_changed = 'kind changed'
 
637
    missing = 'missing'
609
638
 
610
639
    # TODO: Handle metadata changes
611
640
 
626
655
                    source_marker = ''
627
656
                else:
628
657
                    source_marker = osutils.kind_marker(kinds[0])
 
658
 
629
659
                if kinds[1] is None:
630
 
                    assert kinds[0] is not None
631
 
                    marker = osutils.kind_marker(kinds[0])
 
660
                    if kinds[0] is None:
 
661
                        # We assume bzr will flag only files in that case,
 
662
                        # there may be a bzr bug there as only files seems to
 
663
                        # not receive any kind.
 
664
                        marker = osutils.kind_marker('file')
 
665
                    else:
 
666
                        marker = osutils.kind_marker(kinds[0])
632
667
                else:
633
668
                    marker = osutils.kind_marker(kinds[1])
634
669
 
636
671
                if real_path is None:
637
672
                    real_path = paths[0]
638
673
                assert real_path is not None
639
 
                display_path = real_path + marker
640
674
 
641
675
                present_source = versioned[0] and kinds[0] is not None
642
676
                present_target = versioned[1] and kinds[1] is not None
643
677
 
644
 
                if present_source != present_target:
 
678
                if kinds[0] is None and kinds[1] is None:
 
679
                    change_type = missing
 
680
                    display_path = real_path + marker
 
681
                elif present_source != present_target:
645
682
                    if present_target:
646
683
                        change_type = added
647
684
                    else:
648
685
                        assert present_source
649
686
                        change_type = removed
 
687
                    display_path = real_path + marker
650
688
                elif names[0] != names[1] or parent_ids[0] != parent_ids[1]:
651
689
                    # Renamed
652
690
                    if changed_content or executables[0] != executables[1]:
660
698
                    change_type = kind_changed
661
699
                    display_path = (paths[0] + source_marker
662
700
                                    + ' => ' + paths[1] + marker)
663
 
                elif changed_content is True or executables[0] != executables[1]:
 
701
                elif changed_content or executables[0] != executables[1]:
664
702
                    change_type = modified
 
703
                    display_path = real_path + marker
665
704
                else:
666
705
                    assert False, "How did we get here?"
667
706