/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: Jelmer Vernooij
  • Date: 2008-06-27 16:55:09 UTC
  • mto: This revision was merged to the branch mainline in revision 506.
  • Revision ID: jelmer@samba.org-20080627165509-7b68w2chzvvgc8vz
Use helper script to open patches.

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 os.path
18
 
import re
19
 
 
20
17
try:
21
18
    import pygtk
22
19
    pygtk.require("2.0")
27
24
import gobject
28
25
import pango
29
26
 
30
 
from bzrlib import (
31
 
    branch,
32
 
    errors,
33
 
    osutils,
34
 
    trace,
35
 
    )
36
 
try:
37
 
    from bzrlib import bencode
38
 
except ImportError:
39
 
    from bzrlib.util import bencode
 
27
import os.path
 
28
import re
 
29
 
 
30
from bzrlib import errors, osutils
 
31
from bzrlib.trace import mutter
 
32
from bzrlib.util import bencode
40
33
 
41
34
from bzrlib.plugins.gtk import _i18n
42
 
from bzrlib.plugins.gtk.dialog import question_dialog
43
 
from bzrlib.plugins.gtk.errors import show_bzr_error
 
35
from dialog import error_dialog, question_dialog
 
36
from errors import show_bzr_error
44
37
 
45
38
try:
46
39
    import dbus
104
97
    return pm
105
98
 
106
99
 
107
 
_newline_variants_re = re.compile(r'\r\n?')
108
 
def _sanitize_and_decode_message(utf8_message):
109
 
    """Turn a utf-8 message into a sanitized Unicode message."""
110
 
    fixed_newline = _newline_variants_re.sub('\n', utf8_message)
111
 
    return fixed_newline.decode('utf-8')
112
 
 
113
 
 
114
100
class CommitDialog(gtk.Dialog):
115
101
    """Implementation of Commit."""
116
102
 
117
103
    def __init__(self, wt, selected=None, parent=None):
118
 
        gtk.Dialog.__init__(self, title="Commit to %s" % wt.basedir,
119
 
                            parent=parent, flags=0,)
120
 
        self.connect('delete-event', self._on_delete_window)
 
104
        gtk.Dialog.__init__(self, title="Commit - Olive",
 
105
                                  parent=parent,
 
106
                                  flags=0,
 
107
                                  buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
121
108
        self._question_dialog = question_dialog
122
109
 
123
 
        self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
124
 
 
125
110
        self._wt = wt
126
111
        # TODO: Do something with this value, it is used by Olive
127
112
        #       It used to set all changes but this one to False
129
114
        self._enable_per_file_commits = True
130
115
        self._commit_all_changes = True
131
116
        self.committed_revision_id = None # Nothing has been committed yet
132
 
        self._saved_commit_messages_manager = SavedCommitMessagesManager(self._wt, self._wt.branch)
133
117
 
134
118
        self.setup_params()
135
119
        self.construct()
204
188
        self._basis_tree.lock_read()
205
189
        try:
206
190
            from diff import iter_changes_to_status
207
 
            saved_file_messages = self._saved_commit_messages_manager.get()[1]
208
191
            for (file_id, real_path, change_type, display_path
209
192
                ) in iter_changes_to_status(self._basis_tree, self._wt):
210
193
                if self._selected and real_path != self._selected:
211
194
                    enabled = False
212
195
                else:
213
196
                    enabled = True
214
 
                try:
215
 
                    default_message = saved_file_messages[file_id]
216
 
                except KeyError:
217
 
                    default_message = ''
218
197
                item_iter = store.append([
219
198
                    file_id,
220
199
                    real_path.encode('UTF-8'),
221
200
                    enabled,
222
201
                    display_path.encode('UTF-8'),
223
202
                    change_type,
224
 
                    default_message, # Initial comment
 
203
                    '', # Initial comment
225
204
                    ])
226
205
                if self._selected and enabled:
227
206
                    initial_cursor = store.get_path(item_iter)
253
232
                proxy_obj = bus.get_object('org.freedesktop.NetworkManager',
254
233
                                           '/org/freedesktop/NetworkManager')
255
234
            except dbus.DBusException:
256
 
                trace.mutter("networkmanager not available.")
 
235
                mutter("networkmanager not available.")
257
236
                self._check_local.show()
258
237
                return
259
238
            
265
244
            except dbus.DBusException, e:
266
245
                # Silently drop errors. While DBus may be
267
246
                # available, NetworkManager doesn't necessarily have to be
268
 
                trace.mutter("unable to get networkmanager state: %r" % e)
 
247
                mutter("unable to get networkmanager state: %r" % e)
269
248
        self._check_local.show()
270
249
 
271
250
    def _fill_in_per_file_info(self):
324
303
                            gtk.gdk.CONTROL_MASK, 0, self._on_accel_next)
325
304
        self.add_accel_group(group)
326
305
 
327
 
        # ignore the escape key (avoid closing the window)
328
 
        self.connect_object('close', self.emit_stop_by_name, 'close')
329
 
 
330
306
    def _construct_left_pane(self):
331
307
        self._left_pane_box = gtk.VBox(homogeneous=False, spacing=5)
332
308
        self._construct_file_list()
359
335
        self._hpane.pack2(self._right_pane_table, resize=True, shrink=True)
360
336
 
361
337
    def _construct_action_pane(self):
362
 
        self._button_cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
363
 
        self._button_cancel.connect('clicked', self._on_cancel_clicked)
364
 
        self._button_cancel.show()
365
 
        self.action_area.pack_end(self._button_cancel)
366
338
        self._button_commit = gtk.Button(_i18n("Comm_it"), use_underline=True)
367
339
        self._button_commit.connect('clicked', self._on_commit_clicked)
368
340
        self._button_commit.set_flags(gtk.CAN_DEFAULT)
564
536
        scroller.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
565
537
 
566
538
        self._global_message_text_view = gtk.TextView()
567
 
        self._set_global_commit_message(self._saved_commit_messages_manager.get()[0])
568
539
        self._global_message_text_view.modify_font(pango.FontDescription("Monospace"))
569
540
        scroller.add(self._global_message_text_view)
570
541
        scroller.set_shadow_type(gtk.SHADOW_IN)
656
627
            if self._commit_all_changes or record[2]:# [2] checkbox
657
628
                file_id = record[0] # [0] file_id
658
629
                path = record[1]    # [1] real path
659
 
                # [5] commit message
660
 
                file_message = _sanitize_and_decode_message(record[5])
 
630
                file_message = record[5] # [5] commit message
661
631
                files.append(path.decode('UTF-8'))
662
632
                if self._enable_per_file_commits and file_message:
663
633
                    # All of this needs to be utf-8 information
664
 
                    file_message = file_message.encode('UTF-8')
665
634
                    file_info.append({'path':path, 'file_id':file_id,
666
635
                                     'message':file_message})
667
636
        file_info.sort(key=lambda x:(x['path'], x['file_id']))
671
640
            return files, []
672
641
 
673
642
    @show_bzr_error
674
 
    def _on_cancel_clicked(self, button):
675
 
        """ Cancel button clicked handler. """
676
 
        self._do_cancel()
677
 
 
678
 
    @show_bzr_error
679
 
    def _on_delete_window(self, source, event):
680
 
        """ Delete window handler. """
681
 
        self._do_cancel()
682
 
 
683
 
    def _do_cancel(self):
684
 
        """If requested, saves commit messages when cancelling gcommit; they are re-used by a next gcommit"""
685
 
        mgr = SavedCommitMessagesManager()
686
 
        self._saved_commit_messages_manager = mgr
687
 
        mgr.insert(self._get_global_commit_message(),
688
 
                   self._get_specific_files()[1])
689
 
        if mgr.is_not_empty(): # maybe worth saving
690
 
            response = self._question_dialog(
691
 
                _i18n('Commit cancelled'),
692
 
                _i18n('Do you want to save your commit messages ?'),
693
 
                parent=self)
694
 
            if response == gtk.RESPONSE_NO:
695
 
                 # save nothing and destroy old comments if any
696
 
                mgr = SavedCommitMessagesManager()
697
 
        mgr.save(self._wt, self._wt.branch)
698
 
        self.response(gtk.RESPONSE_CANCEL) # close window
699
 
 
700
 
    @show_bzr_error
701
643
    def _on_commit_clicked(self, button):
702
644
        """ Commit button clicked handler. """
703
645
        self._do_commit()
708
650
        if message == '':
709
651
            response = self._question_dialog(
710
652
                _i18n('Commit with an empty message?'),
711
 
                _i18n('You can describe your commit intent in the message.'),
712
 
                parent=self)
 
653
                _i18n('You can describe your commit intent in the message.'))
713
654
            if response == gtk.RESPONSE_NO:
714
655
                # Kindly give focus to message area
715
656
                self._global_message_text_view.grab_focus()
729
670
        for path in self._wt.unknowns():
730
671
            response = self._question_dialog(
731
672
                _i18n("Commit with unknowns?"),
732
 
                _i18n("Unknown files exist in the working tree. Commit anyway?"),
733
 
                parent=self)
734
 
                # Doesn't set a parent for the dialog..
 
673
                _i18n("Unknown files exist in the working tree. Commit anyway?"))
735
674
            if response == gtk.RESPONSE_NO:
736
675
                return
737
676
            break
751
690
            response = self._question_dialog(
752
691
                _i18n('Commit with no changes?'),
753
692
                _i18n('There are no changes in the working tree.'
754
 
                      ' Do you want to commit anyway?'),
755
 
                parent=self)
 
693
                      ' Do you want to commit anyway?'))
756
694
            if response == gtk.RESPONSE_YES:
757
695
                rev_id = self._wt.commit(message,
758
696
                               allow_pointless=True,
761
699
                               specific_files=specific_files,
762
700
                               revprops=revprops)
763
701
        self.committed_revision_id = rev_id
764
 
        # destroy old comments if any
765
 
        SavedCommitMessagesManager().save(self._wt, self._wt.branch)
766
702
        self.response(gtk.RESPONSE_OK)
767
703
 
768
704
    def _get_global_commit_message(self):
769
705
        buf = self._global_message_text_view.get_buffer()
770
706
        start, end = buf.get_bounds()
771
 
        text = buf.get_text(start, end)
772
 
        return _sanitize_and_decode_message(text)
 
707
        return buf.get_text(start, end).decode('utf-8')
773
708
 
774
709
    def _set_global_commit_message(self, message):
775
710
        """Just a helper for the test suite."""
796
731
                                       show_offset=False)
797
732
        rev_dict['revision_id'] = rev.revision_id
798
733
        return rev_dict
799
 
 
800
 
 
801
 
class SavedCommitMessagesManager:
802
 
    """Save glogal and per-file commit messages.
803
 
 
804
 
    Saves global commit message and utf-8 file_id->message dictionary
805
 
    of per-file commit messages on disk. Re-reads them later for re-using.
806
 
    """
807
 
 
808
 
    def __init__(self, tree=None, branch=None):
809
 
        """If branch is None, builds empty messages, otherwise reads them
810
 
        from branch's disk storage. 'tree' argument is for the future."""
811
 
        if branch is None:
812
 
            self.global_message = u''
813
 
            self.file_messages = {}
814
 
        else:
815
 
            config = branch.get_config()._get_branch_data_config()
816
 
            self.global_message = config.get_user_option(
817
 
                'gtk_global_commit_message')
818
 
            if self.global_message is None:
819
 
                self.global_message = u''
820
 
            file_messages = config.get_user_option('gtk_file_commit_messages')
821
 
            if file_messages: # unicode and B-encoded:
822
 
                self.file_messages = bencode.bdecode(
823
 
                    file_messages.encode('UTF-8'))
824
 
            else:
825
 
                self.file_messages = {}
826
 
 
827
 
    def get(self):
828
 
        return self.global_message, self.file_messages
829
 
 
830
 
    def is_not_empty(self):
831
 
        return bool(self.global_message or self.file_messages)
832
 
 
833
 
    def insert(self, global_message, file_info):
834
 
        """Formats per-file commit messages (list of dictionaries, one per file)
835
 
        into one utf-8 file_id->message dictionary and merges this with
836
 
        previously existing dictionary. Merges global commit message too."""
837
 
        file_messages = {}
838
 
        for fi in file_info:
839
 
            file_message = fi['message']
840
 
            if file_message:
841
 
                file_messages[fi['file_id']] = file_message # utf-8 strings
842
 
        for k,v in file_messages.iteritems():
843
 
            try:
844
 
                self.file_messages[k] = v + '\n******\n' + self.file_messages[k]
845
 
            except KeyError:
846
 
                self.file_messages[k] = v
847
 
        if self.global_message:
848
 
            self.global_message = global_message + '\n******\n' \
849
 
                + self.global_message
850
 
        else:
851
 
            self.global_message = global_message
852
 
 
853
 
    def save(self, tree, branch):
854
 
        # We store in branch's config, which can be a problem if two gcommit
855
 
        # are done in two checkouts of one single branch (comments overwrite
856
 
        # each other). Ideally should be in working tree. But uncommit does
857
 
        # not always have a working tree, though it always has a branch.
858
 
        # 'tree' argument is for the future
859
 
        config = branch.get_config()
860
 
        # should it be named "gtk_" or some more neutral name ("gui_" ?) to
861
 
        # be compatible with qbzr in the future?
862
 
        config.set_user_option('gtk_global_commit_message', self.global_message)
863
 
        # bencode() does not know unicode objects but set_user_option()
864
 
        # requires one:
865
 
        config.set_user_option(
866
 
            'gtk_file_commit_messages',
867
 
            bencode.bencode(self.file_messages).decode('UTF-8'))
868
 
 
869
 
 
870
 
def save_commit_messages(local, master, old_revno, old_revid,
871
 
                         new_revno, new_revid):
872
 
    b = local
873
 
    if b is None:
874
 
        b = master
875
 
    mgr = SavedCommitMessagesManager(None, b)
876
 
    revid_iterator = b.repository.iter_reverse_revision_history(old_revid)
877
 
    cur_revno = old_revno
878
 
    new_revision_id = old_revid
879
 
    graph = b.repository.get_graph()
880
 
    for rev_id in revid_iterator:
881
 
        if cur_revno == new_revno:
882
 
            break
883
 
        cur_revno -= 1
884
 
        rev = b.repository.get_revision(rev_id)
885
 
        file_info = rev.properties.get('file-info', None)
886
 
        if file_info is None:
887
 
            file_info = {}
888
 
        else:
889
 
            file_info = bencode.bdecode(file_info.encode('UTF-8'))
890
 
        global_message = osutils.safe_unicode(rev.message)
891
 
        # Concatenate comment of the uncommitted revision
892
 
        mgr.insert(global_message, file_info)
893
 
 
894
 
        parents = graph.get_parent_map([rev_id]).get(rev_id, None)
895
 
        if not parents:
896
 
            continue
897
 
    mgr.save(None, b)