/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 commit.py

  • Committer: Curtis Hovey
  • Date: 2011-07-31 16:50:29 UTC
  • mto: This revision was merged to the branch mainline in revision 741.
  • Revision ID: sinzui.is@verizon.net-20110731165029-9gixuqypi3lwapzm
Removed import_pygtk because gi does not impicitly call Main(). Inlined checks for gtk availablility.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
import re
18
18
 
19
 
from gi.repository import Gdk
20
19
from gi.repository import Gtk
21
20
from gi.repository import GObject
22
21
from gi.repository import Pango
23
22
 
24
23
from bzrlib import (
25
 
    bencode,
26
24
    errors,
27
25
    osutils,
28
 
    revision as _mod_revision,
29
26
    trace,
30
 
    tsort,
31
27
    )
 
28
try:
 
29
    from bzrlib import bencode
 
30
except ImportError:
 
31
    from bzrlib.util import bencode
 
32
 
32
33
from bzrlib.plugins.gtk.dialog import question_dialog
33
 
from bzrlib.plugins.gtk.diff import DiffView
34
34
from bzrlib.plugins.gtk.errors import show_bzr_error
35
35
from bzrlib.plugins.gtk.i18n import _i18n
36
 
from bzrlib.plugins.gtk.commitmsgs import SavedCommitMessagesManager
37
36
 
38
37
try:
39
38
    import dbus
43
42
    have_dbus = False
44
43
 
45
44
 
46
 
def _get_sorted_revisions(tip_revision, revision_ids, parent_map):
47
 
    """Get an iterator which will return the revisions in merge sorted order.
48
 
 
49
 
    This will build up a list of all nodes, such that only nodes in the list
50
 
    are referenced. It then uses MergeSorter to return them in 'merge-sorted'
51
 
    order.
52
 
 
53
 
    :param revision_ids: A set of revision_ids
54
 
    :param parent_map: The parent information for each node. Revisions which
55
 
        are considered ghosts should not be present in the map.
56
 
    :return: iterator from MergeSorter.iter_topo_order()
57
 
    """
58
 
    # MergeSorter requires that all nodes be present in the graph, so get rid
59
 
    # of any references pointing outside of this graph.
60
 
    parent_graph = {}
61
 
    for revision_id in revision_ids:
62
 
        if revision_id not in parent_map: # ghost
63
 
            parent_graph[revision_id] = []
64
 
        else:
65
 
            # Only include parents which are in this sub-graph
66
 
            parent_graph[revision_id] = [p for p in parent_map[revision_id]
67
 
                                            if p in revision_ids]
68
 
    sorter = tsort.MergeSorter(parent_graph, tip_revision)
69
 
    return sorter.iter_topo_order()
70
 
 
71
 
 
72
45
def pending_revisions(wt):
73
46
    """Return a list of pending merges or None if there are none of them.
74
47
 
79
52
    """
80
53
    parents = wt.get_parent_ids()
81
54
    if len(parents) < 2:
82
 
        return
 
55
        return None
83
56
 
84
57
    # The basic pending merge algorithm uses the same algorithm as
85
58
    # bzrlib.status.show_pending_merges
87
60
    branch = wt.branch
88
61
    last_revision = parents[0]
89
62
 
90
 
    graph = branch.repository.get_graph()
91
 
    other_revisions = [last_revision]
 
63
    if last_revision is not None:
 
64
        try:
 
65
            ignore = set(branch.repository.get_ancestry(last_revision,
 
66
                                                        topo_sorted=False))
 
67
        except errors.NoSuchRevision:
 
68
            # the last revision is a ghost : assume everything is new
 
69
            # except for it
 
70
            ignore = set([None, last_revision])
 
71
    else:
 
72
        ignore = set([None])
92
73
 
93
74
    pm = []
94
75
    for merge in pending:
95
 
        try:
96
 
            merge_rev = branch.repository.get_revision(merge)
97
 
        except errors.NoSuchRevision:
98
 
            # If we are missing a revision, just print out the revision id
99
 
            trace.mutter("ghost: %r", merge)
100
 
            other_revisions.append(merge)
101
 
            continue
102
 
 
103
 
        # Find all of the revisions in the merge source, which are not in the
104
 
        # last committed revision.
105
 
        merge_extra = graph.find_unique_ancestors(merge, other_revisions)
106
 
        other_revisions.append(merge)
107
 
        merge_extra.discard(_mod_revision.NULL_REVISION)
108
 
 
109
 
        # Get a handle to all of the revisions we will need
110
 
        try:
111
 
            revisions = dict((rev.revision_id, rev) for rev in
112
 
                             branch.repository.get_revisions(merge_extra))
113
 
        except errors.NoSuchRevision:
114
 
            # One of the sub nodes is a ghost, check each one
115
 
            revisions = {}
116
 
            for revision_id in merge_extra:
117
 
                try:
118
 
                    rev = branch.repository.get_revisions([revision_id])[0]
119
 
                except errors.NoSuchRevision:
120
 
                    revisions[revision_id] = None
121
 
                else:
122
 
                    revisions[revision_id] = rev
123
 
 
124
 
         # Display the revisions brought in by this merge.
125
 
        rev_id_iterator = _get_sorted_revisions(merge, merge_extra,
126
 
                            branch.repository.get_parent_map(merge_extra))
127
 
        # Skip the first node
128
 
        num, first, depth, eom = rev_id_iterator.next()
129
 
        if first != merge:
130
 
            raise AssertionError('Somehow we misunderstood how'
131
 
                ' iter_topo_order works %s != %s' % (first, merge))
132
 
        children = []
133
 
        for num, sub_merge, depth, eom in rev_id_iterator:
134
 
            rev = revisions[sub_merge]
135
 
            if rev is None:
136
 
                trace.warning("ghost: %r", sub_merge)
137
 
                continue
138
 
            children.append(rev)
139
 
        yield (merge_rev, children)
 
76
        ignore.add(merge)
 
77
        try:
 
78
            rev = branch.repository.get_revision(merge)
 
79
            children = []
 
80
            pm.append((rev, children))
 
81
 
 
82
            # This does need to be topo sorted, so we search backwards
 
83
            inner_merges = branch.repository.get_ancestry(merge)
 
84
            assert inner_merges[0] is None
 
85
            inner_merges.pop(0)
 
86
            for mmerge in reversed(inner_merges):
 
87
                if mmerge in ignore:
 
88
                    continue
 
89
                rev = branch.repository.get_revision(mmerge)
 
90
                children.append(rev)
 
91
 
 
92
                ignore.add(mmerge)
 
93
        except errors.NoSuchRevision:
 
94
            print "DEBUG: NoSuchRevision:", merge
 
95
 
 
96
    return pm
140
97
 
141
98
 
142
99
_newline_variants_re = re.compile(r'\r\n?')
143
100
def _sanitize_and_decode_message(utf8_message):
144
101
    """Turn a utf-8 message into a sanitized Unicode message."""
145
102
    fixed_newline = _newline_variants_re.sub('\n', utf8_message)
146
 
    return osutils.safe_unicode(fixed_newline)
 
103
    return fixed_newline.decode('utf-8')
147
104
 
148
105
 
149
106
class CommitDialog(Gtk.Dialog):
150
107
    """Implementation of Commit."""
151
108
 
152
109
    def __init__(self, wt, selected=None, parent=None):
153
 
        super(CommitDialog, self).__init__(
154
 
            title="Commit to %s" % wt.basedir, parent=parent, flags=0)
 
110
        GObject.GObject.__init__(self, title="Commit to %s" % wt.basedir,
 
111
                            parent=parent, flags=0,)
155
112
        self.connect('delete-event', self._on_delete_window)
156
113
        self._question_dialog = question_dialog
157
114
 
164
121
        self._enable_per_file_commits = True
165
122
        self._commit_all_changes = True
166
123
        self.committed_revision_id = None # Nothing has been committed yet
167
 
        self._last_selected_file = None
168
 
        self._saved_commit_messages_manager = SavedCommitMessagesManager(
169
 
            self._wt, self._wt.branch)
 
124
        self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
170
125
 
171
126
        self.setup_params()
172
127
        self.construct()
176
131
        """Setup the member variables for state."""
177
132
        self._basis_tree = self._wt.basis_tree()
178
133
        self._delta = None
179
 
        self._wt.lock_read()
180
 
        try:
181
 
            self._pending = list(pending_revisions(self._wt))
182
 
        finally:
183
 
            self._wt.unlock()
 
134
        self._pending = pending_revisions(self._wt)
184
135
 
185
136
        self._is_checkout = (self._wt.branch.get_bound_location() is not None)
186
137
 
238
189
 
239
190
        all_enabled = (self._selected is None)
240
191
        # The first entry is always the 'whole tree'
241
 
        all_iter = store.append(["", "", all_enabled, 'All Files', '', ''])
 
192
        all_iter = store.append([None, None, all_enabled, 'All Files', '', ''])
242
193
        initial_cursor = store.get_path(all_iter)
243
194
        # should we pass specific_files?
244
195
        self._wt.lock_read()
275
226
        # This sets the cursor, which causes the expander to close, which
276
227
        # causes the _file_message_text_view to never get realized. So we have
277
228
        # to give it a little kick, or it warns when we try to grab the focus
278
 
        self._treeview_files.set_cursor(initial_cursor, None, False)
 
229
        self._treeview_files.set_cursor(initial_cursor)
279
230
 
280
231
        def _realize_file_message_tree_view(*args):
281
232
            self._file_message_text_view.realize()
289
240
            self._check_local.hide()
290
241
            return
291
242
        if have_dbus:
292
 
            try:
293
 
                bus = dbus.SystemBus()
294
 
            except dbus.DBusException:
295
 
                trace.mutter("DBus system bus not available")
296
 
                self._check_local.show()
297
 
                return
 
243
            bus = dbus.SystemBus()
298
244
            try:
299
245
                proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
300
246
                                           '/org/freedesktop/NetworkManager')
302
248
                trace.mutter("networkmanager not available.")
303
249
                self._check_local.show()
304
250
                return
305
 
 
 
251
            
306
252
            dbus_iface = dbus.Interface(proxy_obj,
307
253
                                        'org.freedesktop.NetworkManager')
308
254
            try:
334
280
        """Build up the dialog widgets."""
335
281
        # The primary pane which splits it into left and right (adjustable)
336
282
        # sections.
337
 
        self._hpane = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL)
 
283
        self._hpane = Gtk.HPaned()
338
284
 
339
285
        self._construct_left_pane()
340
286
        self._construct_right_pane()
341
287
        self._construct_action_pane()
342
288
 
343
 
        self.get_content_area().pack_start(self._hpane, True, True, 0)
 
289
        self.vbox.pack_start(self._hpane, True, True, 0)
344
290
        self._hpane.show()
345
291
        self.set_focus(self._global_message_text_view)
346
292
 
366
312
 
367
313
    def _construct_accelerators(self):
368
314
        group = Gtk.AccelGroup()
369
 
        group.connect(Gdk.keyval_from_name('N'),
370
 
                      Gdk.ModifierType.CONTROL_MASK, 0, self._on_accel_next)
 
315
        group.connect_group(Gdk.keyval_from_name('N'),
 
316
                            Gdk.EventMask.CONTROL_MASK, 0, self._on_accel_next)
371
317
        self.add_accel_group(group)
372
318
 
373
319
        # ignore the escape key (avoid closing the window)
380
326
 
381
327
        self._check_local = Gtk.CheckButton(_i18n("_Only commit locally"),
382
328
                                            use_underline=True)
383
 
        self._left_pane_box.pack_end(self._check_local, False, False, 0)
 
329
        self._left_pane_box.pack_end(self._check_local, False, False)
384
330
        self._check_local.set_active(False)
385
331
 
386
332
        self._hpane.pack1(self._left_pane_box, resize=False, shrink=False)
408
354
        self._button_cancel = Gtk.Button(stock=Gtk.STOCK_CANCEL)
409
355
        self._button_cancel.connect('clicked', self._on_cancel_clicked)
410
356
        self._button_cancel.show()
411
 
        self.get_action_area().pack_end(
412
 
            self._button_cancel, True, True, 0)
 
357
        self.action_area.pack_end(self._button_cancel)
413
358
        self._button_commit = Gtk.Button(_i18n("Comm_it"), use_underline=True)
414
359
        self._button_commit.connect('clicked', self._on_commit_clicked)
415
360
        self._button_commit.set_can_default(True)
416
361
        self._button_commit.show()
417
 
        self.get_action_area().pack_end(
418
 
            self._button_commit, True, True, 0)
 
362
        self.action_area.pack_end(self._button_commit)
419
363
        self._button_commit.grab_default()
420
364
 
421
365
    def _add_to_right_table(self, widget, weight, expanding=False):
441
385
        # file_label.show()
442
386
        self._files_box.pack_start(file_label, False, True, 0)
443
387
 
444
 
        self._commit_all_files_radio = Gtk.RadioButton.new_with_label(
 
388
        self._commit_all_files_radio = Gtk.RadioButton(
445
389
            None, _i18n("Commit all changes"))
446
390
        self._files_box.pack_start(self._commit_all_files_radio, False, True, 0)
447
391
        self._commit_all_files_radio.show()
448
392
        self._commit_all_files_radio.connect('toggled',
449
393
            self._toggle_commit_selection)
450
 
        self._commit_selected_radio = Gtk.RadioButton.new_with_label_from_widget(
 
394
        self._commit_selected_radio = Gtk.RadioButton(
451
395
            self._commit_all_files_radio, _i18n("Only commit selected changes"))
452
396
        self._files_box.pack_start(self._commit_selected_radio, False, True, 0)
453
397
        self._commit_selected_radio.show()
465
409
        scroller.add(self._treeview_files)
466
410
        scroller.set_shadow_type(Gtk.ShadowType.IN)
467
411
        scroller.show()
468
 
        self._files_box.pack_start(scroller, True, True, 0)
 
412
        self._files_box.pack_start(scroller,
 
413
                                   expand=True, fill=True)
469
414
        self._files_box.show()
470
415
        self._left_pane_box.pack_start(self._files_box, True, True, 0)
471
416
 
500
445
                                     self._on_treeview_files_cursor_changed)
501
446
 
502
447
    def _toggle_commit(self, cell, path, model):
503
 
        if model[path][0] == "": # No file_id means 'All Files'
 
448
        if model[path][0] is None: # No file_id means 'All Files'
504
449
            new_val = not model[path][2]
505
450
            for node in model:
506
451
                node[2] = new_val
516
461
                checked_col.set_visible(False)
517
462
            else:
518
463
                checked_col.set_visible(True)
519
 
            renderer = checked_col.get_cells()[0]
 
464
            renderer = checked_col.get_cell_renderers()[0]
520
465
            renderer.set_property('activatable', not all_files)
521
466
 
522
467
    def _construct_pending_list(self):
528
473
        pending_message = Gtk.Label()
529
474
        pending_message.set_markup(
530
475
            _i18n('<i>* Cannot select specific files when merging</i>'))
531
 
        self._pending_box.pack_start(pending_message, False, True, 5)
 
476
        self._pending_box.pack_start(pending_message, expand=False, padding=5)
532
477
        pending_message.show()
533
478
 
534
479
        pending_label = Gtk.Label(label=_i18n('Pending Revisions'))
535
 
        self._pending_box.pack_start(pending_label, False, True, 0)
 
480
        self._pending_box.pack_start(pending_label, expand=False, padding=0)
536
481
        pending_label.show()
537
482
 
538
483
        scroller = Gtk.ScrolledWindow()
541
486
        scroller.add(self._treeview_pending)
542
487
        scroller.set_shadow_type(Gtk.ShadowType.IN)
543
488
        scroller.show()
544
 
        self._pending_box.pack_start(scroller, True, True, 5)
 
489
        self._pending_box.pack_start(scroller,
 
490
                                     expand=True, fill=True, padding=5)
545
491
        self._treeview_pending.show()
546
492
        self._left_pane_box.pack_start(self._pending_box, True, True, 0)
547
493
 
560
506
                                             Gtk.CellRendererText(), text=3))
561
507
 
562
508
    def _construct_diff_view(self):
 
509
        from bzrlib.plugins.gtk.diff import DiffView
 
510
 
563
511
        # TODO: jam 2007-10-30 The diff label is currently disabled. If we
564
512
        #       decide that we really don't ever want to display it, we should
565
513
        #       actually remove it, and other references to it, along with the
574
522
        self._add_to_right_table(self._diff_view, 4, True)
575
523
        self._diff_view.show()
576
524
 
577
 
    @staticmethod
578
 
    def get_line_height(widget):
579
 
        pango_layout = widget.create_pango_layout("X");
580
 
        ink_rectangle, logical_rectangle = pango_layout.get_pixel_extents()
581
 
        return logical_rectangle.height
582
 
 
583
525
    def _construct_file_message(self):
584
526
        scroller = Gtk.ScrolledWindow()
585
527
        scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
592
534
        self._file_message_text_view.modify_font(Pango.FontDescription("Monospace"))
593
535
        self._file_message_text_view.set_wrap_mode(Gtk.WrapMode.WORD)
594
536
        self._file_message_text_view.set_accepts_tab(False)
595
 
        line_height = self.get_line_height(self._file_message_text_view)
596
 
        self._file_message_text_view.set_size_request(-1, line_height * 2)
597
537
        self._file_message_text_view.show()
598
538
 
599
 
        self._file_message_expander = Gtk.Expander(
600
 
            label=_i18n('File commit message'))
 
539
        self._file_message_expander = Gtk.Expander(_i18n('File commit message'))
601
540
        self._file_message_expander.set_expanded(True)
602
541
        self._file_message_expander.add(scroller)
603
542
        self._add_to_right_table(self._file_message_expander, 1, False)
629
568
 
630
569
    def _on_treeview_files_cursor_changed(self, treeview):
631
570
        treeselection = treeview.get_selection()
632
 
        if treeselection is None:
633
 
            # The treeview was probably destroyed as the dialog closes.
634
 
            return
635
571
        (model, selection) = treeselection.get_selected()
636
572
 
637
573
        if selection is not None:
638
574
            path, display_path = model.get(selection, 1, 3)
639
575
            self._diff_label.set_text(_i18n('Diff for ') + display_path)
640
 
            if path == "":
 
576
            if path is None:
641
577
                self._diff_view.show_diff(None)
642
578
            else:
643
 
                self._diff_view.show_diff([osutils.safe_unicode(path)])
 
579
                self._diff_view.show_diff([path.decode('UTF-8')])
644
580
            self._update_per_file_info(selection)
645
581
 
646
582
    def _on_accel_next(self, accel_group, window, keyval, modifier):
657
593
            # We have either made it to the end of the list, or nothing was
658
594
            # selected. Either way, select All Files, and jump to the global
659
595
            # commit message.
660
 
            self._treeview_files.set_cursor(
661
 
                Gtk.TreePath(path=0), "", False)
 
596
            self._treeview_files.set_cursor((0,))
662
597
            self._global_message_text_view.grab_focus()
663
598
        else:
664
599
            # Set the cursor to this entry, and jump to the per-file commit
665
600
            # message
666
 
            self._treeview_files.set_cursor(model.get_path(next), None, False)
 
601
            self._treeview_files.set_cursor(model.get_path(next))
667
602
            self._file_message_text_view.grab_focus()
668
603
 
669
604
    def _save_current_file_message(self):
671
606
            return # Nothing to save
672
607
        text_buffer = self._file_message_text_view.get_buffer()
673
608
        cur_text = text_buffer.get_text(text_buffer.get_start_iter(),
674
 
                                        text_buffer.get_end_iter(), True)
 
609
                                        text_buffer.get_end_iter())
675
610
        last_selected = self._files_store.get_iter(self._last_selected_file)
676
611
        self._files_store.set_value(last_selected, 5, cur_text)
677
612
 
683
618
        self._save_current_file_message()
684
619
        text_buffer = self._file_message_text_view.get_buffer()
685
620
        file_id, display_path, message = self._files_store.get(selection, 0, 3, 5)
686
 
        if file_id == "": # Whole tree
 
621
        if file_id is None: # Whole tree
687
622
            self._file_message_expander.set_label(_i18n('File commit message'))
688
623
            self._file_message_expander.set_expanded(False)
689
624
            self._file_message_expander.set_sensitive(False)
706
641
        files = []
707
642
        records = iter(self._files_store)
708
643
        rec = records.next() # Skip the All Files record
709
 
        assert rec[0] == "", "Are we skipping the wrong record?"
 
644
        assert rec[0] is None, "Are we skipping the wrong record?"
710
645
 
711
646
        file_info = []
712
647
        for record in records:
713
648
            if self._commit_all_changes or record[2]:# [2] checkbox
714
 
                file_id = osutils.safe_utf8(record[0]) # [0] file_id
715
 
                path = osutils.safe_utf8(record[1])    # [1] real path
 
649
                file_id = record[0] # [0] file_id
 
650
                path = record[1]    # [1] real path
716
651
                # [5] commit message
717
652
                file_message = _sanitize_and_decode_message(record[5])
718
653
                files.append(path.decode('UTF-8'))
825
760
    def _get_global_commit_message(self):
826
761
        buf = self._global_message_text_view.get_buffer()
827
762
        start, end = buf.get_bounds()
828
 
        text = buf.get_text(start, end, True)
 
763
        text = buf.get_text(start, end)
829
764
        return _sanitize_and_decode_message(text)
830
765
 
831
766
    def _set_global_commit_message(self, message):
854
789
        rev_dict['revision_id'] = rev.revision_id
855
790
        return rev_dict
856
791
 
 
792
 
 
793
class SavedCommitMessagesManager:
 
794
    """Save glogal and per-file commit messages.
 
795
 
 
796
    Saves global commit message and utf-8 file_id->message dictionary
 
797
    of per-file commit messages on disk. Re-reads them later for re-using.
 
798
    """
 
799
 
 
800
    def __init__(self, tree=None, branch=None):
 
801
        """If branch is None, builds empty messages, otherwise reads them
 
802
        from branch's disk storage. 'tree' argument is for the future."""
 
803
        if branch is None:
 
804
            self.global_message = u''
 
805
            self.file_messages = {}
 
806
        else:
 
807
            config = branch.get_config()
 
808
            self.global_message = config.get_user_option(
 
809
                'gtk_global_commit_message')
 
810
            if self.global_message is None:
 
811
                self.global_message = u''
 
812
            file_messages = config.get_user_option('gtk_file_commit_messages')
 
813
            if file_messages: # unicode and B-encoded:
 
814
                self.file_messages = bencode.bdecode(
 
815
                    file_messages.encode('UTF-8'))
 
816
            else:
 
817
                self.file_messages = {}
 
818
 
 
819
    def get(self):
 
820
        return self.global_message, self.file_messages
 
821
 
 
822
    def is_not_empty(self):
 
823
        return bool(self.global_message or self.file_messages)
 
824
 
 
825
    def insert(self, global_message, file_info):
 
826
        """Formats per-file commit messages (list of dictionaries, one per file)
 
827
        into one utf-8 file_id->message dictionary and merges this with
 
828
        previously existing dictionary. Merges global commit message too."""
 
829
        file_messages = {}
 
830
        for fi in file_info:
 
831
            file_message = fi['message']
 
832
            if file_message:
 
833
                file_messages[fi['file_id']] = file_message # utf-8 strings
 
834
        for k,v in file_messages.iteritems():
 
835
            try:
 
836
                self.file_messages[k] = v + '\n******\n' + self.file_messages[k]
 
837
            except KeyError:
 
838
                self.file_messages[k] = v
 
839
        if self.global_message:
 
840
            self.global_message = global_message + '\n******\n' \
 
841
                + self.global_message
 
842
        else:
 
843
            self.global_message = global_message
 
844
 
 
845
    def save(self, tree, branch):
 
846
        # We store in branch's config, which can be a problem if two gcommit
 
847
        # are done in two checkouts of one single branch (comments overwrite
 
848
        # each other). Ideally should be in working tree. But uncommit does
 
849
        # not always have a working tree, though it always has a branch.
 
850
        # 'tree' argument is for the future
 
851
        config = branch.get_config()
 
852
        # should it be named "gtk_" or some more neutral name ("gui_" ?) to
 
853
        # be compatible with qbzr in the future?
 
854
        config.set_user_option('gtk_global_commit_message', self.global_message)
 
855
        # bencode() does not know unicode objects but set_user_option()
 
856
        # requires one:
 
857
        config.set_user_option(
 
858
            'gtk_file_commit_messages',
 
859
            bencode.bencode(self.file_messages).decode('UTF-8'))
 
860
 
 
861
 
 
862
def save_commit_messages(local, master, old_revno, old_revid,
 
863
                         new_revno, new_revid):
 
864
    b = local
 
865
    if b is None:
 
866
        b = master
 
867
    mgr = SavedCommitMessagesManager(None, b)
 
868
    revid_iterator = b.repository.iter_reverse_revision_history(old_revid)
 
869
    cur_revno = old_revno
 
870
    new_revision_id = old_revid
 
871
    graph = b.repository.get_graph()
 
872
    for rev_id in revid_iterator:
 
873
        if cur_revno == new_revno:
 
874
            break
 
875
        cur_revno -= 1
 
876
        rev = b.repository.get_revision(rev_id)
 
877
        file_info = rev.properties.get('file-info', None)
 
878
        if file_info is None:
 
879
            file_info = {}
 
880
        else:
 
881
            file_info = bencode.bdecode(file_info.encode('UTF-8'))
 
882
        global_message = osutils.safe_unicode(rev.message)
 
883
        # Concatenate comment of the uncommitted revision
 
884
        mgr.insert(global_message, file_info)
 
885
 
 
886
        parents = graph.get_parent_map([rev_id]).get(rev_id, None)
 
887
        if not parents:
 
888
            continue
 
889
    mgr.save(None, b)